mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 01:08:03 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
39
drivers/usb/wusbcore/Kconfig
Normal file
39
drivers/usb/wusbcore/Kconfig
Normal file
|
@ -0,0 +1,39 @@
|
|||
#
|
||||
# Wireless USB Core configuration
|
||||
#
|
||||
config USB_WUSB
|
||||
tristate "Enable Wireless USB extensions"
|
||||
depends on UWB
|
||||
select CRYPTO
|
||||
select CRYPTO_BLKCIPHER
|
||||
select CRYPTO_CBC
|
||||
select CRYPTO_MANAGER
|
||||
select CRYPTO_AES
|
||||
help
|
||||
Enable the host-side support for Wireless USB.
|
||||
|
||||
To compile this support select Y (built in). It is safe to
|
||||
select even if you don't have the hardware.
|
||||
|
||||
config USB_WUSB_CBAF
|
||||
tristate "Support WUSB Cable Based Association (CBA)"
|
||||
depends on USB
|
||||
help
|
||||
Some WUSB devices support Cable Based Association. It's used to
|
||||
enable the secure communication between the host and the
|
||||
device.
|
||||
|
||||
Enable this option if your WUSB device must to be connected
|
||||
via wired USB before establishing a wireless link.
|
||||
|
||||
It is safe to select even if you don't have a compatible
|
||||
hardware.
|
||||
|
||||
config USB_WUSB_CBAF_DEBUG
|
||||
bool "Enable CBA debug messages"
|
||||
depends on USB_WUSB_CBAF
|
||||
help
|
||||
Say Y here if you want the CBA to produce a bunch of debug messages
|
||||
to the system log. Select this if you are having a problem with
|
||||
CBA support and want to see more of what is going on.
|
||||
|
25
drivers/usb/wusbcore/Makefile
Normal file
25
drivers/usb/wusbcore/Makefile
Normal file
|
@ -0,0 +1,25 @@
|
|||
ccflags-$(CONFIG_USB_WUSB_CBAF_DEBUG) := -DDEBUG
|
||||
|
||||
obj-$(CONFIG_USB_WUSB) += wusbcore.o
|
||||
obj-$(CONFIG_USB_HWA_HCD) += wusb-wa.o
|
||||
obj-$(CONFIG_USB_WUSB_CBAF) += wusb-cbaf.o
|
||||
|
||||
|
||||
wusbcore-y := \
|
||||
crypto.o \
|
||||
devconnect.o \
|
||||
dev-sysfs.o \
|
||||
mmc.o \
|
||||
pal.o \
|
||||
rh.o \
|
||||
reservation.o \
|
||||
security.o \
|
||||
wusbhc.o
|
||||
|
||||
wusb-cbaf-y := cbaf.o
|
||||
|
||||
wusb-wa-y := \
|
||||
wa-hc.o \
|
||||
wa-nep.o \
|
||||
wa-rpipe.o \
|
||||
wa-xfer.o
|
667
drivers/usb/wusbcore/cbaf.c
Normal file
667
drivers/usb/wusbcore/cbaf.c
Normal file
|
@ -0,0 +1,667 @@
|
|||
/*
|
||||
* Wireless USB - Cable Based Association
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2006 Intel Corporation
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
* Copyright (C) 2008 Cambridge Silicon Radio Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* WUSB devices have to be paired (associated in WUSB lingo) so
|
||||
* that they can connect to the system.
|
||||
*
|
||||
* One way of pairing is using CBA-Cable Based Association. First
|
||||
* time you plug the device with a cable, association is done between
|
||||
* host and device and subsequent times, you can connect wirelessly
|
||||
* without having to associate again. That's the idea.
|
||||
*
|
||||
* This driver does nothing Earth shattering. It just provides an
|
||||
* interface to chat with the wire-connected device so we can get a
|
||||
* CDID (device ID) that might have been previously associated to a
|
||||
* CHID (host ID) and to set up a new <CHID,CDID,CK> triplet
|
||||
* (connection context), with the CK being the secret, or connection
|
||||
* key. This is the pairing data.
|
||||
*
|
||||
* When a device with the CBA capability connects, the probe routine
|
||||
* just creates a bunch of sysfs files that a user space enumeration
|
||||
* manager uses to allow it to connect wirelessly to the system or not.
|
||||
*
|
||||
* The process goes like this:
|
||||
*
|
||||
* 1. Device plugs, cbaf is loaded, notifications happen.
|
||||
*
|
||||
* 2. The connection manager (CM) sees a device with CBAF capability
|
||||
* (the wusb_chid etc. files in /sys/devices/blah/OURDEVICE).
|
||||
*
|
||||
* 3. The CM writes the host name, supported band groups, and the CHID
|
||||
* (host ID) into the wusb_host_name, wusb_host_band_groups and
|
||||
* wusb_chid files. These get sent to the device and the CDID (if
|
||||
* any) for this host is requested.
|
||||
*
|
||||
* 4. The CM can verify that the device's supported band groups
|
||||
* (wusb_device_band_groups) are compatible with the host.
|
||||
*
|
||||
* 5. The CM reads the wusb_cdid file.
|
||||
*
|
||||
* 6. The CM looks up its database
|
||||
*
|
||||
* 6.1 If it has a matching CHID,CDID entry, the device has been
|
||||
* authorized before (paired) and nothing further needs to be
|
||||
* done.
|
||||
*
|
||||
* 6.2 If the CDID is zero (or the CM doesn't find a matching CDID in
|
||||
* its database), the device is assumed to be not known. The CM
|
||||
* may associate the host with device by: writing a randomly
|
||||
* generated CDID to wusb_cdid and then a random CK to wusb_ck
|
||||
* (this uploads the new CC to the device).
|
||||
*
|
||||
* CMD may choose to prompt the user before associating with a new
|
||||
* device.
|
||||
*
|
||||
* 7. Device is unplugged.
|
||||
*
|
||||
* When the device tries to connect wirelessly, it will present its
|
||||
* CDID to the WUSB host controller. The CM will query the
|
||||
* database. If the CHID/CDID pair found, it will (with a 4-way
|
||||
* handshake) challenge the device to demonstrate it has the CK secret
|
||||
* key (from our database) without actually exchanging it. Once
|
||||
* satisfied, crypto keys are derived from the CK, the device is
|
||||
* connected and all communication is encrypted.
|
||||
*
|
||||
* References:
|
||||
* [WUSB-AM] Association Models Supplement to the Certified Wireless
|
||||
* Universal Serial Bus Specification, version 1.0.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/uwb.h>
|
||||
#include <linux/usb/wusb.h>
|
||||
#include <linux/usb/association.h>
|
||||
|
||||
#define CBA_NAME_LEN 0x40 /* [WUSB-AM] table 4-7 */
|
||||
|
||||
/* An instance of a Cable-Based-Association-Framework device */
|
||||
struct cbaf {
|
||||
struct usb_device *usb_dev;
|
||||
struct usb_interface *usb_iface;
|
||||
void *buffer;
|
||||
size_t buffer_size;
|
||||
|
||||
struct wusb_ckhdid chid;
|
||||
char host_name[CBA_NAME_LEN];
|
||||
u16 host_band_groups;
|
||||
|
||||
struct wusb_ckhdid cdid;
|
||||
char device_name[CBA_NAME_LEN];
|
||||
u16 device_band_groups;
|
||||
|
||||
struct wusb_ckhdid ck;
|
||||
};
|
||||
|
||||
/*
|
||||
* Verify that a CBAF USB-interface has what we need
|
||||
*
|
||||
* According to [WUSB-AM], CBA devices should provide at least two
|
||||
* interfaces:
|
||||
* - RETRIEVE_HOST_INFO
|
||||
* - ASSOCIATE
|
||||
*
|
||||
* If the device doesn't provide these interfaces, we do not know how
|
||||
* to deal with it.
|
||||
*/
|
||||
static int cbaf_check(struct cbaf *cbaf)
|
||||
{
|
||||
int result;
|
||||
struct device *dev = &cbaf->usb_iface->dev;
|
||||
struct wusb_cbaf_assoc_info *assoc_info;
|
||||
struct wusb_cbaf_assoc_request *assoc_request;
|
||||
size_t assoc_size;
|
||||
void *itr, *top;
|
||||
int ar_rhi = 0, ar_assoc = 0;
|
||||
|
||||
result = usb_control_msg(
|
||||
cbaf->usb_dev, usb_rcvctrlpipe(cbaf->usb_dev, 0),
|
||||
CBAF_REQ_GET_ASSOCIATION_INFORMATION,
|
||||
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
0, cbaf->usb_iface->cur_altsetting->desc.bInterfaceNumber,
|
||||
cbaf->buffer, cbaf->buffer_size, USB_CTRL_GET_TIMEOUT);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "Cannot get available association types: %d\n",
|
||||
result);
|
||||
return result;
|
||||
}
|
||||
|
||||
assoc_info = cbaf->buffer;
|
||||
if (result < sizeof(*assoc_info)) {
|
||||
dev_err(dev, "Not enough data to decode association info "
|
||||
"header (%zu vs %zu bytes required)\n",
|
||||
(size_t)result, sizeof(*assoc_info));
|
||||
return result;
|
||||
}
|
||||
|
||||
assoc_size = le16_to_cpu(assoc_info->Length);
|
||||
if (result < assoc_size) {
|
||||
dev_err(dev, "Not enough data to decode association info "
|
||||
"(%zu vs %zu bytes required)\n",
|
||||
(size_t)assoc_size, sizeof(*assoc_info));
|
||||
return result;
|
||||
}
|
||||
/*
|
||||
* From now on, we just verify, but won't error out unless we
|
||||
* don't find the AR_TYPE_WUSB_{RETRIEVE_HOST_INFO,ASSOCIATE}
|
||||
* types.
|
||||
*/
|
||||
itr = cbaf->buffer + sizeof(*assoc_info);
|
||||
top = cbaf->buffer + assoc_size;
|
||||
dev_dbg(dev, "Found %u association requests (%zu bytes)\n",
|
||||
assoc_info->NumAssociationRequests, assoc_size);
|
||||
|
||||
while (itr < top) {
|
||||
u16 ar_type, ar_subtype;
|
||||
u32 ar_size;
|
||||
const char *ar_name;
|
||||
|
||||
assoc_request = itr;
|
||||
|
||||
if (top - itr < sizeof(*assoc_request)) {
|
||||
dev_err(dev, "Not enough data to decode association "
|
||||
"request (%zu vs %zu bytes needed)\n",
|
||||
top - itr, sizeof(*assoc_request));
|
||||
break;
|
||||
}
|
||||
|
||||
ar_type = le16_to_cpu(assoc_request->AssociationTypeId);
|
||||
ar_subtype = le16_to_cpu(assoc_request->AssociationSubTypeId);
|
||||
ar_size = le32_to_cpu(assoc_request->AssociationTypeInfoSize);
|
||||
ar_name = "unknown";
|
||||
|
||||
switch (ar_type) {
|
||||
case AR_TYPE_WUSB:
|
||||
/* Verify we have what is mandated by [WUSB-AM]. */
|
||||
switch (ar_subtype) {
|
||||
case AR_TYPE_WUSB_RETRIEVE_HOST_INFO:
|
||||
ar_name = "RETRIEVE_HOST_INFO";
|
||||
ar_rhi = 1;
|
||||
break;
|
||||
case AR_TYPE_WUSB_ASSOCIATE:
|
||||
/* send assoc data */
|
||||
ar_name = "ASSOCIATE";
|
||||
ar_assoc = 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "Association request #%02u: 0x%04x/%04x "
|
||||
"(%zu bytes): %s\n",
|
||||
assoc_request->AssociationDataIndex, ar_type,
|
||||
ar_subtype, (size_t)ar_size, ar_name);
|
||||
|
||||
itr += sizeof(*assoc_request);
|
||||
}
|
||||
|
||||
if (!ar_rhi) {
|
||||
dev_err(dev, "Missing RETRIEVE_HOST_INFO association "
|
||||
"request\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!ar_assoc) {
|
||||
dev_err(dev, "Missing ASSOCIATE association request\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct wusb_cbaf_host_info cbaf_host_info_defaults = {
|
||||
.AssociationTypeId_hdr = WUSB_AR_AssociationTypeId,
|
||||
.AssociationTypeId = cpu_to_le16(AR_TYPE_WUSB),
|
||||
.AssociationSubTypeId_hdr = WUSB_AR_AssociationSubTypeId,
|
||||
.AssociationSubTypeId = cpu_to_le16(AR_TYPE_WUSB_RETRIEVE_HOST_INFO),
|
||||
.CHID_hdr = WUSB_AR_CHID,
|
||||
.LangID_hdr = WUSB_AR_LangID,
|
||||
.HostFriendlyName_hdr = WUSB_AR_HostFriendlyName,
|
||||
};
|
||||
|
||||
/* Send WUSB host information (CHID and name) to a CBAF device */
|
||||
static int cbaf_send_host_info(struct cbaf *cbaf)
|
||||
{
|
||||
struct wusb_cbaf_host_info *hi;
|
||||
size_t name_len;
|
||||
size_t hi_size;
|
||||
|
||||
hi = cbaf->buffer;
|
||||
memset(hi, 0, sizeof(*hi));
|
||||
*hi = cbaf_host_info_defaults;
|
||||
hi->CHID = cbaf->chid;
|
||||
hi->LangID = 0; /* FIXME: I guess... */
|
||||
strlcpy(hi->HostFriendlyName, cbaf->host_name, CBA_NAME_LEN);
|
||||
name_len = strlen(cbaf->host_name);
|
||||
hi->HostFriendlyName_hdr.len = cpu_to_le16(name_len);
|
||||
hi_size = sizeof(*hi) + name_len;
|
||||
|
||||
return usb_control_msg(cbaf->usb_dev,
|
||||
usb_sndctrlpipe(cbaf->usb_dev, 0),
|
||||
CBAF_REQ_SET_ASSOCIATION_RESPONSE,
|
||||
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
0x0101,
|
||||
cbaf->usb_iface->cur_altsetting->desc.bInterfaceNumber,
|
||||
hi, hi_size, USB_CTRL_SET_TIMEOUT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get device's information (CDID) associated to CHID
|
||||
*
|
||||
* The device will return it's information (CDID, name, bandgroups)
|
||||
* associated to the CHID we have set before, or 0 CDID and default
|
||||
* name and bandgroup if no CHID set or unknown.
|
||||
*/
|
||||
static int cbaf_cdid_get(struct cbaf *cbaf)
|
||||
{
|
||||
int result;
|
||||
struct device *dev = &cbaf->usb_iface->dev;
|
||||
struct wusb_cbaf_device_info *di;
|
||||
size_t needed;
|
||||
|
||||
di = cbaf->buffer;
|
||||
result = usb_control_msg(
|
||||
cbaf->usb_dev, usb_rcvctrlpipe(cbaf->usb_dev, 0),
|
||||
CBAF_REQ_GET_ASSOCIATION_REQUEST,
|
||||
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
0x0200, cbaf->usb_iface->cur_altsetting->desc.bInterfaceNumber,
|
||||
di, cbaf->buffer_size, USB_CTRL_GET_TIMEOUT);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "Cannot request device information: %d\n",
|
||||
result);
|
||||
return result;
|
||||
}
|
||||
|
||||
needed = result < sizeof(*di) ? sizeof(*di) : le32_to_cpu(di->Length);
|
||||
if (result < needed) {
|
||||
dev_err(dev, "Not enough data in DEVICE_INFO reply (%zu vs "
|
||||
"%zu bytes needed)\n", (size_t)result, needed);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
strlcpy(cbaf->device_name, di->DeviceFriendlyName, CBA_NAME_LEN);
|
||||
cbaf->cdid = di->CDID;
|
||||
cbaf->device_band_groups = le16_to_cpu(di->BandGroups);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t cbaf_wusb_chid_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct usb_interface *iface = to_usb_interface(dev);
|
||||
struct cbaf *cbaf = usb_get_intfdata(iface);
|
||||
char pr_chid[WUSB_CKHDID_STRSIZE];
|
||||
|
||||
ckhdid_printf(pr_chid, sizeof(pr_chid), &cbaf->chid);
|
||||
return scnprintf(buf, PAGE_SIZE, "%s\n", pr_chid);
|
||||
}
|
||||
|
||||
static ssize_t cbaf_wusb_chid_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
ssize_t result;
|
||||
struct usb_interface *iface = to_usb_interface(dev);
|
||||
struct cbaf *cbaf = usb_get_intfdata(iface);
|
||||
|
||||
result = sscanf(buf,
|
||||
"%02hhx %02hhx %02hhx %02hhx "
|
||||
"%02hhx %02hhx %02hhx %02hhx "
|
||||
"%02hhx %02hhx %02hhx %02hhx "
|
||||
"%02hhx %02hhx %02hhx %02hhx",
|
||||
&cbaf->chid.data[0] , &cbaf->chid.data[1],
|
||||
&cbaf->chid.data[2] , &cbaf->chid.data[3],
|
||||
&cbaf->chid.data[4] , &cbaf->chid.data[5],
|
||||
&cbaf->chid.data[6] , &cbaf->chid.data[7],
|
||||
&cbaf->chid.data[8] , &cbaf->chid.data[9],
|
||||
&cbaf->chid.data[10], &cbaf->chid.data[11],
|
||||
&cbaf->chid.data[12], &cbaf->chid.data[13],
|
||||
&cbaf->chid.data[14], &cbaf->chid.data[15]);
|
||||
|
||||
if (result != 16)
|
||||
return -EINVAL;
|
||||
|
||||
result = cbaf_send_host_info(cbaf);
|
||||
if (result < 0)
|
||||
return result;
|
||||
result = cbaf_cdid_get(cbaf);
|
||||
if (result < 0)
|
||||
return result;
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR(wusb_chid, 0600, cbaf_wusb_chid_show, cbaf_wusb_chid_store);
|
||||
|
||||
static ssize_t cbaf_wusb_host_name_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct usb_interface *iface = to_usb_interface(dev);
|
||||
struct cbaf *cbaf = usb_get_intfdata(iface);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%s\n", cbaf->host_name);
|
||||
}
|
||||
|
||||
static ssize_t cbaf_wusb_host_name_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
ssize_t result;
|
||||
struct usb_interface *iface = to_usb_interface(dev);
|
||||
struct cbaf *cbaf = usb_get_intfdata(iface);
|
||||
|
||||
result = sscanf(buf, "%63s", cbaf->host_name);
|
||||
if (result != 1)
|
||||
return -EINVAL;
|
||||
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR(wusb_host_name, 0600, cbaf_wusb_host_name_show,
|
||||
cbaf_wusb_host_name_store);
|
||||
|
||||
static ssize_t cbaf_wusb_host_band_groups_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct usb_interface *iface = to_usb_interface(dev);
|
||||
struct cbaf *cbaf = usb_get_intfdata(iface);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "0x%04x\n", cbaf->host_band_groups);
|
||||
}
|
||||
|
||||
static ssize_t cbaf_wusb_host_band_groups_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
ssize_t result;
|
||||
struct usb_interface *iface = to_usb_interface(dev);
|
||||
struct cbaf *cbaf = usb_get_intfdata(iface);
|
||||
u16 band_groups = 0;
|
||||
|
||||
result = sscanf(buf, "%04hx", &band_groups);
|
||||
if (result != 1)
|
||||
return -EINVAL;
|
||||
|
||||
cbaf->host_band_groups = band_groups;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(wusb_host_band_groups, 0600,
|
||||
cbaf_wusb_host_band_groups_show,
|
||||
cbaf_wusb_host_band_groups_store);
|
||||
|
||||
static const struct wusb_cbaf_device_info cbaf_device_info_defaults = {
|
||||
.Length_hdr = WUSB_AR_Length,
|
||||
.CDID_hdr = WUSB_AR_CDID,
|
||||
.BandGroups_hdr = WUSB_AR_BandGroups,
|
||||
.LangID_hdr = WUSB_AR_LangID,
|
||||
.DeviceFriendlyName_hdr = WUSB_AR_DeviceFriendlyName,
|
||||
};
|
||||
|
||||
static ssize_t cbaf_wusb_cdid_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_interface *iface = to_usb_interface(dev);
|
||||
struct cbaf *cbaf = usb_get_intfdata(iface);
|
||||
char pr_cdid[WUSB_CKHDID_STRSIZE];
|
||||
|
||||
ckhdid_printf(pr_cdid, sizeof(pr_cdid), &cbaf->cdid);
|
||||
return scnprintf(buf, PAGE_SIZE, "%s\n", pr_cdid);
|
||||
}
|
||||
|
||||
static ssize_t cbaf_wusb_cdid_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
ssize_t result;
|
||||
struct usb_interface *iface = to_usb_interface(dev);
|
||||
struct cbaf *cbaf = usb_get_intfdata(iface);
|
||||
struct wusb_ckhdid cdid;
|
||||
|
||||
result = sscanf(buf,
|
||||
"%02hhx %02hhx %02hhx %02hhx "
|
||||
"%02hhx %02hhx %02hhx %02hhx "
|
||||
"%02hhx %02hhx %02hhx %02hhx "
|
||||
"%02hhx %02hhx %02hhx %02hhx",
|
||||
&cdid.data[0] , &cdid.data[1],
|
||||
&cdid.data[2] , &cdid.data[3],
|
||||
&cdid.data[4] , &cdid.data[5],
|
||||
&cdid.data[6] , &cdid.data[7],
|
||||
&cdid.data[8] , &cdid.data[9],
|
||||
&cdid.data[10], &cdid.data[11],
|
||||
&cdid.data[12], &cdid.data[13],
|
||||
&cdid.data[14], &cdid.data[15]);
|
||||
if (result != 16)
|
||||
return -EINVAL;
|
||||
|
||||
cbaf->cdid = cdid;
|
||||
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR(wusb_cdid, 0600, cbaf_wusb_cdid_show, cbaf_wusb_cdid_store);
|
||||
|
||||
static ssize_t cbaf_wusb_device_band_groups_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct usb_interface *iface = to_usb_interface(dev);
|
||||
struct cbaf *cbaf = usb_get_intfdata(iface);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "0x%04x\n", cbaf->device_band_groups);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(wusb_device_band_groups, 0600,
|
||||
cbaf_wusb_device_band_groups_show,
|
||||
NULL);
|
||||
|
||||
static ssize_t cbaf_wusb_device_name_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct usb_interface *iface = to_usb_interface(dev);
|
||||
struct cbaf *cbaf = usb_get_intfdata(iface);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%s\n", cbaf->device_name);
|
||||
}
|
||||
static DEVICE_ATTR(wusb_device_name, 0600, cbaf_wusb_device_name_show, NULL);
|
||||
|
||||
static const struct wusb_cbaf_cc_data cbaf_cc_data_defaults = {
|
||||
.AssociationTypeId_hdr = WUSB_AR_AssociationTypeId,
|
||||
.AssociationTypeId = cpu_to_le16(AR_TYPE_WUSB),
|
||||
.AssociationSubTypeId_hdr = WUSB_AR_AssociationSubTypeId,
|
||||
.AssociationSubTypeId = cpu_to_le16(AR_TYPE_WUSB_ASSOCIATE),
|
||||
.Length_hdr = WUSB_AR_Length,
|
||||
.Length = cpu_to_le32(sizeof(struct wusb_cbaf_cc_data)),
|
||||
.ConnectionContext_hdr = WUSB_AR_ConnectionContext,
|
||||
.BandGroups_hdr = WUSB_AR_BandGroups,
|
||||
};
|
||||
|
||||
static const struct wusb_cbaf_cc_data_fail cbaf_cc_data_fail_defaults = {
|
||||
.AssociationTypeId_hdr = WUSB_AR_AssociationTypeId,
|
||||
.AssociationSubTypeId_hdr = WUSB_AR_AssociationSubTypeId,
|
||||
.Length_hdr = WUSB_AR_Length,
|
||||
.AssociationStatus_hdr = WUSB_AR_AssociationStatus,
|
||||
};
|
||||
|
||||
/*
|
||||
* Send a new CC to the device.
|
||||
*/
|
||||
static int cbaf_cc_upload(struct cbaf *cbaf)
|
||||
{
|
||||
int result;
|
||||
struct device *dev = &cbaf->usb_iface->dev;
|
||||
struct wusb_cbaf_cc_data *ccd;
|
||||
char pr_cdid[WUSB_CKHDID_STRSIZE];
|
||||
|
||||
ccd = cbaf->buffer;
|
||||
*ccd = cbaf_cc_data_defaults;
|
||||
ccd->CHID = cbaf->chid;
|
||||
ccd->CDID = cbaf->cdid;
|
||||
ccd->CK = cbaf->ck;
|
||||
ccd->BandGroups = cpu_to_le16(cbaf->host_band_groups);
|
||||
|
||||
dev_dbg(dev, "Trying to upload CC:\n");
|
||||
ckhdid_printf(pr_cdid, sizeof(pr_cdid), &ccd->CHID);
|
||||
dev_dbg(dev, " CHID %s\n", pr_cdid);
|
||||
ckhdid_printf(pr_cdid, sizeof(pr_cdid), &ccd->CDID);
|
||||
dev_dbg(dev, " CDID %s\n", pr_cdid);
|
||||
dev_dbg(dev, " Bandgroups 0x%04x\n", cbaf->host_band_groups);
|
||||
|
||||
result = usb_control_msg(
|
||||
cbaf->usb_dev, usb_sndctrlpipe(cbaf->usb_dev, 0),
|
||||
CBAF_REQ_SET_ASSOCIATION_RESPONSE,
|
||||
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
0x0201, cbaf->usb_iface->cur_altsetting->desc.bInterfaceNumber,
|
||||
ccd, sizeof(*ccd), USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static ssize_t cbaf_wusb_ck_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
ssize_t result;
|
||||
struct usb_interface *iface = to_usb_interface(dev);
|
||||
struct cbaf *cbaf = usb_get_intfdata(iface);
|
||||
|
||||
result = sscanf(buf,
|
||||
"%02hhx %02hhx %02hhx %02hhx "
|
||||
"%02hhx %02hhx %02hhx %02hhx "
|
||||
"%02hhx %02hhx %02hhx %02hhx "
|
||||
"%02hhx %02hhx %02hhx %02hhx",
|
||||
&cbaf->ck.data[0] , &cbaf->ck.data[1],
|
||||
&cbaf->ck.data[2] , &cbaf->ck.data[3],
|
||||
&cbaf->ck.data[4] , &cbaf->ck.data[5],
|
||||
&cbaf->ck.data[6] , &cbaf->ck.data[7],
|
||||
&cbaf->ck.data[8] , &cbaf->ck.data[9],
|
||||
&cbaf->ck.data[10], &cbaf->ck.data[11],
|
||||
&cbaf->ck.data[12], &cbaf->ck.data[13],
|
||||
&cbaf->ck.data[14], &cbaf->ck.data[15]);
|
||||
if (result != 16)
|
||||
return -EINVAL;
|
||||
|
||||
result = cbaf_cc_upload(cbaf);
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR(wusb_ck, 0600, NULL, cbaf_wusb_ck_store);
|
||||
|
||||
static struct attribute *cbaf_dev_attrs[] = {
|
||||
&dev_attr_wusb_host_name.attr,
|
||||
&dev_attr_wusb_host_band_groups.attr,
|
||||
&dev_attr_wusb_chid.attr,
|
||||
&dev_attr_wusb_cdid.attr,
|
||||
&dev_attr_wusb_device_name.attr,
|
||||
&dev_attr_wusb_device_band_groups.attr,
|
||||
&dev_attr_wusb_ck.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group cbaf_dev_attr_group = {
|
||||
.name = NULL, /* we want them in the same directory */
|
||||
.attrs = cbaf_dev_attrs,
|
||||
};
|
||||
|
||||
static int cbaf_probe(struct usb_interface *iface,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct cbaf *cbaf;
|
||||
struct device *dev = &iface->dev;
|
||||
int result = -ENOMEM;
|
||||
|
||||
cbaf = kzalloc(sizeof(*cbaf), GFP_KERNEL);
|
||||
if (cbaf == NULL)
|
||||
goto error_kzalloc;
|
||||
cbaf->buffer = kmalloc(512, GFP_KERNEL);
|
||||
if (cbaf->buffer == NULL)
|
||||
goto error_kmalloc_buffer;
|
||||
|
||||
cbaf->buffer_size = 512;
|
||||
cbaf->usb_dev = usb_get_dev(interface_to_usbdev(iface));
|
||||
cbaf->usb_iface = usb_get_intf(iface);
|
||||
result = cbaf_check(cbaf);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "This device is not WUSB-CBAF compliant"
|
||||
"and is not supported yet.\n");
|
||||
goto error_check;
|
||||
}
|
||||
|
||||
result = sysfs_create_group(&dev->kobj, &cbaf_dev_attr_group);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "Can't register sysfs attr group: %d\n", result);
|
||||
goto error_create_group;
|
||||
}
|
||||
usb_set_intfdata(iface, cbaf);
|
||||
return 0;
|
||||
|
||||
error_create_group:
|
||||
error_check:
|
||||
usb_put_intf(iface);
|
||||
usb_put_dev(cbaf->usb_dev);
|
||||
kfree(cbaf->buffer);
|
||||
error_kmalloc_buffer:
|
||||
kfree(cbaf);
|
||||
error_kzalloc:
|
||||
return result;
|
||||
}
|
||||
|
||||
static void cbaf_disconnect(struct usb_interface *iface)
|
||||
{
|
||||
struct cbaf *cbaf = usb_get_intfdata(iface);
|
||||
struct device *dev = &iface->dev;
|
||||
sysfs_remove_group(&dev->kobj, &cbaf_dev_attr_group);
|
||||
usb_set_intfdata(iface, NULL);
|
||||
usb_put_intf(iface);
|
||||
usb_put_dev(cbaf->usb_dev);
|
||||
kfree(cbaf->buffer);
|
||||
/* paranoia: clean up crypto keys */
|
||||
kzfree(cbaf);
|
||||
}
|
||||
|
||||
static const struct usb_device_id cbaf_id_table[] = {
|
||||
{ USB_INTERFACE_INFO(0xef, 0x03, 0x01), },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, cbaf_id_table);
|
||||
|
||||
static struct usb_driver cbaf_driver = {
|
||||
.name = "wusb-cbaf",
|
||||
.id_table = cbaf_id_table,
|
||||
.probe = cbaf_probe,
|
||||
.disconnect = cbaf_disconnect,
|
||||
};
|
||||
|
||||
module_usb_driver(cbaf_driver);
|
||||
|
||||
MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
|
||||
MODULE_DESCRIPTION("Wireless USB Cable Based Association");
|
||||
MODULE_LICENSE("GPL");
|
516
drivers/usb/wusbcore/crypto.c
Normal file
516
drivers/usb/wusbcore/crypto.c
Normal file
|
@ -0,0 +1,516 @@
|
|||
/*
|
||||
* Ultra Wide Band
|
||||
* AES-128 CCM Encryption
|
||||
*
|
||||
* Copyright (C) 2007 Intel Corporation
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* We don't do any encryption here; we use the Linux Kernel's AES-128
|
||||
* crypto modules to construct keys and payload blocks in a way
|
||||
* defined by WUSB1.0[6]. Check the erratas, as typos are are patched
|
||||
* there.
|
||||
*
|
||||
* Thanks a zillion to John Keys for his help and clarifications over
|
||||
* the designed-by-a-committee text.
|
||||
*
|
||||
* So the idea is that there is this basic Pseudo-Random-Function
|
||||
* defined in WUSB1.0[6.5] which is the core of everything. It works
|
||||
* by tweaking some blocks, AES crypting them and then xoring
|
||||
* something else with them (this seems to be called CBC(AES) -- can
|
||||
* you tell I know jack about crypto?). So we just funnel it into the
|
||||
* Linux Crypto API.
|
||||
*
|
||||
* We leave a crypto test module so we can verify that vectors match,
|
||||
* every now and then.
|
||||
*
|
||||
* Block size: 16 bytes -- AES seems to do things in 'block sizes'. I
|
||||
* am learning a lot...
|
||||
*
|
||||
* Conveniently, some data structures that need to be
|
||||
* funneled through AES are...16 bytes in size!
|
||||
*/
|
||||
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/uwb.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/usb/wusb.h>
|
||||
#include <linux/scatterlist.h>
|
||||
|
||||
static int debug_crypto_verify = 0;
|
||||
|
||||
module_param(debug_crypto_verify, int, 0);
|
||||
MODULE_PARM_DESC(debug_crypto_verify, "verify the key generation algorithms");
|
||||
|
||||
static void wusb_key_dump(const void *buf, size_t len)
|
||||
{
|
||||
print_hex_dump(KERN_ERR, " ", DUMP_PREFIX_OFFSET, 16, 1,
|
||||
buf, len, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Block of data, as understood by AES-CCM
|
||||
*
|
||||
* The code assumes this structure is nothing but a 16 byte array
|
||||
* (packed in a struct to avoid common mess ups that I usually do with
|
||||
* arrays and enforcing type checking).
|
||||
*/
|
||||
struct aes_ccm_block {
|
||||
u8 data[16];
|
||||
} __attribute__((packed));
|
||||
|
||||
/*
|
||||
* Counter-mode Blocks (WUSB1.0[6.4])
|
||||
*
|
||||
* According to CCM (or so it seems), for the purpose of calculating
|
||||
* the MIC, the message is broken in N counter-mode blocks, B0, B1,
|
||||
* ... BN.
|
||||
*
|
||||
* B0 contains flags, the CCM nonce and l(m).
|
||||
*
|
||||
* B1 contains l(a), the MAC header, the encryption offset and padding.
|
||||
*
|
||||
* If EO is nonzero, additional blocks are built from payload bytes
|
||||
* until EO is exhausted (FIXME: padding to 16 bytes, I guess). The
|
||||
* padding is not xmitted.
|
||||
*/
|
||||
|
||||
/* WUSB1.0[T6.4] */
|
||||
struct aes_ccm_b0 {
|
||||
u8 flags; /* 0x59, per CCM spec */
|
||||
struct aes_ccm_nonce ccm_nonce;
|
||||
__be16 lm;
|
||||
} __attribute__((packed));
|
||||
|
||||
/* WUSB1.0[T6.5] */
|
||||
struct aes_ccm_b1 {
|
||||
__be16 la;
|
||||
u8 mac_header[10];
|
||||
__le16 eo;
|
||||
u8 security_reserved; /* This is always zero */
|
||||
u8 padding; /* 0 */
|
||||
} __attribute__((packed));
|
||||
|
||||
/*
|
||||
* Encryption Blocks (WUSB1.0[6.4.4])
|
||||
*
|
||||
* CCM uses Ax blocks to generate a keystream with which the MIC and
|
||||
* the message's payload are encoded. A0 always encrypts/decrypts the
|
||||
* MIC. Ax (x>0) are used for the successive payload blocks.
|
||||
*
|
||||
* The x is the counter, and is increased for each block.
|
||||
*/
|
||||
struct aes_ccm_a {
|
||||
u8 flags; /* 0x01, per CCM spec */
|
||||
struct aes_ccm_nonce ccm_nonce;
|
||||
__be16 counter; /* Value of x */
|
||||
} __attribute__((packed));
|
||||
|
||||
static void bytewise_xor(void *_bo, const void *_bi1, const void *_bi2,
|
||||
size_t size)
|
||||
{
|
||||
u8 *bo = _bo;
|
||||
const u8 *bi1 = _bi1, *bi2 = _bi2;
|
||||
size_t itr;
|
||||
for (itr = 0; itr < size; itr++)
|
||||
bo[itr] = bi1[itr] ^ bi2[itr];
|
||||
}
|
||||
|
||||
/*
|
||||
* CC-MAC function WUSB1.0[6.5]
|
||||
*
|
||||
* Take a data string and produce the encrypted CBC Counter-mode MIC
|
||||
*
|
||||
* Note the names for most function arguments are made to (more or
|
||||
* less) match those used in the pseudo-function definition given in
|
||||
* WUSB1.0[6.5].
|
||||
*
|
||||
* @tfm_cbc: CBC(AES) blkcipher handle (initialized)
|
||||
*
|
||||
* @tfm_aes: AES cipher handle (initialized)
|
||||
*
|
||||
* @mic: buffer for placing the computed MIC (Message Integrity
|
||||
* Code). This is exactly 8 bytes, and we expect the buffer to
|
||||
* be at least eight bytes in length.
|
||||
*
|
||||
* @key: 128 bit symmetric key
|
||||
*
|
||||
* @n: CCM nonce
|
||||
*
|
||||
* @a: ASCII string, 14 bytes long (I guess zero padded if needed;
|
||||
* we use exactly 14 bytes).
|
||||
*
|
||||
* @b: data stream to be processed; cannot be a global or const local
|
||||
* (will confuse the scatterlists)
|
||||
*
|
||||
* @blen: size of b...
|
||||
*
|
||||
* Still not very clear how this is done, but looks like this: we
|
||||
* create block B0 (as WUSB1.0[6.5] says), then we AES-crypt it with
|
||||
* @key. We bytewise xor B0 with B1 (1) and AES-crypt that. Then we
|
||||
* take the payload and divide it in blocks (16 bytes), xor them with
|
||||
* the previous crypto result (16 bytes) and crypt it, repeat the next
|
||||
* block with the output of the previous one, rinse wash (I guess this
|
||||
* is what AES CBC mode means...but I truly have no idea). So we use
|
||||
* the CBC(AES) blkcipher, that does precisely that. The IV (Initial
|
||||
* Vector) is 16 bytes and is set to zero, so
|
||||
*
|
||||
* See rfc3610. Linux crypto has a CBC implementation, but the
|
||||
* documentation is scarce, to say the least, and the example code is
|
||||
* so intricated that is difficult to understand how things work. Most
|
||||
* of this is guess work -- bite me.
|
||||
*
|
||||
* (1) Created as 6.5 says, again, using as l(a) 'Blen + 14', and
|
||||
* using the 14 bytes of @a to fill up
|
||||
* b1.{mac_header,e0,security_reserved,padding}.
|
||||
*
|
||||
* NOTE: The definition of l(a) in WUSB1.0[6.5] vs the definition of
|
||||
* l(m) is orthogonal, they bear no relationship, so it is not
|
||||
* in conflict with the parameter's relation that
|
||||
* WUSB1.0[6.4.2]) defines.
|
||||
*
|
||||
* NOTE: WUSB1.0[A.1]: Host Nonce is missing a nibble? (1e); fixed in
|
||||
* first errata released on 2005/07.
|
||||
*
|
||||
* NOTE: we need to clean IV to zero at each invocation to make sure
|
||||
* we start with a fresh empty Initial Vector, so that the CBC
|
||||
* works ok.
|
||||
*
|
||||
* NOTE: blen is not aligned to a block size, we'll pad zeros, that's
|
||||
* what sg[4] is for. Maybe there is a smarter way to do this.
|
||||
*/
|
||||
static int wusb_ccm_mac(struct crypto_blkcipher *tfm_cbc,
|
||||
struct crypto_cipher *tfm_aes, void *mic,
|
||||
const struct aes_ccm_nonce *n,
|
||||
const struct aes_ccm_label *a, const void *b,
|
||||
size_t blen)
|
||||
{
|
||||
int result = 0;
|
||||
struct blkcipher_desc desc;
|
||||
struct aes_ccm_b0 b0;
|
||||
struct aes_ccm_b1 b1;
|
||||
struct aes_ccm_a ax;
|
||||
struct scatterlist sg[4], sg_dst;
|
||||
void *iv, *dst_buf;
|
||||
size_t ivsize, dst_size;
|
||||
const u8 bzero[16] = { 0 };
|
||||
size_t zero_padding;
|
||||
|
||||
/*
|
||||
* These checks should be compile time optimized out
|
||||
* ensure @a fills b1's mac_header and following fields
|
||||
*/
|
||||
WARN_ON(sizeof(*a) != sizeof(b1) - sizeof(b1.la));
|
||||
WARN_ON(sizeof(b0) != sizeof(struct aes_ccm_block));
|
||||
WARN_ON(sizeof(b1) != sizeof(struct aes_ccm_block));
|
||||
WARN_ON(sizeof(ax) != sizeof(struct aes_ccm_block));
|
||||
|
||||
result = -ENOMEM;
|
||||
zero_padding = blen % sizeof(struct aes_ccm_block);
|
||||
if (zero_padding)
|
||||
zero_padding = sizeof(struct aes_ccm_block) - zero_padding;
|
||||
dst_size = blen + sizeof(b0) + sizeof(b1) + zero_padding;
|
||||
dst_buf = kzalloc(dst_size, GFP_KERNEL);
|
||||
if (dst_buf == NULL) {
|
||||
printk(KERN_ERR "E: can't alloc destination buffer\n");
|
||||
goto error_dst_buf;
|
||||
}
|
||||
|
||||
iv = crypto_blkcipher_crt(tfm_cbc)->iv;
|
||||
ivsize = crypto_blkcipher_ivsize(tfm_cbc);
|
||||
memset(iv, 0, ivsize);
|
||||
|
||||
/* Setup B0 */
|
||||
b0.flags = 0x59; /* Format B0 */
|
||||
b0.ccm_nonce = *n;
|
||||
b0.lm = cpu_to_be16(0); /* WUSB1.0[6.5] sez l(m) is 0 */
|
||||
|
||||
/* Setup B1
|
||||
*
|
||||
* The WUSB spec is anything but clear! WUSB1.0[6.5]
|
||||
* says that to initialize B1 from A with 'l(a) = blen +
|
||||
* 14'--after clarification, it means to use A's contents
|
||||
* for MAC Header, EO, sec reserved and padding.
|
||||
*/
|
||||
b1.la = cpu_to_be16(blen + 14);
|
||||
memcpy(&b1.mac_header, a, sizeof(*a));
|
||||
|
||||
sg_init_table(sg, ARRAY_SIZE(sg));
|
||||
sg_set_buf(&sg[0], &b0, sizeof(b0));
|
||||
sg_set_buf(&sg[1], &b1, sizeof(b1));
|
||||
sg_set_buf(&sg[2], b, blen);
|
||||
/* 0 if well behaved :) */
|
||||
sg_set_buf(&sg[3], bzero, zero_padding);
|
||||
sg_init_one(&sg_dst, dst_buf, dst_size);
|
||||
|
||||
desc.tfm = tfm_cbc;
|
||||
desc.flags = 0;
|
||||
result = crypto_blkcipher_encrypt(&desc, &sg_dst, sg, dst_size);
|
||||
if (result < 0) {
|
||||
printk(KERN_ERR "E: can't compute CBC-MAC tag (MIC): %d\n",
|
||||
result);
|
||||
goto error_cbc_crypt;
|
||||
}
|
||||
|
||||
/* Now we crypt the MIC Tag (*iv) with Ax -- values per WUSB1.0[6.5]
|
||||
* The procedure is to AES crypt the A0 block and XOR the MIC
|
||||
* Tag against it; we only do the first 8 bytes and place it
|
||||
* directly in the destination buffer.
|
||||
*
|
||||
* POS Crypto API: size is assumed to be AES's block size.
|
||||
* Thanks for documenting it -- tip taken from airo.c
|
||||
*/
|
||||
ax.flags = 0x01; /* as per WUSB 1.0 spec */
|
||||
ax.ccm_nonce = *n;
|
||||
ax.counter = 0;
|
||||
crypto_cipher_encrypt_one(tfm_aes, (void *)&ax, (void *)&ax);
|
||||
bytewise_xor(mic, &ax, iv, 8);
|
||||
result = 8;
|
||||
error_cbc_crypt:
|
||||
kfree(dst_buf);
|
||||
error_dst_buf:
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* WUSB Pseudo Random Function (WUSB1.0[6.5])
|
||||
*
|
||||
* @b: buffer to the source data; cannot be a global or const local
|
||||
* (will confuse the scatterlists)
|
||||
*/
|
||||
ssize_t wusb_prf(void *out, size_t out_size,
|
||||
const u8 key[16], const struct aes_ccm_nonce *_n,
|
||||
const struct aes_ccm_label *a,
|
||||
const void *b, size_t blen, size_t len)
|
||||
{
|
||||
ssize_t result, bytes = 0, bitr;
|
||||
struct aes_ccm_nonce n = *_n;
|
||||
struct crypto_blkcipher *tfm_cbc;
|
||||
struct crypto_cipher *tfm_aes;
|
||||
u64 sfn = 0;
|
||||
__le64 sfn_le;
|
||||
|
||||
tfm_cbc = crypto_alloc_blkcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(tfm_cbc)) {
|
||||
result = PTR_ERR(tfm_cbc);
|
||||
printk(KERN_ERR "E: can't load CBC(AES): %d\n", (int)result);
|
||||
goto error_alloc_cbc;
|
||||
}
|
||||
result = crypto_blkcipher_setkey(tfm_cbc, key, 16);
|
||||
if (result < 0) {
|
||||
printk(KERN_ERR "E: can't set CBC key: %d\n", (int)result);
|
||||
goto error_setkey_cbc;
|
||||
}
|
||||
|
||||
tfm_aes = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(tfm_aes)) {
|
||||
result = PTR_ERR(tfm_aes);
|
||||
printk(KERN_ERR "E: can't load AES: %d\n", (int)result);
|
||||
goto error_alloc_aes;
|
||||
}
|
||||
result = crypto_cipher_setkey(tfm_aes, key, 16);
|
||||
if (result < 0) {
|
||||
printk(KERN_ERR "E: can't set AES key: %d\n", (int)result);
|
||||
goto error_setkey_aes;
|
||||
}
|
||||
|
||||
for (bitr = 0; bitr < (len + 63) / 64; bitr++) {
|
||||
sfn_le = cpu_to_le64(sfn++);
|
||||
memcpy(&n.sfn, &sfn_le, sizeof(n.sfn)); /* n.sfn++... */
|
||||
result = wusb_ccm_mac(tfm_cbc, tfm_aes, out + bytes,
|
||||
&n, a, b, blen);
|
||||
if (result < 0)
|
||||
goto error_ccm_mac;
|
||||
bytes += result;
|
||||
}
|
||||
result = bytes;
|
||||
error_ccm_mac:
|
||||
error_setkey_aes:
|
||||
crypto_free_cipher(tfm_aes);
|
||||
error_alloc_aes:
|
||||
error_setkey_cbc:
|
||||
crypto_free_blkcipher(tfm_cbc);
|
||||
error_alloc_cbc:
|
||||
return result;
|
||||
}
|
||||
|
||||
/* WUSB1.0[A.2] test vectors */
|
||||
static const u8 stv_hsmic_key[16] = {
|
||||
0x4b, 0x79, 0xa3, 0xcf, 0xe5, 0x53, 0x23, 0x9d,
|
||||
0xd7, 0xc1, 0x6d, 0x1c, 0x2d, 0xab, 0x6d, 0x3f
|
||||
};
|
||||
|
||||
static const struct aes_ccm_nonce stv_hsmic_n = {
|
||||
.sfn = { 0 },
|
||||
.tkid = { 0x76, 0x98, 0x01, },
|
||||
.dest_addr = { .data = { 0xbe, 0x00 } },
|
||||
.src_addr = { .data = { 0x76, 0x98 } },
|
||||
};
|
||||
|
||||
/*
|
||||
* Out-of-band MIC Generation verification code
|
||||
*
|
||||
*/
|
||||
static int wusb_oob_mic_verify(void)
|
||||
{
|
||||
int result;
|
||||
u8 mic[8];
|
||||
/* WUSB1.0[A.2] test vectors
|
||||
*
|
||||
* Need to keep it in the local stack as GCC 4.1.3something
|
||||
* messes up and generates noise.
|
||||
*/
|
||||
struct usb_handshake stv_hsmic_hs = {
|
||||
.bMessageNumber = 2,
|
||||
.bStatus = 00,
|
||||
.tTKID = { 0x76, 0x98, 0x01 },
|
||||
.bReserved = 00,
|
||||
.CDID = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
|
||||
0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
|
||||
0x3c, 0x3d, 0x3e, 0x3f },
|
||||
.nonce = { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
|
||||
0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
|
||||
0x2c, 0x2d, 0x2e, 0x2f },
|
||||
.MIC = { 0x75, 0x6a, 0x97, 0x51, 0x0c, 0x8c,
|
||||
0x14, 0x7b } ,
|
||||
};
|
||||
size_t hs_size;
|
||||
|
||||
result = wusb_oob_mic(mic, stv_hsmic_key, &stv_hsmic_n, &stv_hsmic_hs);
|
||||
if (result < 0)
|
||||
printk(KERN_ERR "E: WUSB OOB MIC test: failed: %d\n", result);
|
||||
else if (memcmp(stv_hsmic_hs.MIC, mic, sizeof(mic))) {
|
||||
printk(KERN_ERR "E: OOB MIC test: "
|
||||
"mismatch between MIC result and WUSB1.0[A2]\n");
|
||||
hs_size = sizeof(stv_hsmic_hs) - sizeof(stv_hsmic_hs.MIC);
|
||||
printk(KERN_ERR "E: Handshake2 in: (%zu bytes)\n", hs_size);
|
||||
wusb_key_dump(&stv_hsmic_hs, hs_size);
|
||||
printk(KERN_ERR "E: CCM Nonce in: (%zu bytes)\n",
|
||||
sizeof(stv_hsmic_n));
|
||||
wusb_key_dump(&stv_hsmic_n, sizeof(stv_hsmic_n));
|
||||
printk(KERN_ERR "E: MIC out:\n");
|
||||
wusb_key_dump(mic, sizeof(mic));
|
||||
printk(KERN_ERR "E: MIC out (from WUSB1.0[A.2]):\n");
|
||||
wusb_key_dump(stv_hsmic_hs.MIC, sizeof(stv_hsmic_hs.MIC));
|
||||
result = -EINVAL;
|
||||
} else
|
||||
result = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test vectors for Key derivation
|
||||
*
|
||||
* These come from WUSB1.0[6.5.1], the vectors in WUSB1.0[A.1]
|
||||
* (errata corrected in 2005/07).
|
||||
*/
|
||||
static const u8 stv_key_a1[16] __attribute__ ((__aligned__(4))) = {
|
||||
0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87,
|
||||
0x78, 0x69, 0x5a, 0x4b, 0x3c, 0x2d, 0x1e, 0x0f
|
||||
};
|
||||
|
||||
static const struct aes_ccm_nonce stv_keydvt_n_a1 = {
|
||||
.sfn = { 0 },
|
||||
.tkid = { 0x76, 0x98, 0x01, },
|
||||
.dest_addr = { .data = { 0xbe, 0x00 } },
|
||||
.src_addr = { .data = { 0x76, 0x98 } },
|
||||
};
|
||||
|
||||
static const struct wusb_keydvt_out stv_keydvt_out_a1 = {
|
||||
.kck = {
|
||||
0x4b, 0x79, 0xa3, 0xcf, 0xe5, 0x53, 0x23, 0x9d,
|
||||
0xd7, 0xc1, 0x6d, 0x1c, 0x2d, 0xab, 0x6d, 0x3f
|
||||
},
|
||||
.ptk = {
|
||||
0xc8, 0x70, 0x62, 0x82, 0xb6, 0x7c, 0xe9, 0x06,
|
||||
0x7b, 0xc5, 0x25, 0x69, 0xf2, 0x36, 0x61, 0x2d
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Performa a test to make sure we match the vectors defined in
|
||||
* WUSB1.0[A.1](Errata2006/12)
|
||||
*/
|
||||
static int wusb_key_derive_verify(void)
|
||||
{
|
||||
int result = 0;
|
||||
struct wusb_keydvt_out keydvt_out;
|
||||
/* These come from WUSB1.0[A.1] + 2006/12 errata
|
||||
* NOTE: can't make this const or global -- somehow it seems
|
||||
* the scatterlists for crypto get confused and we get
|
||||
* bad data. There is no doc on this... */
|
||||
struct wusb_keydvt_in stv_keydvt_in_a1 = {
|
||||
.hnonce = {
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
|
||||
},
|
||||
.dnonce = {
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
|
||||
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f
|
||||
}
|
||||
};
|
||||
|
||||
result = wusb_key_derive(&keydvt_out, stv_key_a1, &stv_keydvt_n_a1,
|
||||
&stv_keydvt_in_a1);
|
||||
if (result < 0)
|
||||
printk(KERN_ERR "E: WUSB key derivation test: "
|
||||
"derivation failed: %d\n", result);
|
||||
if (memcmp(&stv_keydvt_out_a1, &keydvt_out, sizeof(keydvt_out))) {
|
||||
printk(KERN_ERR "E: WUSB key derivation test: "
|
||||
"mismatch between key derivation result "
|
||||
"and WUSB1.0[A1] Errata 2006/12\n");
|
||||
printk(KERN_ERR "E: keydvt in: key\n");
|
||||
wusb_key_dump(stv_key_a1, sizeof(stv_key_a1));
|
||||
printk(KERN_ERR "E: keydvt in: nonce\n");
|
||||
wusb_key_dump( &stv_keydvt_n_a1, sizeof(stv_keydvt_n_a1));
|
||||
printk(KERN_ERR "E: keydvt in: hnonce & dnonce\n");
|
||||
wusb_key_dump(&stv_keydvt_in_a1, sizeof(stv_keydvt_in_a1));
|
||||
printk(KERN_ERR "E: keydvt out: KCK\n");
|
||||
wusb_key_dump(&keydvt_out.kck, sizeof(keydvt_out.kck));
|
||||
printk(KERN_ERR "E: keydvt out: PTK\n");
|
||||
wusb_key_dump(&keydvt_out.ptk, sizeof(keydvt_out.ptk));
|
||||
result = -EINVAL;
|
||||
} else
|
||||
result = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize crypto system
|
||||
*
|
||||
* FIXME: we do nothing now, other than verifying. Later on we'll
|
||||
* cache the encryption stuff, so that's why we have a separate init.
|
||||
*/
|
||||
int wusb_crypto_init(void)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (debug_crypto_verify) {
|
||||
result = wusb_key_derive_verify();
|
||||
if (result < 0)
|
||||
return result;
|
||||
return wusb_oob_mic_verify();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void wusb_crypto_exit(void)
|
||||
{
|
||||
/* FIXME: free cached crypto transforms */
|
||||
}
|
139
drivers/usb/wusbcore/dev-sysfs.c
Normal file
139
drivers/usb/wusbcore/dev-sysfs.c
Normal file
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* WUSB devices
|
||||
* sysfs bindings
|
||||
*
|
||||
* Copyright (C) 2007 Intel Corporation
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* Get them out of the way...
|
||||
*/
|
||||
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include "wusbhc.h"
|
||||
|
||||
static ssize_t wusb_disconnect_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct usb_device *usb_dev;
|
||||
struct wusbhc *wusbhc;
|
||||
unsigned command;
|
||||
u8 port_idx;
|
||||
|
||||
if (sscanf(buf, "%u", &command) != 1)
|
||||
return -EINVAL;
|
||||
if (command == 0)
|
||||
return size;
|
||||
usb_dev = to_usb_device(dev);
|
||||
wusbhc = wusbhc_get_by_usb_dev(usb_dev);
|
||||
if (wusbhc == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
mutex_lock(&wusbhc->mutex);
|
||||
port_idx = wusb_port_no_to_idx(usb_dev->portnum);
|
||||
__wusbhc_dev_disable(wusbhc, port_idx);
|
||||
mutex_unlock(&wusbhc->mutex);
|
||||
wusbhc_put(wusbhc);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR(wusb_disconnect, 0200, NULL, wusb_disconnect_store);
|
||||
|
||||
static ssize_t wusb_cdid_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
ssize_t result;
|
||||
struct wusb_dev *wusb_dev;
|
||||
|
||||
wusb_dev = wusb_dev_get_by_usb_dev(to_usb_device(dev));
|
||||
if (wusb_dev == NULL)
|
||||
return -ENODEV;
|
||||
result = ckhdid_printf(buf, PAGE_SIZE, &wusb_dev->cdid);
|
||||
strcat(buf, "\n");
|
||||
wusb_dev_put(wusb_dev);
|
||||
return result + 1;
|
||||
}
|
||||
static DEVICE_ATTR(wusb_cdid, 0444, wusb_cdid_show, NULL);
|
||||
|
||||
static ssize_t wusb_ck_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
int result;
|
||||
struct usb_device *usb_dev;
|
||||
struct wusbhc *wusbhc;
|
||||
struct wusb_ckhdid ck;
|
||||
|
||||
result = sscanf(buf,
|
||||
"%02hhx %02hhx %02hhx %02hhx "
|
||||
"%02hhx %02hhx %02hhx %02hhx "
|
||||
"%02hhx %02hhx %02hhx %02hhx "
|
||||
"%02hhx %02hhx %02hhx %02hhx\n",
|
||||
&ck.data[0] , &ck.data[1],
|
||||
&ck.data[2] , &ck.data[3],
|
||||
&ck.data[4] , &ck.data[5],
|
||||
&ck.data[6] , &ck.data[7],
|
||||
&ck.data[8] , &ck.data[9],
|
||||
&ck.data[10], &ck.data[11],
|
||||
&ck.data[12], &ck.data[13],
|
||||
&ck.data[14], &ck.data[15]);
|
||||
if (result != 16)
|
||||
return -EINVAL;
|
||||
|
||||
usb_dev = to_usb_device(dev);
|
||||
wusbhc = wusbhc_get_by_usb_dev(usb_dev);
|
||||
if (wusbhc == NULL)
|
||||
return -ENODEV;
|
||||
result = wusb_dev_4way_handshake(wusbhc, usb_dev->wusb_dev, &ck);
|
||||
memset(&ck, 0, sizeof(ck));
|
||||
wusbhc_put(wusbhc);
|
||||
return result < 0 ? result : size;
|
||||
}
|
||||
static DEVICE_ATTR(wusb_ck, 0200, NULL, wusb_ck_store);
|
||||
|
||||
static struct attribute *wusb_dev_attrs[] = {
|
||||
&dev_attr_wusb_disconnect.attr,
|
||||
&dev_attr_wusb_cdid.attr,
|
||||
&dev_attr_wusb_ck.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group wusb_dev_attr_group = {
|
||||
.name = NULL, /* we want them in the same directory */
|
||||
.attrs = wusb_dev_attrs,
|
||||
};
|
||||
|
||||
int wusb_dev_sysfs_add(struct wusbhc *wusbhc, struct usb_device *usb_dev,
|
||||
struct wusb_dev *wusb_dev)
|
||||
{
|
||||
int result = sysfs_create_group(&usb_dev->dev.kobj,
|
||||
&wusb_dev_attr_group);
|
||||
struct device *dev = &usb_dev->dev;
|
||||
if (result < 0)
|
||||
dev_err(dev, "Cannot register WUSB-dev attributes: %d\n",
|
||||
result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void wusb_dev_sysfs_rm(struct wusb_dev *wusb_dev)
|
||||
{
|
||||
struct usb_device *usb_dev = wusb_dev->usb_dev;
|
||||
if (usb_dev)
|
||||
sysfs_remove_group(&usb_dev->dev.kobj, &wusb_dev_attr_group);
|
||||
}
|
1100
drivers/usb/wusbcore/devconnect.c
Normal file
1100
drivers/usb/wusbcore/devconnect.c
Normal file
File diff suppressed because it is too large
Load diff
317
drivers/usb/wusbcore/mmc.c
Normal file
317
drivers/usb/wusbcore/mmc.c
Normal file
|
@ -0,0 +1,317 @@
|
|||
/*
|
||||
* WUSB Wire Adapter: Control/Data Streaming Interface (WUSB[8])
|
||||
* MMC (Microscheduled Management Command) handling
|
||||
*
|
||||
* Copyright (C) 2005-2006 Intel Corporation
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* WUIEs and MMC IEs...well, they are almost the same at the end. MMC
|
||||
* IEs are Wireless USB IEs that go into the MMC period...[what is
|
||||
* that? look in Design-overview.txt].
|
||||
*
|
||||
*
|
||||
* This is a simple subsystem to keep track of which IEs are being
|
||||
* sent by the host in the MMC period.
|
||||
*
|
||||
* For each WUIE we ask to send, we keep it in an array, so we can
|
||||
* request its removal later, or replace the content. They are tracked
|
||||
* by pointer, so be sure to use the same pointer if you want to
|
||||
* remove it or update the contents.
|
||||
*
|
||||
* FIXME:
|
||||
* - add timers that autoremove intervalled IEs?
|
||||
*/
|
||||
#include <linux/usb/wusb.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/export.h>
|
||||
#include "wusbhc.h"
|
||||
|
||||
/* Initialize the MMCIEs handling mechanism */
|
||||
int wusbhc_mmcie_create(struct wusbhc *wusbhc)
|
||||
{
|
||||
u8 mmcies = wusbhc->mmcies_max;
|
||||
wusbhc->mmcie = kcalloc(mmcies, sizeof(wusbhc->mmcie[0]), GFP_KERNEL);
|
||||
if (wusbhc->mmcie == NULL)
|
||||
return -ENOMEM;
|
||||
mutex_init(&wusbhc->mmcie_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Release resources used by the MMCIEs handling mechanism */
|
||||
void wusbhc_mmcie_destroy(struct wusbhc *wusbhc)
|
||||
{
|
||||
kfree(wusbhc->mmcie);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add or replace an MMC Wireless USB IE.
|
||||
*
|
||||
* @interval: See WUSB1.0[8.5.3.1]
|
||||
* @repeat_cnt: See WUSB1.0[8.5.3.1]
|
||||
* @handle: See WUSB1.0[8.5.3.1]
|
||||
* @wuie: Pointer to the header of the WUSB IE data to add.
|
||||
* MUST BE allocated in a kmalloc buffer (no stack or
|
||||
* vmalloc).
|
||||
* THE CALLER ALWAYS OWNS THE POINTER (we don't free it
|
||||
* on remove, we just forget about it).
|
||||
* @returns: 0 if ok, < 0 errno code on error.
|
||||
*
|
||||
* Goes over the *whole* @wusbhc->mmcie array looking for (a) the
|
||||
* first free spot and (b) if @wuie is already in the array (aka:
|
||||
* transmitted in the MMCs) the spot were it is.
|
||||
*
|
||||
* If present, we "overwrite it" (update).
|
||||
*
|
||||
*
|
||||
* NOTE: Need special ordering rules -- see below WUSB1.0 Table 7-38.
|
||||
* The host uses the handle as the 'sort' index. We
|
||||
* allocate the last one always for the WUIE_ID_HOST_INFO, and
|
||||
* the rest, first come first serve in inverse order.
|
||||
*
|
||||
* Host software must make sure that it adds the other IEs in
|
||||
* the right order... the host hardware is responsible for
|
||||
* placing the WCTA IEs in the right place with the other IEs
|
||||
* set by host software.
|
||||
*
|
||||
* NOTE: we can access wusbhc->wa_descr without locking because it is
|
||||
* read only.
|
||||
*/
|
||||
int wusbhc_mmcie_set(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt,
|
||||
struct wuie_hdr *wuie)
|
||||
{
|
||||
int result = -ENOBUFS;
|
||||
unsigned handle, itr;
|
||||
|
||||
/* Search a handle, taking into account the ordering */
|
||||
mutex_lock(&wusbhc->mmcie_mutex);
|
||||
switch (wuie->bIEIdentifier) {
|
||||
case WUIE_ID_HOST_INFO:
|
||||
/* Always last */
|
||||
handle = wusbhc->mmcies_max - 1;
|
||||
break;
|
||||
case WUIE_ID_ISOCH_DISCARD:
|
||||
dev_err(wusbhc->dev, "Special ordering case for WUIE ID 0x%x "
|
||||
"unimplemented\n", wuie->bIEIdentifier);
|
||||
result = -ENOSYS;
|
||||
goto error_unlock;
|
||||
default:
|
||||
/* search for it or find the last empty slot */
|
||||
handle = ~0;
|
||||
for (itr = 0; itr < wusbhc->mmcies_max - 1; itr++) {
|
||||
if (wusbhc->mmcie[itr] == wuie) {
|
||||
handle = itr;
|
||||
break;
|
||||
}
|
||||
if (wusbhc->mmcie[itr] == NULL)
|
||||
handle = itr;
|
||||
}
|
||||
if (handle == ~0)
|
||||
goto error_unlock;
|
||||
}
|
||||
result = (wusbhc->mmcie_add)(wusbhc, interval, repeat_cnt, handle,
|
||||
wuie);
|
||||
if (result >= 0)
|
||||
wusbhc->mmcie[handle] = wuie;
|
||||
error_unlock:
|
||||
mutex_unlock(&wusbhc->mmcie_mutex);
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wusbhc_mmcie_set);
|
||||
|
||||
/*
|
||||
* Remove an MMC IE previously added with wusbhc_mmcie_set()
|
||||
*
|
||||
* @wuie Pointer used to add the WUIE
|
||||
*/
|
||||
void wusbhc_mmcie_rm(struct wusbhc *wusbhc, struct wuie_hdr *wuie)
|
||||
{
|
||||
int result;
|
||||
unsigned handle, itr;
|
||||
|
||||
mutex_lock(&wusbhc->mmcie_mutex);
|
||||
for (itr = 0; itr < wusbhc->mmcies_max; itr++) {
|
||||
if (wusbhc->mmcie[itr] == wuie) {
|
||||
handle = itr;
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&wusbhc->mmcie_mutex);
|
||||
return;
|
||||
|
||||
found:
|
||||
result = (wusbhc->mmcie_rm)(wusbhc, handle);
|
||||
if (result == 0)
|
||||
wusbhc->mmcie[itr] = NULL;
|
||||
mutex_unlock(&wusbhc->mmcie_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wusbhc_mmcie_rm);
|
||||
|
||||
static int wusbhc_mmc_start(struct wusbhc *wusbhc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&wusbhc->mutex);
|
||||
ret = wusbhc->start(wusbhc);
|
||||
if (ret >= 0)
|
||||
wusbhc->active = 1;
|
||||
mutex_unlock(&wusbhc->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void wusbhc_mmc_stop(struct wusbhc *wusbhc)
|
||||
{
|
||||
mutex_lock(&wusbhc->mutex);
|
||||
wusbhc->active = 0;
|
||||
wusbhc->stop(wusbhc, WUSB_CHANNEL_STOP_DELAY_MS);
|
||||
mutex_unlock(&wusbhc->mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* wusbhc_start - start transmitting MMCs and accepting connections
|
||||
* @wusbhc: the HC to start
|
||||
*
|
||||
* Establishes a cluster reservation, enables device connections, and
|
||||
* starts MMCs with appropriate DNTS parameters.
|
||||
*/
|
||||
int wusbhc_start(struct wusbhc *wusbhc)
|
||||
{
|
||||
int result;
|
||||
struct device *dev = wusbhc->dev;
|
||||
|
||||
WARN_ON(wusbhc->wuie_host_info != NULL);
|
||||
BUG_ON(wusbhc->uwb_rc == NULL);
|
||||
|
||||
result = wusbhc_rsv_establish(wusbhc);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "cannot establish cluster reservation: %d\n",
|
||||
result);
|
||||
goto error_rsv_establish;
|
||||
}
|
||||
|
||||
result = wusbhc_devconnect_start(wusbhc);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "error enabling device connections: %d\n",
|
||||
result);
|
||||
goto error_devconnect_start;
|
||||
}
|
||||
|
||||
result = wusbhc_sec_start(wusbhc);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "error starting security in the HC: %d\n",
|
||||
result);
|
||||
goto error_sec_start;
|
||||
}
|
||||
|
||||
result = wusbhc->set_num_dnts(wusbhc, wusbhc->dnts_interval,
|
||||
wusbhc->dnts_num_slots);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "Cannot set DNTS parameters: %d\n", result);
|
||||
goto error_set_num_dnts;
|
||||
}
|
||||
result = wusbhc_mmc_start(wusbhc);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "error starting wusbch: %d\n", result);
|
||||
goto error_wusbhc_start;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_wusbhc_start:
|
||||
wusbhc_sec_stop(wusbhc);
|
||||
error_set_num_dnts:
|
||||
error_sec_start:
|
||||
wusbhc_devconnect_stop(wusbhc);
|
||||
error_devconnect_start:
|
||||
wusbhc_rsv_terminate(wusbhc);
|
||||
error_rsv_establish:
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* wusbhc_stop - stop transmitting MMCs
|
||||
* @wusbhc: the HC to stop
|
||||
*
|
||||
* Stops the WUSB channel and removes the cluster reservation.
|
||||
*/
|
||||
void wusbhc_stop(struct wusbhc *wusbhc)
|
||||
{
|
||||
wusbhc_mmc_stop(wusbhc);
|
||||
wusbhc_sec_stop(wusbhc);
|
||||
wusbhc_devconnect_stop(wusbhc);
|
||||
wusbhc_rsv_terminate(wusbhc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set/reset/update a new CHID
|
||||
*
|
||||
* Depending on the previous state of the MMCs, start, stop or change
|
||||
* the sent MMC. This effectively switches the host controller on and
|
||||
* off (radio wise).
|
||||
*/
|
||||
int wusbhc_chid_set(struct wusbhc *wusbhc, const struct wusb_ckhdid *chid)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (memcmp(chid, &wusb_ckhdid_zero, sizeof(*chid)) == 0)
|
||||
chid = NULL;
|
||||
|
||||
mutex_lock(&wusbhc->mutex);
|
||||
if (chid) {
|
||||
if (wusbhc->active) {
|
||||
mutex_unlock(&wusbhc->mutex);
|
||||
return -EBUSY;
|
||||
}
|
||||
wusbhc->chid = *chid;
|
||||
}
|
||||
|
||||
/* register with UWB if we haven't already since we are about to start
|
||||
the radio. */
|
||||
if ((chid) && (wusbhc->uwb_rc == NULL)) {
|
||||
wusbhc->uwb_rc = uwb_rc_get_by_grandpa(wusbhc->dev->parent);
|
||||
if (wusbhc->uwb_rc == NULL) {
|
||||
result = -ENODEV;
|
||||
dev_err(wusbhc->dev,
|
||||
"Cannot get associated UWB Host Controller\n");
|
||||
goto error_rc_get;
|
||||
}
|
||||
|
||||
result = wusbhc_pal_register(wusbhc);
|
||||
if (result < 0) {
|
||||
dev_err(wusbhc->dev, "Cannot register as a UWB PAL\n");
|
||||
goto error_pal_register;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&wusbhc->mutex);
|
||||
|
||||
if (chid)
|
||||
result = uwb_radio_start(&wusbhc->pal);
|
||||
else if (wusbhc->uwb_rc)
|
||||
uwb_radio_stop(&wusbhc->pal);
|
||||
|
||||
return result;
|
||||
|
||||
error_pal_register:
|
||||
uwb_rc_put(wusbhc->uwb_rc);
|
||||
wusbhc->uwb_rc = NULL;
|
||||
error_rc_get:
|
||||
mutex_unlock(&wusbhc->mutex);
|
||||
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wusbhc_chid_set);
|
56
drivers/usb/wusbcore/pal.c
Normal file
56
drivers/usb/wusbcore/pal.c
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Wireless USB Host Controller
|
||||
* UWB Protocol Adaptation Layer (PAL) glue.
|
||||
*
|
||||
* Copyright (C) 2008 Cambridge Silicon Radio Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "wusbhc.h"
|
||||
|
||||
static void wusbhc_channel_changed(struct uwb_pal *pal, int channel)
|
||||
{
|
||||
struct wusbhc *wusbhc = container_of(pal, struct wusbhc, pal);
|
||||
|
||||
dev_dbg(wusbhc->dev, "%s: channel = %d\n", __func__, channel);
|
||||
if (channel < 0)
|
||||
wusbhc_stop(wusbhc);
|
||||
else
|
||||
wusbhc_start(wusbhc);
|
||||
}
|
||||
|
||||
/**
|
||||
* wusbhc_pal_register - register the WUSB HC as a UWB PAL
|
||||
* @wusbhc: the WUSB HC
|
||||
*/
|
||||
int wusbhc_pal_register(struct wusbhc *wusbhc)
|
||||
{
|
||||
uwb_pal_init(&wusbhc->pal);
|
||||
|
||||
wusbhc->pal.name = "wusbhc";
|
||||
wusbhc->pal.device = wusbhc->usb_hcd.self.controller;
|
||||
wusbhc->pal.rc = wusbhc->uwb_rc;
|
||||
wusbhc->pal.channel_changed = wusbhc_channel_changed;
|
||||
|
||||
return uwb_pal_register(&wusbhc->pal);
|
||||
}
|
||||
|
||||
/**
|
||||
* wusbhc_pal_unregister - unregister the WUSB HC as a UWB PAL
|
||||
* @wusbhc: the WUSB HC
|
||||
*/
|
||||
void wusbhc_pal_unregister(struct wusbhc *wusbhc)
|
||||
{
|
||||
if (wusbhc->uwb_rc)
|
||||
uwb_pal_unregister(&wusbhc->pal);
|
||||
}
|
122
drivers/usb/wusbcore/reservation.c
Normal file
122
drivers/usb/wusbcore/reservation.c
Normal file
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* WUSB cluster reservation management
|
||||
*
|
||||
* Copyright (C) 2007 Cambridge Silicon Radio Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/uwb.h>
|
||||
|
||||
#include "wusbhc.h"
|
||||
|
||||
/*
|
||||
* WUSB cluster reservations are multicast reservations with the
|
||||
* broadcast cluster ID (BCID) as the target DevAddr.
|
||||
*
|
||||
* FIXME: consider adjusting the reservation depending on what devices
|
||||
* are attached.
|
||||
*/
|
||||
|
||||
static int wusbhc_bwa_set(struct wusbhc *wusbhc, u8 stream,
|
||||
const struct uwb_mas_bm *mas)
|
||||
{
|
||||
if (mas == NULL)
|
||||
mas = &uwb_mas_bm_zero;
|
||||
return wusbhc->bwa_set(wusbhc, stream, mas);
|
||||
}
|
||||
|
||||
/**
|
||||
* wusbhc_rsv_complete_cb - WUSB HC reservation complete callback
|
||||
* @rsv: the reservation
|
||||
*
|
||||
* Either set or clear the HC's view of the reservation.
|
||||
*
|
||||
* FIXME: when a reservation is denied the HC should be stopped.
|
||||
*/
|
||||
static void wusbhc_rsv_complete_cb(struct uwb_rsv *rsv)
|
||||
{
|
||||
struct wusbhc *wusbhc = rsv->pal_priv;
|
||||
struct device *dev = wusbhc->dev;
|
||||
struct uwb_mas_bm mas;
|
||||
char buf[72];
|
||||
|
||||
dev_dbg(dev, "%s: state = %d\n", __func__, rsv->state);
|
||||
switch (rsv->state) {
|
||||
case UWB_RSV_STATE_O_ESTABLISHED:
|
||||
uwb_rsv_get_usable_mas(rsv, &mas);
|
||||
bitmap_scnprintf(buf, sizeof(buf), mas.bm, UWB_NUM_MAS);
|
||||
dev_dbg(dev, "established reservation: %s\n", buf);
|
||||
wusbhc_bwa_set(wusbhc, rsv->stream, &mas);
|
||||
break;
|
||||
case UWB_RSV_STATE_NONE:
|
||||
dev_dbg(dev, "removed reservation\n");
|
||||
wusbhc_bwa_set(wusbhc, 0, NULL);
|
||||
break;
|
||||
default:
|
||||
dev_dbg(dev, "unexpected reservation state: %d\n", rsv->state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* wusbhc_rsv_establish - establish a reservation for the cluster
|
||||
* @wusbhc: the WUSB HC requesting a bandwidth reservation
|
||||
*/
|
||||
int wusbhc_rsv_establish(struct wusbhc *wusbhc)
|
||||
{
|
||||
struct uwb_rc *rc = wusbhc->uwb_rc;
|
||||
struct uwb_rsv *rsv;
|
||||
struct uwb_dev_addr bcid;
|
||||
int ret;
|
||||
|
||||
if (rc == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
rsv = uwb_rsv_create(rc, wusbhc_rsv_complete_cb, wusbhc);
|
||||
if (rsv == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
bcid.data[0] = wusbhc->cluster_id;
|
||||
bcid.data[1] = 0;
|
||||
|
||||
rsv->target.type = UWB_RSV_TARGET_DEVADDR;
|
||||
rsv->target.devaddr = bcid;
|
||||
rsv->type = UWB_DRP_TYPE_PRIVATE;
|
||||
rsv->max_mas = 256; /* try to get as much as possible */
|
||||
rsv->min_mas = 15; /* one MAS per zone */
|
||||
rsv->max_interval = 1; /* max latency is one zone */
|
||||
rsv->is_multicast = true;
|
||||
|
||||
ret = uwb_rsv_establish(rsv);
|
||||
if (ret == 0)
|
||||
wusbhc->rsv = rsv;
|
||||
else
|
||||
uwb_rsv_destroy(rsv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* wusbhc_rsv_terminate - terminate the cluster reservation
|
||||
* @wusbhc: the WUSB host whose reservation is to be terminated
|
||||
*/
|
||||
void wusbhc_rsv_terminate(struct wusbhc *wusbhc)
|
||||
{
|
||||
if (wusbhc->rsv) {
|
||||
uwb_rsv_terminate(wusbhc->rsv);
|
||||
uwb_rsv_destroy(wusbhc->rsv);
|
||||
wusbhc->rsv = NULL;
|
||||
}
|
||||
}
|
440
drivers/usb/wusbcore/rh.c
Normal file
440
drivers/usb/wusbcore/rh.c
Normal file
|
@ -0,0 +1,440 @@
|
|||
/*
|
||||
* Wireless USB Host Controller
|
||||
* Root Hub operations
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2005-2006 Intel Corporation
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* We fake a root hub that has fake ports (as many as simultaneous
|
||||
* devices the Wireless USB Host Controller can deal with). For each
|
||||
* port we keep an state in @wusbhc->port[index] identical to the one
|
||||
* specified in the USB2.0[ch11] spec and some extra device
|
||||
* information that complements the one in 'struct usb_device' (as
|
||||
* this lacs a hcpriv pointer).
|
||||
*
|
||||
* Note this is common to WHCI and HWA host controllers.
|
||||
*
|
||||
* Through here we enable most of the state changes that the USB stack
|
||||
* will use to connect or disconnect devices. We need to do some
|
||||
* forced adaptation of Wireless USB device states vs. wired:
|
||||
*
|
||||
* USB: WUSB:
|
||||
*
|
||||
* Port Powered-off port slot n/a
|
||||
* Powered-on port slot available
|
||||
* Disconnected port slot available
|
||||
* Connected port slot assigned device
|
||||
* device sent DN_Connect
|
||||
* device was authenticated
|
||||
* Enabled device is authenticated, transitioned
|
||||
* from unauth -> auth -> default address
|
||||
* -> enabled
|
||||
* Reset disconnect
|
||||
* Disable disconnect
|
||||
*
|
||||
* This maps the standard USB port states with the WUSB device states
|
||||
* so we can fake ports without having to modify the USB stack.
|
||||
*
|
||||
* FIXME: this process will change in the future
|
||||
*
|
||||
*
|
||||
* ENTRY POINTS
|
||||
*
|
||||
* Our entry points into here are, as in hcd.c, the USB stack root hub
|
||||
* ops defined in the usb_hcd struct:
|
||||
*
|
||||
* wusbhc_rh_status_data() Provide hub and port status data bitmap
|
||||
*
|
||||
* wusbhc_rh_control() Execution of all the major requests
|
||||
* you can do to a hub (Set|Clear
|
||||
* features, get descriptors, status, etc).
|
||||
*
|
||||
* wusbhc_rh_[suspend|resume]() That
|
||||
*
|
||||
* wusbhc_rh_start_port_reset() ??? unimplemented
|
||||
*/
|
||||
#include <linux/slab.h>
|
||||
#include <linux/export.h>
|
||||
#include "wusbhc.h"
|
||||
|
||||
/*
|
||||
* Reset a fake port
|
||||
*
|
||||
* Using a Reset Device IE is too heavyweight as it causes the device
|
||||
* to enter the UnConnected state and leave the cluster, this can mean
|
||||
* that when the device reconnects it is connected to a different fake
|
||||
* port.
|
||||
*
|
||||
* Instead, reset authenticated devices with a SetAddress(0), followed
|
||||
* by a SetAddresss(AuthAddr).
|
||||
*
|
||||
* For unauthenticated devices just pretend to reset but do nothing.
|
||||
* If the device initialization continues to fail it will eventually
|
||||
* time out after TrustTimeout and enter the UnConnected state.
|
||||
*
|
||||
* @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
|
||||
*
|
||||
* Supposedly we are the only thread accesing @wusbhc->port; in any
|
||||
* case, maybe we should move the mutex locking from
|
||||
* wusbhc_devconnect_auth() to here.
|
||||
*
|
||||
* @port_idx refers to the wusbhc's port index, not the USB port number
|
||||
*/
|
||||
static int wusbhc_rh_port_reset(struct wusbhc *wusbhc, u8 port_idx)
|
||||
{
|
||||
int result = 0;
|
||||
struct wusb_port *port = wusb_port_by_idx(wusbhc, port_idx);
|
||||
struct wusb_dev *wusb_dev = port->wusb_dev;
|
||||
|
||||
if (wusb_dev == NULL)
|
||||
return -ENOTCONN;
|
||||
|
||||
port->status |= USB_PORT_STAT_RESET;
|
||||
port->change |= USB_PORT_STAT_C_RESET;
|
||||
|
||||
if (wusb_dev->addr & WUSB_DEV_ADDR_UNAUTH)
|
||||
result = 0;
|
||||
else
|
||||
result = wusb_dev_update_address(wusbhc, wusb_dev);
|
||||
|
||||
port->status &= ~USB_PORT_STAT_RESET;
|
||||
port->status |= USB_PORT_STAT_ENABLE;
|
||||
port->change |= USB_PORT_STAT_C_RESET | USB_PORT_STAT_C_ENABLE;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the hub change status bitmap
|
||||
*
|
||||
* The bits in the change status bitmap are cleared when a
|
||||
* ClearPortFeature request is issued (USB2.0[11.12.3,11.12.4].
|
||||
*
|
||||
* @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
|
||||
*
|
||||
* WARNING!! This gets called from atomic context; we cannot get the
|
||||
* mutex--the only race condition we can find is some bit
|
||||
* changing just after we copy it, which shouldn't be too
|
||||
* big of a problem [and we can't make it an spinlock
|
||||
* because other parts need to take it and sleep] .
|
||||
*
|
||||
* @usb_hcd is refcounted, so it won't disappear under us
|
||||
* and before killing a host, the polling of the root hub
|
||||
* would be stopped anyway.
|
||||
*/
|
||||
int wusbhc_rh_status_data(struct usb_hcd *usb_hcd, char *_buf)
|
||||
{
|
||||
struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
|
||||
size_t cnt, size, bits_set = 0;
|
||||
|
||||
/* WE DON'T LOCK, see comment */
|
||||
/* round up to bytes. Hub bit is bit 0 so add 1. */
|
||||
size = DIV_ROUND_UP(wusbhc->ports_max + 1, 8);
|
||||
|
||||
/* clear the output buffer. */
|
||||
memset(_buf, 0, size);
|
||||
/* set the bit for each changed port. */
|
||||
for (cnt = 0; cnt < wusbhc->ports_max; cnt++) {
|
||||
|
||||
if (wusb_port_by_idx(wusbhc, cnt)->change) {
|
||||
const int bitpos = cnt+1;
|
||||
|
||||
_buf[bitpos/8] |= (1 << (bitpos % 8));
|
||||
bits_set++;
|
||||
}
|
||||
}
|
||||
|
||||
return bits_set ? size : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wusbhc_rh_status_data);
|
||||
|
||||
/*
|
||||
* Return the hub's descriptor
|
||||
*
|
||||
* NOTE: almost cut and paste from ehci-hub.c
|
||||
*
|
||||
* @wusbhc is assumed referenced and @wusbhc->mutex unlocked
|
||||
*/
|
||||
static int wusbhc_rh_get_hub_descr(struct wusbhc *wusbhc, u16 wValue,
|
||||
u16 wIndex,
|
||||
struct usb_hub_descriptor *descr,
|
||||
u16 wLength)
|
||||
{
|
||||
u16 temp = 1 + (wusbhc->ports_max / 8);
|
||||
u8 length = 7 + 2 * temp;
|
||||
|
||||
if (wLength < length)
|
||||
return -ENOSPC;
|
||||
descr->bDescLength = 7 + 2 * temp;
|
||||
descr->bDescriptorType = 0x29; /* HUB type */
|
||||
descr->bNbrPorts = wusbhc->ports_max;
|
||||
descr->wHubCharacteristics = cpu_to_le16(
|
||||
0x00 /* All ports power at once */
|
||||
| 0x00 /* not part of compound device */
|
||||
| 0x10 /* No overcurrent protection */
|
||||
| 0x00 /* 8 FS think time FIXME ?? */
|
||||
| 0x00); /* No port indicators */
|
||||
descr->bPwrOn2PwrGood = 0;
|
||||
descr->bHubContrCurrent = 0;
|
||||
/* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */
|
||||
memset(&descr->u.hs.DeviceRemovable[0], 0, temp);
|
||||
memset(&descr->u.hs.DeviceRemovable[temp], 0xff, temp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear a hub feature
|
||||
*
|
||||
* @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
|
||||
*
|
||||
* Nothing to do, so no locking needed ;)
|
||||
*/
|
||||
static int wusbhc_rh_clear_hub_feat(struct wusbhc *wusbhc, u16 feature)
|
||||
{
|
||||
int result;
|
||||
|
||||
switch (feature) {
|
||||
case C_HUB_LOCAL_POWER:
|
||||
/* FIXME: maybe plug bit 0 to the power input status,
|
||||
* if any?
|
||||
* see wusbhc_rh_get_hub_status() */
|
||||
case C_HUB_OVER_CURRENT:
|
||||
result = 0;
|
||||
break;
|
||||
default:
|
||||
result = -EPIPE;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return hub status (it is always zero...)
|
||||
*
|
||||
* @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
|
||||
*
|
||||
* Nothing to do, so no locking needed ;)
|
||||
*/
|
||||
static int wusbhc_rh_get_hub_status(struct wusbhc *wusbhc, u32 *buf,
|
||||
u16 wLength)
|
||||
{
|
||||
/* FIXME: maybe plug bit 0 to the power input status (if any)? */
|
||||
*buf = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set a port feature
|
||||
*
|
||||
* @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
|
||||
*/
|
||||
static int wusbhc_rh_set_port_feat(struct wusbhc *wusbhc, u16 feature,
|
||||
u8 selector, u8 port_idx)
|
||||
{
|
||||
struct device *dev = wusbhc->dev;
|
||||
|
||||
if (port_idx > wusbhc->ports_max)
|
||||
return -EINVAL;
|
||||
|
||||
switch (feature) {
|
||||
/* According to USB2.0[11.24.2.13]p2, these features
|
||||
* are not required to be implemented. */
|
||||
case USB_PORT_FEAT_C_OVER_CURRENT:
|
||||
case USB_PORT_FEAT_C_ENABLE:
|
||||
case USB_PORT_FEAT_C_SUSPEND:
|
||||
case USB_PORT_FEAT_C_CONNECTION:
|
||||
case USB_PORT_FEAT_C_RESET:
|
||||
return 0;
|
||||
case USB_PORT_FEAT_POWER:
|
||||
/* No such thing, but we fake it works */
|
||||
mutex_lock(&wusbhc->mutex);
|
||||
wusb_port_by_idx(wusbhc, port_idx)->status |= USB_PORT_STAT_POWER;
|
||||
mutex_unlock(&wusbhc->mutex);
|
||||
return 0;
|
||||
case USB_PORT_FEAT_RESET:
|
||||
return wusbhc_rh_port_reset(wusbhc, port_idx);
|
||||
case USB_PORT_FEAT_ENABLE:
|
||||
case USB_PORT_FEAT_SUSPEND:
|
||||
dev_err(dev, "(port_idx %d) set feat %d/%d UNIMPLEMENTED\n",
|
||||
port_idx, feature, selector);
|
||||
return -ENOSYS;
|
||||
default:
|
||||
dev_err(dev, "(port_idx %d) set feat %d/%d UNKNOWN\n",
|
||||
port_idx, feature, selector);
|
||||
return -EPIPE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear a port feature...
|
||||
*
|
||||
* @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
|
||||
*/
|
||||
static int wusbhc_rh_clear_port_feat(struct wusbhc *wusbhc, u16 feature,
|
||||
u8 selector, u8 port_idx)
|
||||
{
|
||||
int result = 0;
|
||||
struct device *dev = wusbhc->dev;
|
||||
|
||||
if (port_idx > wusbhc->ports_max)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&wusbhc->mutex);
|
||||
switch (feature) {
|
||||
case USB_PORT_FEAT_POWER: /* fake port always on */
|
||||
/* According to USB2.0[11.24.2.7.1.4], no need to implement? */
|
||||
case USB_PORT_FEAT_C_OVER_CURRENT:
|
||||
break;
|
||||
case USB_PORT_FEAT_C_RESET:
|
||||
wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_RESET;
|
||||
break;
|
||||
case USB_PORT_FEAT_C_CONNECTION:
|
||||
wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_CONNECTION;
|
||||
break;
|
||||
case USB_PORT_FEAT_ENABLE:
|
||||
__wusbhc_dev_disable(wusbhc, port_idx);
|
||||
break;
|
||||
case USB_PORT_FEAT_C_ENABLE:
|
||||
wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_ENABLE;
|
||||
break;
|
||||
case USB_PORT_FEAT_SUSPEND:
|
||||
case USB_PORT_FEAT_C_SUSPEND:
|
||||
dev_err(dev, "(port_idx %d) Clear feat %d/%d UNIMPLEMENTED\n",
|
||||
port_idx, feature, selector);
|
||||
result = -ENOSYS;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "(port_idx %d) Clear feat %d/%d UNKNOWN\n",
|
||||
port_idx, feature, selector);
|
||||
result = -EPIPE;
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&wusbhc->mutex);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the port's status
|
||||
*
|
||||
* @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
|
||||
*/
|
||||
static int wusbhc_rh_get_port_status(struct wusbhc *wusbhc, u16 port_idx,
|
||||
u32 *_buf, u16 wLength)
|
||||
{
|
||||
__le16 *buf = (__le16 *)_buf;
|
||||
|
||||
if (port_idx > wusbhc->ports_max)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&wusbhc->mutex);
|
||||
buf[0] = cpu_to_le16(wusb_port_by_idx(wusbhc, port_idx)->status);
|
||||
buf[1] = cpu_to_le16(wusb_port_by_idx(wusbhc, port_idx)->change);
|
||||
mutex_unlock(&wusbhc->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Entry point for Root Hub operations
|
||||
*
|
||||
* @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
|
||||
*/
|
||||
int wusbhc_rh_control(struct usb_hcd *usb_hcd, u16 reqntype, u16 wValue,
|
||||
u16 wIndex, char *buf, u16 wLength)
|
||||
{
|
||||
int result = -ENOSYS;
|
||||
struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
|
||||
|
||||
switch (reqntype) {
|
||||
case GetHubDescriptor:
|
||||
result = wusbhc_rh_get_hub_descr(
|
||||
wusbhc, wValue, wIndex,
|
||||
(struct usb_hub_descriptor *) buf, wLength);
|
||||
break;
|
||||
case ClearHubFeature:
|
||||
result = wusbhc_rh_clear_hub_feat(wusbhc, wValue);
|
||||
break;
|
||||
case GetHubStatus:
|
||||
result = wusbhc_rh_get_hub_status(wusbhc, (u32 *)buf, wLength);
|
||||
break;
|
||||
|
||||
case SetPortFeature:
|
||||
result = wusbhc_rh_set_port_feat(wusbhc, wValue, wIndex >> 8,
|
||||
(wIndex & 0xff) - 1);
|
||||
break;
|
||||
case ClearPortFeature:
|
||||
result = wusbhc_rh_clear_port_feat(wusbhc, wValue, wIndex >> 8,
|
||||
(wIndex & 0xff) - 1);
|
||||
break;
|
||||
case GetPortStatus:
|
||||
result = wusbhc_rh_get_port_status(wusbhc, wIndex - 1,
|
||||
(u32 *)buf, wLength);
|
||||
break;
|
||||
|
||||
case SetHubFeature:
|
||||
default:
|
||||
dev_err(wusbhc->dev, "%s (%p [%p], %x, %x, %x, %p, %x) "
|
||||
"UNIMPLEMENTED\n", __func__, usb_hcd, wusbhc, reqntype,
|
||||
wValue, wIndex, buf, wLength);
|
||||
/* dump_stack(); */
|
||||
result = -ENOSYS;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wusbhc_rh_control);
|
||||
|
||||
int wusbhc_rh_start_port_reset(struct usb_hcd *usb_hcd, unsigned port_idx)
|
||||
{
|
||||
struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
|
||||
dev_err(wusbhc->dev, "%s (%p [%p], port_idx %u) UNIMPLEMENTED\n",
|
||||
__func__, usb_hcd, wusbhc, port_idx);
|
||||
WARN_ON(1);
|
||||
return -ENOSYS;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wusbhc_rh_start_port_reset);
|
||||
|
||||
static void wusb_port_init(struct wusb_port *port)
|
||||
{
|
||||
port->status |= USB_PORT_STAT_HIGH_SPEED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Alloc fake port specific fields and status.
|
||||
*/
|
||||
int wusbhc_rh_create(struct wusbhc *wusbhc)
|
||||
{
|
||||
int result = -ENOMEM;
|
||||
size_t port_size, itr;
|
||||
port_size = wusbhc->ports_max * sizeof(wusbhc->port[0]);
|
||||
wusbhc->port = kzalloc(port_size, GFP_KERNEL);
|
||||
if (wusbhc->port == NULL)
|
||||
goto error_port_alloc;
|
||||
for (itr = 0; itr < wusbhc->ports_max; itr++)
|
||||
wusb_port_init(&wusbhc->port[itr]);
|
||||
result = 0;
|
||||
error_port_alloc:
|
||||
return result;
|
||||
}
|
||||
|
||||
void wusbhc_rh_destroy(struct wusbhc *wusbhc)
|
||||
{
|
||||
kfree(wusbhc->port);
|
||||
}
|
615
drivers/usb/wusbcore/security.c
Normal file
615
drivers/usb/wusbcore/security.c
Normal file
|
@ -0,0 +1,615 @@
|
|||
/*
|
||||
* Wireless USB Host Controller
|
||||
* Security support: encryption enablement, etc
|
||||
*
|
||||
* Copyright (C) 2006 Intel Corporation
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* FIXME: docs
|
||||
*/
|
||||
#include <linux/types.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/export.h>
|
||||
#include "wusbhc.h"
|
||||
|
||||
static void wusbhc_gtk_rekey_work(struct work_struct *work);
|
||||
|
||||
int wusbhc_sec_create(struct wusbhc *wusbhc)
|
||||
{
|
||||
/*
|
||||
* WQ is singlethread because we need to serialize rekey operations.
|
||||
* Use a separate workqueue for security operations instead of the
|
||||
* wusbd workqueue because security operations may need to communicate
|
||||
* directly with downstream wireless devices using synchronous URBs.
|
||||
* If a device is not responding, this could block other host
|
||||
* controller operations.
|
||||
*/
|
||||
wusbhc->wq_security = create_singlethread_workqueue("wusbd_security");
|
||||
if (wusbhc->wq_security == NULL) {
|
||||
pr_err("WUSB-core: Cannot create wusbd_security workqueue\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
wusbhc->gtk.descr.bLength = sizeof(wusbhc->gtk.descr) +
|
||||
sizeof(wusbhc->gtk.data);
|
||||
wusbhc->gtk.descr.bDescriptorType = USB_DT_KEY;
|
||||
wusbhc->gtk.descr.bReserved = 0;
|
||||
wusbhc->gtk_index = 0;
|
||||
|
||||
INIT_WORK(&wusbhc->gtk_rekey_work, wusbhc_gtk_rekey_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Called when the HC is destroyed */
|
||||
void wusbhc_sec_destroy(struct wusbhc *wusbhc)
|
||||
{
|
||||
destroy_workqueue(wusbhc->wq_security);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* wusbhc_next_tkid - generate a new, currently unused, TKID
|
||||
* @wusbhc: the WUSB host controller
|
||||
* @wusb_dev: the device whose PTK the TKID is for
|
||||
* (or NULL for a TKID for a GTK)
|
||||
*
|
||||
* The generated TKID consists of two parts: the device's authenticated
|
||||
* address (or 0 or a GTK); and an incrementing number. This ensures
|
||||
* that TKIDs cannot be shared between devices and by the time the
|
||||
* incrementing number wraps around the older TKIDs will no longer be
|
||||
* in use (a maximum of two keys may be active at any one time).
|
||||
*/
|
||||
static u32 wusbhc_next_tkid(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev)
|
||||
{
|
||||
u32 *tkid;
|
||||
u32 addr;
|
||||
|
||||
if (wusb_dev == NULL) {
|
||||
tkid = &wusbhc->gtk_tkid;
|
||||
addr = 0;
|
||||
} else {
|
||||
tkid = &wusb_port_by_idx(wusbhc, wusb_dev->port_idx)->ptk_tkid;
|
||||
addr = wusb_dev->addr & 0x7f;
|
||||
}
|
||||
|
||||
*tkid = (addr << 8) | ((*tkid + 1) & 0xff);
|
||||
|
||||
return *tkid;
|
||||
}
|
||||
|
||||
static void wusbhc_generate_gtk(struct wusbhc *wusbhc)
|
||||
{
|
||||
const size_t key_size = sizeof(wusbhc->gtk.data);
|
||||
u32 tkid;
|
||||
|
||||
tkid = wusbhc_next_tkid(wusbhc, NULL);
|
||||
|
||||
wusbhc->gtk.descr.tTKID[0] = (tkid >> 0) & 0xff;
|
||||
wusbhc->gtk.descr.tTKID[1] = (tkid >> 8) & 0xff;
|
||||
wusbhc->gtk.descr.tTKID[2] = (tkid >> 16) & 0xff;
|
||||
|
||||
get_random_bytes(wusbhc->gtk.descr.bKeyData, key_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* wusbhc_sec_start - start the security management process
|
||||
* @wusbhc: the WUSB host controller
|
||||
*
|
||||
* Generate and set an initial GTK on the host controller.
|
||||
*
|
||||
* Called when the HC is started.
|
||||
*/
|
||||
int wusbhc_sec_start(struct wusbhc *wusbhc)
|
||||
{
|
||||
const size_t key_size = sizeof(wusbhc->gtk.data);
|
||||
int result;
|
||||
|
||||
wusbhc_generate_gtk(wusbhc);
|
||||
|
||||
result = wusbhc->set_gtk(wusbhc, wusbhc->gtk_tkid,
|
||||
&wusbhc->gtk.descr.bKeyData, key_size);
|
||||
if (result < 0)
|
||||
dev_err(wusbhc->dev, "cannot set GTK for the host: %d\n",
|
||||
result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* wusbhc_sec_stop - stop the security management process
|
||||
* @wusbhc: the WUSB host controller
|
||||
*
|
||||
* Wait for any pending GTK rekeys to stop.
|
||||
*/
|
||||
void wusbhc_sec_stop(struct wusbhc *wusbhc)
|
||||
{
|
||||
cancel_work_sync(&wusbhc->gtk_rekey_work);
|
||||
}
|
||||
|
||||
|
||||
/** @returns encryption type name */
|
||||
const char *wusb_et_name(u8 x)
|
||||
{
|
||||
switch (x) {
|
||||
case USB_ENC_TYPE_UNSECURE: return "unsecure";
|
||||
case USB_ENC_TYPE_WIRED: return "wired";
|
||||
case USB_ENC_TYPE_CCM_1: return "CCM-1";
|
||||
case USB_ENC_TYPE_RSA_1: return "RSA-1";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wusb_et_name);
|
||||
|
||||
/*
|
||||
* Set the device encryption method
|
||||
*
|
||||
* We tell the device which encryption method to use; we do this when
|
||||
* setting up the device's security.
|
||||
*/
|
||||
static int wusb_dev_set_encryption(struct usb_device *usb_dev, int value)
|
||||
{
|
||||
int result;
|
||||
struct device *dev = &usb_dev->dev;
|
||||
struct wusb_dev *wusb_dev = usb_dev->wusb_dev;
|
||||
|
||||
if (value) {
|
||||
value = wusb_dev->ccm1_etd.bEncryptionValue;
|
||||
} else {
|
||||
/* FIXME: should be wusb_dev->etd[UNSECURE].bEncryptionValue */
|
||||
value = 0;
|
||||
}
|
||||
/* Set device's */
|
||||
result = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
USB_REQ_SET_ENCRYPTION,
|
||||
USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
|
||||
value, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
|
||||
if (result < 0)
|
||||
dev_err(dev, "Can't set device's WUSB encryption to "
|
||||
"%s (value %d): %d\n",
|
||||
wusb_et_name(wusb_dev->ccm1_etd.bEncryptionType),
|
||||
wusb_dev->ccm1_etd.bEncryptionValue, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the GTK to be used by a device.
|
||||
*
|
||||
* The device must be authenticated.
|
||||
*/
|
||||
static int wusb_dev_set_gtk(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev)
|
||||
{
|
||||
struct usb_device *usb_dev = wusb_dev->usb_dev;
|
||||
u8 key_index = wusb_key_index(wusbhc->gtk_index,
|
||||
WUSB_KEY_INDEX_TYPE_GTK, WUSB_KEY_INDEX_ORIGINATOR_HOST);
|
||||
|
||||
return usb_control_msg(
|
||||
usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
USB_REQ_SET_DESCRIPTOR,
|
||||
USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
|
||||
USB_DT_KEY << 8 | key_index, 0,
|
||||
&wusbhc->gtk.descr, wusbhc->gtk.descr.bLength,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
}
|
||||
|
||||
|
||||
/* FIXME: prototype for adding security */
|
||||
int wusb_dev_sec_add(struct wusbhc *wusbhc,
|
||||
struct usb_device *usb_dev, struct wusb_dev *wusb_dev)
|
||||
{
|
||||
int result, bytes, secd_size;
|
||||
struct device *dev = &usb_dev->dev;
|
||||
struct usb_security_descriptor *secd, *new_secd;
|
||||
const struct usb_encryption_descriptor *etd, *ccm1_etd = NULL;
|
||||
const void *itr, *top;
|
||||
char buf[64];
|
||||
|
||||
secd = kmalloc(sizeof(*secd), GFP_KERNEL);
|
||||
if (secd == NULL) {
|
||||
result = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
result = usb_get_descriptor(usb_dev, USB_DT_SECURITY,
|
||||
0, secd, sizeof(*secd));
|
||||
if (result < sizeof(*secd)) {
|
||||
dev_err(dev, "Can't read security descriptor or "
|
||||
"not enough data: %d\n", result);
|
||||
goto out;
|
||||
}
|
||||
secd_size = le16_to_cpu(secd->wTotalLength);
|
||||
new_secd = krealloc(secd, secd_size, GFP_KERNEL);
|
||||
if (new_secd == NULL) {
|
||||
dev_err(dev,
|
||||
"Can't allocate space for security descriptors\n");
|
||||
goto out;
|
||||
}
|
||||
secd = new_secd;
|
||||
result = usb_get_descriptor(usb_dev, USB_DT_SECURITY,
|
||||
0, secd, secd_size);
|
||||
if (result < secd_size) {
|
||||
dev_err(dev, "Can't read security descriptor or "
|
||||
"not enough data: %d\n", result);
|
||||
goto out;
|
||||
}
|
||||
bytes = 0;
|
||||
itr = &secd[1];
|
||||
top = (void *)secd + result;
|
||||
while (itr < top) {
|
||||
etd = itr;
|
||||
if (top - itr < sizeof(*etd)) {
|
||||
dev_err(dev, "BUG: bad device security descriptor; "
|
||||
"not enough data (%zu vs %zu bytes left)\n",
|
||||
top - itr, sizeof(*etd));
|
||||
break;
|
||||
}
|
||||
if (etd->bLength < sizeof(*etd)) {
|
||||
dev_err(dev, "BUG: bad device encryption descriptor; "
|
||||
"descriptor is too short "
|
||||
"(%u vs %zu needed)\n",
|
||||
etd->bLength, sizeof(*etd));
|
||||
break;
|
||||
}
|
||||
itr += etd->bLength;
|
||||
bytes += snprintf(buf + bytes, sizeof(buf) - bytes,
|
||||
"%s (0x%02x/%02x) ",
|
||||
wusb_et_name(etd->bEncryptionType),
|
||||
etd->bEncryptionValue, etd->bAuthKeyIndex);
|
||||
if (etd->bEncryptionType == USB_ENC_TYPE_CCM_1)
|
||||
ccm1_etd = etd;
|
||||
}
|
||||
/* This code only supports CCM1 as of now. */
|
||||
/* FIXME: user has to choose which sec mode to use?
|
||||
* In theory we want CCM */
|
||||
if (ccm1_etd == NULL) {
|
||||
dev_err(dev, "WUSB device doesn't support CCM1 encryption, "
|
||||
"can't use!\n");
|
||||
result = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
wusb_dev->ccm1_etd = *ccm1_etd;
|
||||
dev_dbg(dev, "supported encryption: %s; using %s (0x%02x/%02x)\n",
|
||||
buf, wusb_et_name(ccm1_etd->bEncryptionType),
|
||||
ccm1_etd->bEncryptionValue, ccm1_etd->bAuthKeyIndex);
|
||||
result = 0;
|
||||
out:
|
||||
kfree(secd);
|
||||
return result;
|
||||
}
|
||||
|
||||
void wusb_dev_sec_rm(struct wusb_dev *wusb_dev)
|
||||
{
|
||||
/* Nothing so far */
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the address of an unauthenticated WUSB device
|
||||
*
|
||||
* Once we have successfully authenticated, we take it to addr0 state
|
||||
* and then to a normal address.
|
||||
*
|
||||
* Before the device's address (as known by it) was usb_dev->devnum |
|
||||
* 0x80 (unauthenticated address). With this we update it to usb_dev->devnum.
|
||||
*/
|
||||
int wusb_dev_update_address(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev)
|
||||
{
|
||||
int result = -ENOMEM;
|
||||
struct usb_device *usb_dev = wusb_dev->usb_dev;
|
||||
struct device *dev = &usb_dev->dev;
|
||||
u8 new_address = wusb_dev->addr & 0x7F;
|
||||
|
||||
/* Set address 0 */
|
||||
result = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
USB_REQ_SET_ADDRESS,
|
||||
USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
|
||||
0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "auth failed: can't set address 0: %d\n",
|
||||
result);
|
||||
goto error_addr0;
|
||||
}
|
||||
result = wusb_set_dev_addr(wusbhc, wusb_dev, 0);
|
||||
if (result < 0)
|
||||
goto error_addr0;
|
||||
usb_set_device_state(usb_dev, USB_STATE_DEFAULT);
|
||||
usb_ep0_reinit(usb_dev);
|
||||
|
||||
/* Set new (authenticated) address. */
|
||||
result = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
USB_REQ_SET_ADDRESS,
|
||||
USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
|
||||
new_address, 0, NULL, 0,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "auth failed: can't set address %u: %d\n",
|
||||
new_address, result);
|
||||
goto error_addr;
|
||||
}
|
||||
result = wusb_set_dev_addr(wusbhc, wusb_dev, new_address);
|
||||
if (result < 0)
|
||||
goto error_addr;
|
||||
usb_set_device_state(usb_dev, USB_STATE_ADDRESS);
|
||||
usb_ep0_reinit(usb_dev);
|
||||
usb_dev->authenticated = 1;
|
||||
error_addr:
|
||||
error_addr0:
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*
|
||||
*/
|
||||
/* FIXME: split and cleanup */
|
||||
int wusb_dev_4way_handshake(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev,
|
||||
struct wusb_ckhdid *ck)
|
||||
{
|
||||
int result = -ENOMEM;
|
||||
struct usb_device *usb_dev = wusb_dev->usb_dev;
|
||||
struct device *dev = &usb_dev->dev;
|
||||
u32 tkid;
|
||||
__le32 tkid_le;
|
||||
struct usb_handshake *hs;
|
||||
struct aes_ccm_nonce ccm_n;
|
||||
u8 mic[8];
|
||||
struct wusb_keydvt_in keydvt_in;
|
||||
struct wusb_keydvt_out keydvt_out;
|
||||
|
||||
hs = kcalloc(3, sizeof(hs[0]), GFP_KERNEL);
|
||||
if (hs == NULL) {
|
||||
dev_err(dev, "can't allocate handshake data\n");
|
||||
goto error_kzalloc;
|
||||
}
|
||||
|
||||
/* We need to turn encryption before beginning the 4way
|
||||
* hshake (WUSB1.0[.3.2.2]) */
|
||||
result = wusb_dev_set_encryption(usb_dev, 1);
|
||||
if (result < 0)
|
||||
goto error_dev_set_encryption;
|
||||
|
||||
tkid = wusbhc_next_tkid(wusbhc, wusb_dev);
|
||||
tkid_le = cpu_to_le32(tkid);
|
||||
|
||||
hs[0].bMessageNumber = 1;
|
||||
hs[0].bStatus = 0;
|
||||
memcpy(hs[0].tTKID, &tkid_le, sizeof(hs[0].tTKID));
|
||||
hs[0].bReserved = 0;
|
||||
memcpy(hs[0].CDID, &wusb_dev->cdid, sizeof(hs[0].CDID));
|
||||
get_random_bytes(&hs[0].nonce, sizeof(hs[0].nonce));
|
||||
memset(hs[0].MIC, 0, sizeof(hs[0].MIC)); /* Per WUSB1.0[T7-22] */
|
||||
|
||||
result = usb_control_msg(
|
||||
usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
USB_REQ_SET_HANDSHAKE,
|
||||
USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
|
||||
1, 0, &hs[0], sizeof(hs[0]), USB_CTRL_SET_TIMEOUT);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "Handshake1: request failed: %d\n", result);
|
||||
goto error_hs1;
|
||||
}
|
||||
|
||||
/* Handshake 2, from the device -- need to verify fields */
|
||||
result = usb_control_msg(
|
||||
usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
USB_REQ_GET_HANDSHAKE,
|
||||
USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
|
||||
2, 0, &hs[1], sizeof(hs[1]), USB_CTRL_GET_TIMEOUT);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "Handshake2: request failed: %d\n", result);
|
||||
goto error_hs2;
|
||||
}
|
||||
|
||||
result = -EINVAL;
|
||||
if (hs[1].bMessageNumber != 2) {
|
||||
dev_err(dev, "Handshake2 failed: bad message number %u\n",
|
||||
hs[1].bMessageNumber);
|
||||
goto error_hs2;
|
||||
}
|
||||
if (hs[1].bStatus != 0) {
|
||||
dev_err(dev, "Handshake2 failed: bad status %u\n",
|
||||
hs[1].bStatus);
|
||||
goto error_hs2;
|
||||
}
|
||||
if (memcmp(hs[0].tTKID, hs[1].tTKID, sizeof(hs[0].tTKID))) {
|
||||
dev_err(dev, "Handshake2 failed: TKID mismatch "
|
||||
"(#1 0x%02x%02x%02x vs #2 0x%02x%02x%02x)\n",
|
||||
hs[0].tTKID[0], hs[0].tTKID[1], hs[0].tTKID[2],
|
||||
hs[1].tTKID[0], hs[1].tTKID[1], hs[1].tTKID[2]);
|
||||
goto error_hs2;
|
||||
}
|
||||
if (memcmp(hs[0].CDID, hs[1].CDID, sizeof(hs[0].CDID))) {
|
||||
dev_err(dev, "Handshake2 failed: CDID mismatch\n");
|
||||
goto error_hs2;
|
||||
}
|
||||
|
||||
/* Setup the CCM nonce */
|
||||
memset(&ccm_n.sfn, 0, sizeof(ccm_n.sfn)); /* Per WUSB1.0[6.5.2] */
|
||||
memcpy(ccm_n.tkid, &tkid_le, sizeof(ccm_n.tkid));
|
||||
ccm_n.src_addr = wusbhc->uwb_rc->uwb_dev.dev_addr;
|
||||
ccm_n.dest_addr.data[0] = wusb_dev->addr;
|
||||
ccm_n.dest_addr.data[1] = 0;
|
||||
|
||||
/* Derive the KCK and PTK from CK, the CCM, H and D nonces */
|
||||
memcpy(keydvt_in.hnonce, hs[0].nonce, sizeof(keydvt_in.hnonce));
|
||||
memcpy(keydvt_in.dnonce, hs[1].nonce, sizeof(keydvt_in.dnonce));
|
||||
result = wusb_key_derive(&keydvt_out, ck->data, &ccm_n, &keydvt_in);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "Handshake2 failed: cannot derive keys: %d\n",
|
||||
result);
|
||||
goto error_hs2;
|
||||
}
|
||||
|
||||
/* Compute MIC and verify it */
|
||||
result = wusb_oob_mic(mic, keydvt_out.kck, &ccm_n, &hs[1]);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "Handshake2 failed: cannot compute MIC: %d\n",
|
||||
result);
|
||||
goto error_hs2;
|
||||
}
|
||||
|
||||
if (memcmp(hs[1].MIC, mic, sizeof(hs[1].MIC))) {
|
||||
dev_err(dev, "Handshake2 failed: MIC mismatch\n");
|
||||
goto error_hs2;
|
||||
}
|
||||
|
||||
/* Send Handshake3 */
|
||||
hs[2].bMessageNumber = 3;
|
||||
hs[2].bStatus = 0;
|
||||
memcpy(hs[2].tTKID, &tkid_le, sizeof(hs[2].tTKID));
|
||||
hs[2].bReserved = 0;
|
||||
memcpy(hs[2].CDID, &wusb_dev->cdid, sizeof(hs[2].CDID));
|
||||
memcpy(hs[2].nonce, hs[0].nonce, sizeof(hs[2].nonce));
|
||||
result = wusb_oob_mic(hs[2].MIC, keydvt_out.kck, &ccm_n, &hs[2]);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "Handshake3 failed: cannot compute MIC: %d\n",
|
||||
result);
|
||||
goto error_hs2;
|
||||
}
|
||||
|
||||
result = usb_control_msg(
|
||||
usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
USB_REQ_SET_HANDSHAKE,
|
||||
USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
|
||||
3, 0, &hs[2], sizeof(hs[2]), USB_CTRL_SET_TIMEOUT);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "Handshake3: request failed: %d\n", result);
|
||||
goto error_hs3;
|
||||
}
|
||||
|
||||
result = wusbhc->set_ptk(wusbhc, wusb_dev->port_idx, tkid,
|
||||
keydvt_out.ptk, sizeof(keydvt_out.ptk));
|
||||
if (result < 0)
|
||||
goto error_wusbhc_set_ptk;
|
||||
|
||||
result = wusb_dev_set_gtk(wusbhc, wusb_dev);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "Set GTK for device: request failed: %d\n",
|
||||
result);
|
||||
goto error_wusbhc_set_gtk;
|
||||
}
|
||||
|
||||
/* Update the device's address from unauth to auth */
|
||||
if (usb_dev->authenticated == 0) {
|
||||
result = wusb_dev_update_address(wusbhc, wusb_dev);
|
||||
if (result < 0)
|
||||
goto error_dev_update_address;
|
||||
}
|
||||
result = 0;
|
||||
dev_info(dev, "device authenticated\n");
|
||||
|
||||
error_dev_update_address:
|
||||
error_wusbhc_set_gtk:
|
||||
error_wusbhc_set_ptk:
|
||||
error_hs3:
|
||||
error_hs2:
|
||||
error_hs1:
|
||||
memset(hs, 0, 3*sizeof(hs[0]));
|
||||
memset(&keydvt_out, 0, sizeof(keydvt_out));
|
||||
memset(&keydvt_in, 0, sizeof(keydvt_in));
|
||||
memset(&ccm_n, 0, sizeof(ccm_n));
|
||||
memset(mic, 0, sizeof(mic));
|
||||
if (result < 0)
|
||||
wusb_dev_set_encryption(usb_dev, 0);
|
||||
error_dev_set_encryption:
|
||||
kfree(hs);
|
||||
error_kzalloc:
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Once all connected and authenticated devices have received the new
|
||||
* GTK, switch the host to using it.
|
||||
*/
|
||||
static void wusbhc_gtk_rekey_work(struct work_struct *work)
|
||||
{
|
||||
struct wusbhc *wusbhc = container_of(work,
|
||||
struct wusbhc, gtk_rekey_work);
|
||||
size_t key_size = sizeof(wusbhc->gtk.data);
|
||||
int port_idx;
|
||||
struct wusb_dev *wusb_dev, *wusb_dev_next;
|
||||
LIST_HEAD(rekey_list);
|
||||
|
||||
mutex_lock(&wusbhc->mutex);
|
||||
/* generate the new key */
|
||||
wusbhc_generate_gtk(wusbhc);
|
||||
/* roll the gtk index. */
|
||||
wusbhc->gtk_index = (wusbhc->gtk_index + 1) % (WUSB_KEY_INDEX_MAX + 1);
|
||||
/*
|
||||
* Save all connected devices on a list while holding wusbhc->mutex and
|
||||
* take a reference to each one. Then submit the set key request to
|
||||
* them after releasing the lock in order to avoid a deadlock.
|
||||
*/
|
||||
for (port_idx = 0; port_idx < wusbhc->ports_max; port_idx++) {
|
||||
wusb_dev = wusbhc->port[port_idx].wusb_dev;
|
||||
if (!wusb_dev || !wusb_dev->usb_dev
|
||||
|| !wusb_dev->usb_dev->authenticated)
|
||||
continue;
|
||||
|
||||
wusb_dev_get(wusb_dev);
|
||||
list_add_tail(&wusb_dev->rekey_node, &rekey_list);
|
||||
}
|
||||
mutex_unlock(&wusbhc->mutex);
|
||||
|
||||
/* Submit the rekey requests without holding wusbhc->mutex. */
|
||||
list_for_each_entry_safe(wusb_dev, wusb_dev_next, &rekey_list,
|
||||
rekey_node) {
|
||||
list_del_init(&wusb_dev->rekey_node);
|
||||
dev_dbg(&wusb_dev->usb_dev->dev,
|
||||
"%s: rekey device at port %d\n",
|
||||
__func__, wusb_dev->port_idx);
|
||||
|
||||
if (wusb_dev_set_gtk(wusbhc, wusb_dev) < 0) {
|
||||
dev_err(&wusb_dev->usb_dev->dev,
|
||||
"%s: rekey device at port %d failed\n",
|
||||
__func__, wusb_dev->port_idx);
|
||||
}
|
||||
wusb_dev_put(wusb_dev);
|
||||
}
|
||||
|
||||
/* Switch the host controller to use the new GTK. */
|
||||
mutex_lock(&wusbhc->mutex);
|
||||
wusbhc->set_gtk(wusbhc, wusbhc->gtk_tkid,
|
||||
&wusbhc->gtk.descr.bKeyData, key_size);
|
||||
mutex_unlock(&wusbhc->mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* wusbhc_gtk_rekey - generate and distribute a new GTK
|
||||
* @wusbhc: the WUSB host controller
|
||||
*
|
||||
* Generate a new GTK and distribute it to all connected and
|
||||
* authenticated devices. When all devices have the new GTK, the host
|
||||
* starts using it.
|
||||
*
|
||||
* This must be called after every device disconnect (see [WUSB]
|
||||
* section 6.2.11.2).
|
||||
*/
|
||||
void wusbhc_gtk_rekey(struct wusbhc *wusbhc)
|
||||
{
|
||||
/*
|
||||
* We need to submit a URB to the downstream WUSB devices in order to
|
||||
* change the group key. This can't be done while holding the
|
||||
* wusbhc->mutex since that is also taken in the urb_enqueue routine
|
||||
* and will cause a deadlock. Instead, queue a work item to do
|
||||
* it when the lock is not held
|
||||
*/
|
||||
queue_work(wusbhc->wq_security, &wusbhc->gtk_rekey_work);
|
||||
}
|
99
drivers/usb/wusbcore/wa-hc.c
Normal file
99
drivers/usb/wusbcore/wa-hc.c
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Wire Adapter Host Controller Driver
|
||||
* Common items to HWA and DWA based HCDs
|
||||
*
|
||||
* Copyright (C) 2005-2006 Intel Corporation
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* FIXME: docs
|
||||
*/
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include "wusbhc.h"
|
||||
#include "wa-hc.h"
|
||||
|
||||
/**
|
||||
* Assumes
|
||||
*
|
||||
* wa->usb_dev and wa->usb_iface initialized and refcounted,
|
||||
* wa->wa_descr initialized.
|
||||
*/
|
||||
int wa_create(struct wahc *wa, struct usb_interface *iface,
|
||||
kernel_ulong_t quirks)
|
||||
{
|
||||
int result;
|
||||
struct device *dev = &iface->dev;
|
||||
|
||||
result = wa_rpipes_create(wa);
|
||||
if (result < 0)
|
||||
goto error_rpipes_create;
|
||||
wa->quirks = quirks;
|
||||
/* Fill up Data Transfer EP pointers */
|
||||
wa->dti_epd = &iface->cur_altsetting->endpoint[1].desc;
|
||||
wa->dto_epd = &iface->cur_altsetting->endpoint[2].desc;
|
||||
wa->dti_buf_size = usb_endpoint_maxp(wa->dti_epd);
|
||||
wa->dti_buf = kmalloc(wa->dti_buf_size, GFP_KERNEL);
|
||||
if (wa->dti_buf == NULL) {
|
||||
result = -ENOMEM;
|
||||
goto error_dti_buf_alloc;
|
||||
}
|
||||
result = wa_nep_create(wa, iface);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "WA-CDS: can't initialize notif endpoint: %d\n",
|
||||
result);
|
||||
goto error_nep_create;
|
||||
}
|
||||
return 0;
|
||||
|
||||
error_nep_create:
|
||||
kfree(wa->dti_buf);
|
||||
error_dti_buf_alloc:
|
||||
wa_rpipes_destroy(wa);
|
||||
error_rpipes_create:
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wa_create);
|
||||
|
||||
|
||||
void __wa_destroy(struct wahc *wa)
|
||||
{
|
||||
if (wa->dti_urb) {
|
||||
usb_kill_urb(wa->dti_urb);
|
||||
usb_put_urb(wa->dti_urb);
|
||||
}
|
||||
kfree(wa->dti_buf);
|
||||
wa_nep_destroy(wa);
|
||||
wa_rpipes_destroy(wa);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__wa_destroy);
|
||||
|
||||
/**
|
||||
* wa_reset_all - reset the WA device
|
||||
* @wa: the WA to be reset
|
||||
*
|
||||
* For HWAs the radio controller and all other PALs are also reset.
|
||||
*/
|
||||
void wa_reset_all(struct wahc *wa)
|
||||
{
|
||||
/* FIXME: assuming HWA. */
|
||||
wusbhc_reset_all(wa->wusb);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
|
||||
MODULE_DESCRIPTION("Wireless USB Wire Adapter core");
|
||||
MODULE_LICENSE("GPL");
|
481
drivers/usb/wusbcore/wa-hc.h
Normal file
481
drivers/usb/wusbcore/wa-hc.h
Normal file
|
@ -0,0 +1,481 @@
|
|||
/*
|
||||
* HWA Host Controller Driver
|
||||
* Wire Adapter Control/Data Streaming Iface (WUSB1.0[8])
|
||||
*
|
||||
* Copyright (C) 2005-2006 Intel Corporation
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* This driver implements a USB Host Controller (struct usb_hcd) for a
|
||||
* Wireless USB Host Controller based on the Wireless USB 1.0
|
||||
* Host-Wire-Adapter specification (in layman terms, a USB-dongle that
|
||||
* implements a Wireless USB host).
|
||||
*
|
||||
* Check out the Design-overview.txt file in the source documentation
|
||||
* for other details on the implementation.
|
||||
*
|
||||
* Main blocks:
|
||||
*
|
||||
* driver glue with the driver API, workqueue daemon
|
||||
*
|
||||
* lc RC instance life cycle management (create, destroy...)
|
||||
*
|
||||
* hcd glue with the USB API Host Controller Interface API.
|
||||
*
|
||||
* nep Notification EndPoint management: collect notifications
|
||||
* and queue them with the workqueue daemon.
|
||||
*
|
||||
* Handle notifications as coming from the NEP. Sends them
|
||||
* off others to their respective modules (eg: connect,
|
||||
* disconnect and reset go to devconnect).
|
||||
*
|
||||
* rpipe Remote Pipe management; rpipe is what we use to write
|
||||
* to an endpoint on a WUSB device that is connected to a
|
||||
* HWA RC.
|
||||
*
|
||||
* xfer Transfer management -- this is all the code that gets a
|
||||
* buffer and pushes it to a device (or viceversa). *
|
||||
*
|
||||
* Some day a lot of this code will be shared between this driver and
|
||||
* the drivers for DWA (xfer, rpipe).
|
||||
*
|
||||
* All starts at driver.c:hwahc_probe(), when one of this guys is
|
||||
* connected. hwahc_disconnect() stops it.
|
||||
*
|
||||
* During operation, the main driver is devices connecting or
|
||||
* disconnecting. They cause the HWA RC to send notifications into
|
||||
* nep.c:hwahc_nep_cb() that will dispatch them to
|
||||
* notif.c:wa_notif_dispatch(). From there they will fan to cause
|
||||
* device connects, disconnects, etc.
|
||||
*
|
||||
* Note much of the activity is difficult to follow. For example a
|
||||
* device connect goes to devconnect, which will cause the "fake" root
|
||||
* hub port to show a connect and stop there. Then hub_wq will notice
|
||||
* and call into the rh.c:hwahc_rc_port_reset() code to authenticate
|
||||
* the device (and this might require user intervention) and enable
|
||||
* the port.
|
||||
*
|
||||
* We also have a timer workqueue going from devconnect.c that
|
||||
* schedules in hwahc_devconnect_create().
|
||||
*
|
||||
* The rest of the traffic is in the usual entry points of a USB HCD,
|
||||
* which are hooked up in driver.c:hwahc_rc_driver, and defined in
|
||||
* hcd.c.
|
||||
*/
|
||||
|
||||
#ifndef __HWAHC_INTERNAL_H__
|
||||
#define __HWAHC_INTERNAL_H__
|
||||
|
||||
#include <linux/completion.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/uwb.h>
|
||||
#include <linux/usb/wusb.h>
|
||||
#include <linux/usb/wusb-wa.h>
|
||||
|
||||
struct wusbhc;
|
||||
struct wahc;
|
||||
extern void wa_urb_enqueue_run(struct work_struct *ws);
|
||||
extern void wa_process_errored_transfers_run(struct work_struct *ws);
|
||||
|
||||
/**
|
||||
* RPipe instance
|
||||
*
|
||||
* @descr's fields are kept in LE, as we need to send it back and
|
||||
* forth.
|
||||
*
|
||||
* @wa is referenced when set
|
||||
*
|
||||
* @segs_available is the number of requests segments that still can
|
||||
* be submitted to the controller without overloading
|
||||
* it. It is initialized to descr->wRequests when
|
||||
* aiming.
|
||||
*
|
||||
* A rpipe supports a max of descr->wRequests at the same time; before
|
||||
* submitting seg_lock has to be taken. If segs_avail > 0, then we can
|
||||
* submit; if not, we have to queue them.
|
||||
*/
|
||||
struct wa_rpipe {
|
||||
struct kref refcnt;
|
||||
struct usb_rpipe_descriptor descr;
|
||||
struct usb_host_endpoint *ep;
|
||||
struct wahc *wa;
|
||||
spinlock_t seg_lock;
|
||||
struct list_head seg_list;
|
||||
struct list_head list_node;
|
||||
atomic_t segs_available;
|
||||
u8 buffer[1]; /* For reads/writes on USB */
|
||||
};
|
||||
|
||||
|
||||
enum wa_dti_state {
|
||||
WA_DTI_TRANSFER_RESULT_PENDING,
|
||||
WA_DTI_ISOC_PACKET_STATUS_PENDING,
|
||||
WA_DTI_BUF_IN_DATA_PENDING
|
||||
};
|
||||
|
||||
enum wa_quirks {
|
||||
/*
|
||||
* The Alereon HWA expects the data frames in isochronous transfer
|
||||
* requests to be concatenated and not sent as separate packets.
|
||||
*/
|
||||
WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC = 0x01,
|
||||
/*
|
||||
* The Alereon HWA can be instructed to not send transfer notifications
|
||||
* as an optimization.
|
||||
*/
|
||||
WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS = 0x02,
|
||||
};
|
||||
|
||||
enum wa_vendor_specific_requests {
|
||||
WA_REQ_ALEREON_DISABLE_XFER_NOTIFICATIONS = 0x4C,
|
||||
WA_REQ_ALEREON_FEATURE_SET = 0x01,
|
||||
WA_REQ_ALEREON_FEATURE_CLEAR = 0x00,
|
||||
};
|
||||
|
||||
#define WA_MAX_BUF_IN_URBS 4
|
||||
/**
|
||||
* Instance of a HWA Host Controller
|
||||
*
|
||||
* Except where a more specific lock/mutex applies or atomic, all
|
||||
* fields protected by @mutex.
|
||||
*
|
||||
* @wa_descr Can be accessed without locking because it is in
|
||||
* the same area where the device descriptors were
|
||||
* read, so it is guaranteed to exist unmodified while
|
||||
* the device exists.
|
||||
*
|
||||
* Endianess has been converted to CPU's.
|
||||
*
|
||||
* @nep_* can be accessed without locking as its processing is
|
||||
* serialized; we submit a NEP URB and it comes to
|
||||
* hwahc_nep_cb(), which won't issue another URB until it is
|
||||
* done processing it.
|
||||
*
|
||||
* @xfer_list:
|
||||
*
|
||||
* List of active transfers to verify existence from a xfer id
|
||||
* gotten from the xfer result message. Can't use urb->list because
|
||||
* it goes by endpoint, and we don't know the endpoint at the time
|
||||
* when we get the xfer result message. We can't really rely on the
|
||||
* pointer (will have to change for 64 bits) as the xfer id is 32 bits.
|
||||
*
|
||||
* @xfer_delayed_list: List of transfers that need to be started
|
||||
* (with a workqueue, because they were
|
||||
* submitted from an atomic context).
|
||||
*
|
||||
* FIXME: this needs to be layered up: a wusbhc layer (for sharing
|
||||
* commonalities with WHCI), a wa layer (for sharing
|
||||
* commonalities with DWA-RC).
|
||||
*/
|
||||
struct wahc {
|
||||
struct usb_device *usb_dev;
|
||||
struct usb_interface *usb_iface;
|
||||
|
||||
/* HC to deliver notifications */
|
||||
union {
|
||||
struct wusbhc *wusb;
|
||||
struct dwahc *dwa;
|
||||
};
|
||||
|
||||
const struct usb_endpoint_descriptor *dto_epd, *dti_epd;
|
||||
const struct usb_wa_descriptor *wa_descr;
|
||||
|
||||
struct urb *nep_urb; /* Notification EndPoint [lockless] */
|
||||
struct edc nep_edc;
|
||||
void *nep_buffer;
|
||||
size_t nep_buffer_size;
|
||||
|
||||
atomic_t notifs_queued;
|
||||
|
||||
u16 rpipes;
|
||||
unsigned long *rpipe_bm; /* rpipe usage bitmap */
|
||||
struct list_head rpipe_delayed_list; /* delayed RPIPES. */
|
||||
spinlock_t rpipe_lock; /* protect rpipe_bm and delayed list */
|
||||
struct mutex rpipe_mutex; /* assigning resources to endpoints */
|
||||
|
||||
/*
|
||||
* dti_state is used to track the state of the dti_urb. When dti_state
|
||||
* is WA_DTI_ISOC_PACKET_STATUS_PENDING, dti_isoc_xfer_in_progress and
|
||||
* dti_isoc_xfer_seg identify which xfer the incoming isoc packet
|
||||
* status refers to.
|
||||
*/
|
||||
enum wa_dti_state dti_state;
|
||||
u32 dti_isoc_xfer_in_progress;
|
||||
u8 dti_isoc_xfer_seg;
|
||||
struct urb *dti_urb; /* URB for reading xfer results */
|
||||
/* URBs for reading data in */
|
||||
struct urb buf_in_urbs[WA_MAX_BUF_IN_URBS];
|
||||
int active_buf_in_urbs; /* number of buf_in_urbs active. */
|
||||
struct edc dti_edc; /* DTI error density counter */
|
||||
void *dti_buf;
|
||||
size_t dti_buf_size;
|
||||
|
||||
unsigned long dto_in_use; /* protect dto endoint serialization */
|
||||
|
||||
s32 status; /* For reading status */
|
||||
|
||||
struct list_head xfer_list;
|
||||
struct list_head xfer_delayed_list;
|
||||
struct list_head xfer_errored_list;
|
||||
/*
|
||||
* lock for the above xfer lists. Can be taken while a xfer->lock is
|
||||
* held but not in the reverse order.
|
||||
*/
|
||||
spinlock_t xfer_list_lock;
|
||||
struct work_struct xfer_enqueue_work;
|
||||
struct work_struct xfer_error_work;
|
||||
atomic_t xfer_id_count;
|
||||
|
||||
kernel_ulong_t quirks;
|
||||
};
|
||||
|
||||
|
||||
extern int wa_create(struct wahc *wa, struct usb_interface *iface,
|
||||
kernel_ulong_t);
|
||||
extern void __wa_destroy(struct wahc *wa);
|
||||
extern int wa_dti_start(struct wahc *wa);
|
||||
void wa_reset_all(struct wahc *wa);
|
||||
|
||||
|
||||
/* Miscellaneous constants */
|
||||
enum {
|
||||
/** Max number of EPROTO errors we tolerate on the NEP in a
|
||||
* period of time */
|
||||
HWAHC_EPROTO_MAX = 16,
|
||||
/** Period of time for EPROTO errors (in jiffies) */
|
||||
HWAHC_EPROTO_PERIOD = 4 * HZ,
|
||||
};
|
||||
|
||||
|
||||
/* Notification endpoint handling */
|
||||
extern int wa_nep_create(struct wahc *, struct usb_interface *);
|
||||
extern void wa_nep_destroy(struct wahc *);
|
||||
|
||||
static inline int wa_nep_arm(struct wahc *wa, gfp_t gfp_mask)
|
||||
{
|
||||
struct urb *urb = wa->nep_urb;
|
||||
urb->transfer_buffer = wa->nep_buffer;
|
||||
urb->transfer_buffer_length = wa->nep_buffer_size;
|
||||
return usb_submit_urb(urb, gfp_mask);
|
||||
}
|
||||
|
||||
static inline void wa_nep_disarm(struct wahc *wa)
|
||||
{
|
||||
usb_kill_urb(wa->nep_urb);
|
||||
}
|
||||
|
||||
|
||||
/* RPipes */
|
||||
static inline void wa_rpipe_init(struct wahc *wa)
|
||||
{
|
||||
INIT_LIST_HEAD(&wa->rpipe_delayed_list);
|
||||
spin_lock_init(&wa->rpipe_lock);
|
||||
mutex_init(&wa->rpipe_mutex);
|
||||
}
|
||||
|
||||
static inline void wa_init(struct wahc *wa)
|
||||
{
|
||||
int index;
|
||||
|
||||
edc_init(&wa->nep_edc);
|
||||
atomic_set(&wa->notifs_queued, 0);
|
||||
wa->dti_state = WA_DTI_TRANSFER_RESULT_PENDING;
|
||||
wa_rpipe_init(wa);
|
||||
edc_init(&wa->dti_edc);
|
||||
INIT_LIST_HEAD(&wa->xfer_list);
|
||||
INIT_LIST_HEAD(&wa->xfer_delayed_list);
|
||||
INIT_LIST_HEAD(&wa->xfer_errored_list);
|
||||
spin_lock_init(&wa->xfer_list_lock);
|
||||
INIT_WORK(&wa->xfer_enqueue_work, wa_urb_enqueue_run);
|
||||
INIT_WORK(&wa->xfer_error_work, wa_process_errored_transfers_run);
|
||||
wa->dto_in_use = 0;
|
||||
atomic_set(&wa->xfer_id_count, 1);
|
||||
/* init the buf in URBs */
|
||||
for (index = 0; index < WA_MAX_BUF_IN_URBS; ++index)
|
||||
usb_init_urb(&(wa->buf_in_urbs[index]));
|
||||
wa->active_buf_in_urbs = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a pipe (when refcount drops to zero)
|
||||
*
|
||||
* Assumes it has been moved to the "QUIESCING" state.
|
||||
*/
|
||||
struct wa_xfer;
|
||||
extern void rpipe_destroy(struct kref *_rpipe);
|
||||
static inline
|
||||
void __rpipe_get(struct wa_rpipe *rpipe)
|
||||
{
|
||||
kref_get(&rpipe->refcnt);
|
||||
}
|
||||
extern int rpipe_get_by_ep(struct wahc *, struct usb_host_endpoint *,
|
||||
struct urb *, gfp_t);
|
||||
static inline void rpipe_put(struct wa_rpipe *rpipe)
|
||||
{
|
||||
kref_put(&rpipe->refcnt, rpipe_destroy);
|
||||
|
||||
}
|
||||
extern void rpipe_ep_disable(struct wahc *, struct usb_host_endpoint *);
|
||||
extern void rpipe_clear_feature_stalled(struct wahc *,
|
||||
struct usb_host_endpoint *);
|
||||
extern int wa_rpipes_create(struct wahc *);
|
||||
extern void wa_rpipes_destroy(struct wahc *);
|
||||
static inline void rpipe_avail_dec(struct wa_rpipe *rpipe)
|
||||
{
|
||||
atomic_dec(&rpipe->segs_available);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the rpipe is ready to submit more segments.
|
||||
*/
|
||||
static inline int rpipe_avail_inc(struct wa_rpipe *rpipe)
|
||||
{
|
||||
return atomic_inc_return(&rpipe->segs_available) > 0
|
||||
&& !list_empty(&rpipe->seg_list);
|
||||
}
|
||||
|
||||
|
||||
/* Transferring data */
|
||||
extern int wa_urb_enqueue(struct wahc *, struct usb_host_endpoint *,
|
||||
struct urb *, gfp_t);
|
||||
extern int wa_urb_dequeue(struct wahc *, struct urb *, int);
|
||||
extern void wa_handle_notif_xfer(struct wahc *, struct wa_notif_hdr *);
|
||||
|
||||
|
||||
/* Misc
|
||||
*
|
||||
* FIXME: Refcounting for the actual @hwahc object is not correct; I
|
||||
* mean, this should be refcounting on the HCD underneath, but
|
||||
* it is not. In any case, the semantics for HCD refcounting
|
||||
* are *weird*...on refcount reaching zero it just frees
|
||||
* it...no RC specific function is called...unless I miss
|
||||
* something.
|
||||
*
|
||||
* FIXME: has to go away in favour of a 'struct' hcd based solution
|
||||
*/
|
||||
static inline struct wahc *wa_get(struct wahc *wa)
|
||||
{
|
||||
usb_get_intf(wa->usb_iface);
|
||||
return wa;
|
||||
}
|
||||
|
||||
static inline void wa_put(struct wahc *wa)
|
||||
{
|
||||
usb_put_intf(wa->usb_iface);
|
||||
}
|
||||
|
||||
|
||||
static inline int __wa_feature(struct wahc *wa, unsigned op, u16 feature)
|
||||
{
|
||||
return usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
|
||||
op ? USB_REQ_SET_FEATURE : USB_REQ_CLEAR_FEATURE,
|
||||
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
feature,
|
||||
wa->usb_iface->cur_altsetting->desc.bInterfaceNumber,
|
||||
NULL, 0, USB_CTRL_SET_TIMEOUT);
|
||||
}
|
||||
|
||||
|
||||
static inline int __wa_set_feature(struct wahc *wa, u16 feature)
|
||||
{
|
||||
return __wa_feature(wa, 1, feature);
|
||||
}
|
||||
|
||||
|
||||
static inline int __wa_clear_feature(struct wahc *wa, u16 feature)
|
||||
{
|
||||
return __wa_feature(wa, 0, feature);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the status of a Wire Adapter
|
||||
*
|
||||
* @wa: Wire Adapter instance
|
||||
* @returns < 0 errno code on error, or status bitmap as described
|
||||
* in WUSB1.0[8.3.1.6].
|
||||
*
|
||||
* NOTE: need malloc, some arches don't take USB from the stack
|
||||
*/
|
||||
static inline
|
||||
s32 __wa_get_status(struct wahc *wa)
|
||||
{
|
||||
s32 result;
|
||||
result = usb_control_msg(
|
||||
wa->usb_dev, usb_rcvctrlpipe(wa->usb_dev, 0),
|
||||
USB_REQ_GET_STATUS,
|
||||
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
0, wa->usb_iface->cur_altsetting->desc.bInterfaceNumber,
|
||||
&wa->status, sizeof(wa->status), USB_CTRL_GET_TIMEOUT);
|
||||
if (result >= 0)
|
||||
result = wa->status;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Waits until the Wire Adapter's status matches @mask/@value
|
||||
*
|
||||
* @wa: Wire Adapter instance.
|
||||
* @returns < 0 errno code on error, otherwise status.
|
||||
*
|
||||
* Loop until the WAs status matches the mask and value (status & mask
|
||||
* == value). Timeout if it doesn't happen.
|
||||
*
|
||||
* FIXME: is there an official specification on how long status
|
||||
* changes can take?
|
||||
*/
|
||||
static inline s32 __wa_wait_status(struct wahc *wa, u32 mask, u32 value)
|
||||
{
|
||||
s32 result;
|
||||
unsigned loops = 10;
|
||||
do {
|
||||
msleep(50);
|
||||
result = __wa_get_status(wa);
|
||||
if ((result & mask) == value)
|
||||
break;
|
||||
if (loops-- == 0) {
|
||||
result = -ETIMEDOUT;
|
||||
break;
|
||||
}
|
||||
} while (result >= 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/** Command @hwahc to stop, @returns 0 if ok, < 0 errno code on error */
|
||||
static inline int __wa_stop(struct wahc *wa)
|
||||
{
|
||||
int result;
|
||||
struct device *dev = &wa->usb_iface->dev;
|
||||
|
||||
result = __wa_clear_feature(wa, WA_ENABLE);
|
||||
if (result < 0 && result != -ENODEV) {
|
||||
dev_err(dev, "error commanding HC to stop: %d\n", result);
|
||||
goto out;
|
||||
}
|
||||
result = __wa_wait_status(wa, WA_ENABLE, 0);
|
||||
if (result < 0 && result != -ENODEV)
|
||||
dev_err(dev, "error waiting for HC to stop: %d\n", result);
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#endif /* #ifndef __HWAHC_INTERNAL_H__ */
|
308
drivers/usb/wusbcore/wa-nep.c
Normal file
308
drivers/usb/wusbcore/wa-nep.c
Normal file
|
@ -0,0 +1,308 @@
|
|||
/*
|
||||
* WUSB Wire Adapter: Control/Data Streaming Interface (WUSB[8])
|
||||
* Notification EndPoint support
|
||||
*
|
||||
* Copyright (C) 2006 Intel Corporation
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* This part takes care of getting the notification from the hw
|
||||
* only and dispatching through wusbwad into
|
||||
* wa_notif_dispatch. Handling is done there.
|
||||
*
|
||||
* WA notifications are limited in size; most of them are three or
|
||||
* four bytes long, and the longest is the HWA Device Notification,
|
||||
* which would not exceed 38 bytes (DNs are limited in payload to 32
|
||||
* bytes plus 3 bytes header (WUSB1.0[7.6p2]), plus 3 bytes HWA
|
||||
* header (WUSB1.0[8.5.4.2]).
|
||||
*
|
||||
* It is not clear if more than one Device Notification can be packed
|
||||
* in a HWA Notification, I assume no because of the wording in
|
||||
* WUSB1.0[8.5.4.2]. In any case, the bigger any notification could
|
||||
* get is 256 bytes (as the bLength field is a byte).
|
||||
*
|
||||
* So what we do is we have this buffer and read into it; when a
|
||||
* notification arrives we schedule work to a specific, single thread
|
||||
* workqueue (so notifications are serialized) and copy the
|
||||
* notification data. After scheduling the work, we rearm the read from
|
||||
* the notification endpoint.
|
||||
*
|
||||
* Entry points here are:
|
||||
*
|
||||
* wa_nep_[create|destroy]() To initialize/release this subsystem
|
||||
*
|
||||
* wa_nep_cb() Callback for the notification
|
||||
* endpoint; when data is ready, this
|
||||
* does the dispatching.
|
||||
*/
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "wa-hc.h"
|
||||
#include "wusbhc.h"
|
||||
|
||||
/* Structure for queueing notifications to the workqueue */
|
||||
struct wa_notif_work {
|
||||
struct work_struct work;
|
||||
struct wahc *wa;
|
||||
size_t size;
|
||||
u8 data[];
|
||||
};
|
||||
|
||||
/*
|
||||
* Process incoming notifications from the WA's Notification EndPoint
|
||||
* [the wuswad daemon, basically]
|
||||
*
|
||||
* @_nw: Pointer to a descriptor which has the pointer to the
|
||||
* @wa, the size of the buffer and the work queue
|
||||
* structure (so we can free all when done).
|
||||
* @returns 0 if ok, < 0 errno code on error.
|
||||
*
|
||||
* All notifications follow the same format; they need to start with a
|
||||
* 'struct wa_notif_hdr' header, so it is easy to parse through
|
||||
* them. We just break the buffer in individual notifications (the
|
||||
* standard doesn't say if it can be done or is forbidden, so we are
|
||||
* cautious) and dispatch each.
|
||||
*
|
||||
* So the handling layers are is:
|
||||
*
|
||||
* WA specific notification (from NEP)
|
||||
* Device Notification Received -> wa_handle_notif_dn()
|
||||
* WUSB Device notification generic handling
|
||||
* BPST Adjustment -> wa_handle_notif_bpst_adj()
|
||||
* ... -> ...
|
||||
*
|
||||
* @wa has to be referenced
|
||||
*/
|
||||
static void wa_notif_dispatch(struct work_struct *ws)
|
||||
{
|
||||
void *itr;
|
||||
u8 missing = 0;
|
||||
struct wa_notif_work *nw = container_of(ws, struct wa_notif_work,
|
||||
work);
|
||||
struct wahc *wa = nw->wa;
|
||||
struct wa_notif_hdr *notif_hdr;
|
||||
size_t size;
|
||||
|
||||
struct device *dev = &wa->usb_iface->dev;
|
||||
|
||||
#if 0
|
||||
/* FIXME: need to check for this??? */
|
||||
if (usb_hcd->state == HC_STATE_QUIESCING) /* Going down? */
|
||||
goto out; /* screw it */
|
||||
#endif
|
||||
atomic_dec(&wa->notifs_queued); /* Throttling ctl */
|
||||
dev = &wa->usb_iface->dev;
|
||||
size = nw->size;
|
||||
itr = nw->data;
|
||||
|
||||
while (size) {
|
||||
if (size < sizeof(*notif_hdr)) {
|
||||
missing = sizeof(*notif_hdr) - size;
|
||||
goto exhausted_buffer;
|
||||
}
|
||||
notif_hdr = itr;
|
||||
if (size < notif_hdr->bLength)
|
||||
goto exhausted_buffer;
|
||||
itr += notif_hdr->bLength;
|
||||
size -= notif_hdr->bLength;
|
||||
/* Dispatch the notification [don't use itr or size!] */
|
||||
switch (notif_hdr->bNotifyType) {
|
||||
case HWA_NOTIF_DN: {
|
||||
struct hwa_notif_dn *hwa_dn;
|
||||
hwa_dn = container_of(notif_hdr, struct hwa_notif_dn,
|
||||
hdr);
|
||||
wusbhc_handle_dn(wa->wusb, hwa_dn->bSourceDeviceAddr,
|
||||
hwa_dn->dndata,
|
||||
notif_hdr->bLength - sizeof(*hwa_dn));
|
||||
break;
|
||||
}
|
||||
case WA_NOTIF_TRANSFER:
|
||||
wa_handle_notif_xfer(wa, notif_hdr);
|
||||
break;
|
||||
case HWA_NOTIF_BPST_ADJ:
|
||||
break; /* no action needed for BPST ADJ. */
|
||||
case DWA_NOTIF_RWAKE:
|
||||
case DWA_NOTIF_PORTSTATUS:
|
||||
/* FIXME: unimplemented WA NOTIFs */
|
||||
/* fallthru */
|
||||
default:
|
||||
dev_err(dev, "HWA: unknown notification 0x%x, "
|
||||
"%zu bytes; discarding\n",
|
||||
notif_hdr->bNotifyType,
|
||||
(size_t)notif_hdr->bLength);
|
||||
break;
|
||||
}
|
||||
}
|
||||
out:
|
||||
wa_put(wa);
|
||||
kfree(nw);
|
||||
return;
|
||||
|
||||
/* THIS SHOULD NOT HAPPEN
|
||||
*
|
||||
* Buffer exahusted with partial data remaining; just warn and
|
||||
* discard the data, as this should not happen.
|
||||
*/
|
||||
exhausted_buffer:
|
||||
dev_warn(dev, "HWA: device sent short notification, "
|
||||
"%d bytes missing; discarding %d bytes.\n",
|
||||
missing, (int)size);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Deliver incoming WA notifications to the wusbwa workqueue
|
||||
*
|
||||
* @wa: Pointer the Wire Adapter Controller Data Streaming
|
||||
* instance (part of an 'struct usb_hcd').
|
||||
* @size: Size of the received buffer
|
||||
* @returns 0 if ok, < 0 errno code on error.
|
||||
*
|
||||
* The input buffer is @wa->nep_buffer, with @size bytes
|
||||
* (guaranteed to fit in the allocated space,
|
||||
* @wa->nep_buffer_size).
|
||||
*/
|
||||
static int wa_nep_queue(struct wahc *wa, size_t size)
|
||||
{
|
||||
int result = 0;
|
||||
struct device *dev = &wa->usb_iface->dev;
|
||||
struct wa_notif_work *nw;
|
||||
|
||||
/* dev_fnstart(dev, "(wa %p, size %zu)\n", wa, size); */
|
||||
BUG_ON(size > wa->nep_buffer_size);
|
||||
if (size == 0)
|
||||
goto out;
|
||||
if (atomic_read(&wa->notifs_queued) > 200) {
|
||||
if (printk_ratelimit())
|
||||
dev_err(dev, "Too many notifications queued, "
|
||||
"throttling back\n");
|
||||
goto out;
|
||||
}
|
||||
nw = kzalloc(sizeof(*nw) + size, GFP_ATOMIC);
|
||||
if (nw == NULL) {
|
||||
if (printk_ratelimit())
|
||||
dev_err(dev, "No memory to queue notification\n");
|
||||
goto out;
|
||||
}
|
||||
INIT_WORK(&nw->work, wa_notif_dispatch);
|
||||
nw->wa = wa_get(wa);
|
||||
nw->size = size;
|
||||
memcpy(nw->data, wa->nep_buffer, size);
|
||||
atomic_inc(&wa->notifs_queued); /* Throttling ctl */
|
||||
queue_work(wusbd, &nw->work);
|
||||
out:
|
||||
/* dev_fnend(dev, "(wa %p, size %zu) = result\n", wa, size, result); */
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback for the notification event endpoint
|
||||
*
|
||||
* Check's that everything is fine and then passes the data to be
|
||||
* queued to the workqueue.
|
||||
*/
|
||||
static void wa_nep_cb(struct urb *urb)
|
||||
{
|
||||
int result;
|
||||
struct wahc *wa = urb->context;
|
||||
struct device *dev = &wa->usb_iface->dev;
|
||||
|
||||
switch (result = urb->status) {
|
||||
case 0:
|
||||
result = wa_nep_queue(wa, urb->actual_length);
|
||||
if (result < 0)
|
||||
dev_err(dev, "NEP: unable to process notification(s): "
|
||||
"%d\n", result);
|
||||
break;
|
||||
case -ECONNRESET: /* Not an error, but a controlled situation; */
|
||||
case -ENOENT: /* (we killed the URB)...so, no broadcast */
|
||||
case -ESHUTDOWN:
|
||||
dev_dbg(dev, "NEP: going down %d\n", urb->status);
|
||||
goto out;
|
||||
default: /* On general errors, we retry unless it gets ugly */
|
||||
if (edc_inc(&wa->nep_edc, EDC_MAX_ERRORS,
|
||||
EDC_ERROR_TIMEFRAME)) {
|
||||
dev_err(dev, "NEP: URB max acceptable errors "
|
||||
"exceeded, resetting device\n");
|
||||
wa_reset_all(wa);
|
||||
goto out;
|
||||
}
|
||||
dev_err(dev, "NEP: URB error %d\n", urb->status);
|
||||
}
|
||||
result = wa_nep_arm(wa, GFP_ATOMIC);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "NEP: cannot submit URB: %d\n", result);
|
||||
wa_reset_all(wa);
|
||||
}
|
||||
out:
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize @wa's notification and event's endpoint stuff
|
||||
*
|
||||
* This includes the allocating the read buffer, the context ID
|
||||
* allocation bitmap, the URB and submitting the URB.
|
||||
*/
|
||||
int wa_nep_create(struct wahc *wa, struct usb_interface *iface)
|
||||
{
|
||||
int result;
|
||||
struct usb_endpoint_descriptor *epd;
|
||||
struct usb_device *usb_dev = interface_to_usbdev(iface);
|
||||
struct device *dev = &iface->dev;
|
||||
|
||||
edc_init(&wa->nep_edc);
|
||||
epd = &iface->cur_altsetting->endpoint[0].desc;
|
||||
wa->nep_buffer_size = 1024;
|
||||
wa->nep_buffer = kmalloc(wa->nep_buffer_size, GFP_KERNEL);
|
||||
if (wa->nep_buffer == NULL) {
|
||||
dev_err(dev,
|
||||
"Unable to allocate notification's read buffer\n");
|
||||
goto error_nep_buffer;
|
||||
}
|
||||
wa->nep_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (wa->nep_urb == NULL) {
|
||||
dev_err(dev, "Unable to allocate notification URB\n");
|
||||
goto error_urb_alloc;
|
||||
}
|
||||
usb_fill_int_urb(wa->nep_urb, usb_dev,
|
||||
usb_rcvintpipe(usb_dev, epd->bEndpointAddress),
|
||||
wa->nep_buffer, wa->nep_buffer_size,
|
||||
wa_nep_cb, wa, epd->bInterval);
|
||||
result = wa_nep_arm(wa, GFP_KERNEL);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "Cannot submit notification URB: %d\n", result);
|
||||
goto error_nep_arm;
|
||||
}
|
||||
return 0;
|
||||
|
||||
error_nep_arm:
|
||||
usb_free_urb(wa->nep_urb);
|
||||
error_urb_alloc:
|
||||
kfree(wa->nep_buffer);
|
||||
error_nep_buffer:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
void wa_nep_destroy(struct wahc *wa)
|
||||
{
|
||||
wa_nep_disarm(wa);
|
||||
usb_free_urb(wa->nep_urb);
|
||||
kfree(wa->nep_buffer);
|
||||
}
|
555
drivers/usb/wusbcore/wa-rpipe.c
Normal file
555
drivers/usb/wusbcore/wa-rpipe.c
Normal file
|
@ -0,0 +1,555 @@
|
|||
/*
|
||||
* WUSB Wire Adapter
|
||||
* rpipe management
|
||||
*
|
||||
* Copyright (C) 2005-2006 Intel Corporation
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* FIXME: docs
|
||||
*
|
||||
* RPIPE
|
||||
*
|
||||
* Targeted at different downstream endpoints
|
||||
*
|
||||
* Descriptor: use to config the remote pipe.
|
||||
*
|
||||
* The number of blocks could be dynamic (wBlocks in descriptor is
|
||||
* 0)--need to schedule them then.
|
||||
*
|
||||
* Each bit in wa->rpipe_bm represents if an rpipe is being used or
|
||||
* not. Rpipes are represented with a 'struct wa_rpipe' that is
|
||||
* attached to the hcpriv member of a 'struct usb_host_endpoint'.
|
||||
*
|
||||
* When you need to xfer data to an endpoint, you get an rpipe for it
|
||||
* with wa_ep_rpipe_get(), which gives you a reference to the rpipe
|
||||
* and keeps a single one (the first one) with the endpoint. When you
|
||||
* are done transferring, you drop that reference. At the end the
|
||||
* rpipe is always allocated and bound to the endpoint. There it might
|
||||
* be recycled when not used.
|
||||
*
|
||||
* Addresses:
|
||||
*
|
||||
* We use a 1:1 mapping mechanism between port address (0 based
|
||||
* index, actually) and the address. The USB stack knows about this.
|
||||
*
|
||||
* USB Stack port number 4 (1 based)
|
||||
* WUSB code port index 3 (0 based)
|
||||
* USB Address 5 (2 based -- 0 is for default, 1 for root hub)
|
||||
*
|
||||
* Now, because we don't use the concept as default address exactly
|
||||
* like the (wired) USB code does, we need to kind of skip it. So we
|
||||
* never take addresses from the urb->pipe, but from the
|
||||
* urb->dev->devnum, to make sure that we always have the right
|
||||
* destination address.
|
||||
*/
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#include "wusbhc.h"
|
||||
#include "wa-hc.h"
|
||||
|
||||
static int __rpipe_get_descr(struct wahc *wa,
|
||||
struct usb_rpipe_descriptor *descr, u16 index)
|
||||
{
|
||||
ssize_t result;
|
||||
struct device *dev = &wa->usb_iface->dev;
|
||||
|
||||
/* Get the RPIPE descriptor -- we cannot use the usb_get_descriptor()
|
||||
* function because the arguments are different.
|
||||
*/
|
||||
result = usb_control_msg(
|
||||
wa->usb_dev, usb_rcvctrlpipe(wa->usb_dev, 0),
|
||||
USB_REQ_GET_DESCRIPTOR,
|
||||
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_RPIPE,
|
||||
USB_DT_RPIPE<<8, index, descr, sizeof(*descr),
|
||||
USB_CTRL_GET_TIMEOUT);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "rpipe %u: get descriptor failed: %d\n",
|
||||
index, (int)result);
|
||||
goto error;
|
||||
}
|
||||
if (result < sizeof(*descr)) {
|
||||
dev_err(dev, "rpipe %u: got short descriptor "
|
||||
"(%zd vs %zd bytes needed)\n",
|
||||
index, result, sizeof(*descr));
|
||||
result = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
result = 0;
|
||||
|
||||
error:
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* The descriptor is assumed to be properly initialized (ie: you got
|
||||
* it through __rpipe_get_descr()).
|
||||
*/
|
||||
static int __rpipe_set_descr(struct wahc *wa,
|
||||
struct usb_rpipe_descriptor *descr, u16 index)
|
||||
{
|
||||
ssize_t result;
|
||||
struct device *dev = &wa->usb_iface->dev;
|
||||
|
||||
/* we cannot use the usb_get_descriptor() function because the
|
||||
* arguments are different.
|
||||
*/
|
||||
result = usb_control_msg(
|
||||
wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
|
||||
USB_REQ_SET_DESCRIPTOR,
|
||||
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_RPIPE,
|
||||
USB_DT_RPIPE<<8, index, descr, sizeof(*descr),
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "rpipe %u: set descriptor failed: %d\n",
|
||||
index, (int)result);
|
||||
goto error;
|
||||
}
|
||||
if (result < sizeof(*descr)) {
|
||||
dev_err(dev, "rpipe %u: sent short descriptor "
|
||||
"(%zd vs %zd bytes required)\n",
|
||||
index, result, sizeof(*descr));
|
||||
result = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
result = 0;
|
||||
|
||||
error:
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
static void rpipe_init(struct wa_rpipe *rpipe)
|
||||
{
|
||||
kref_init(&rpipe->refcnt);
|
||||
spin_lock_init(&rpipe->seg_lock);
|
||||
INIT_LIST_HEAD(&rpipe->seg_list);
|
||||
INIT_LIST_HEAD(&rpipe->list_node);
|
||||
}
|
||||
|
||||
static unsigned rpipe_get_idx(struct wahc *wa, unsigned rpipe_idx)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&wa->rpipe_lock, flags);
|
||||
rpipe_idx = find_next_zero_bit(wa->rpipe_bm, wa->rpipes, rpipe_idx);
|
||||
if (rpipe_idx < wa->rpipes)
|
||||
set_bit(rpipe_idx, wa->rpipe_bm);
|
||||
spin_unlock_irqrestore(&wa->rpipe_lock, flags);
|
||||
|
||||
return rpipe_idx;
|
||||
}
|
||||
|
||||
static void rpipe_put_idx(struct wahc *wa, unsigned rpipe_idx)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&wa->rpipe_lock, flags);
|
||||
clear_bit(rpipe_idx, wa->rpipe_bm);
|
||||
spin_unlock_irqrestore(&wa->rpipe_lock, flags);
|
||||
}
|
||||
|
||||
void rpipe_destroy(struct kref *_rpipe)
|
||||
{
|
||||
struct wa_rpipe *rpipe = container_of(_rpipe, struct wa_rpipe, refcnt);
|
||||
u8 index = le16_to_cpu(rpipe->descr.wRPipeIndex);
|
||||
|
||||
if (rpipe->ep)
|
||||
rpipe->ep->hcpriv = NULL;
|
||||
rpipe_put_idx(rpipe->wa, index);
|
||||
wa_put(rpipe->wa);
|
||||
kfree(rpipe);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rpipe_destroy);
|
||||
|
||||
/*
|
||||
* Locate an idle rpipe, create an structure for it and return it
|
||||
*
|
||||
* @wa is referenced and unlocked
|
||||
* @crs enum rpipe_attr, required endpoint characteristics
|
||||
*
|
||||
* The rpipe can be used only sequentially (not in parallel).
|
||||
*
|
||||
* The rpipe is moved into the "ready" state.
|
||||
*/
|
||||
static int rpipe_get_idle(struct wa_rpipe **prpipe, struct wahc *wa, u8 crs,
|
||||
gfp_t gfp)
|
||||
{
|
||||
int result;
|
||||
unsigned rpipe_idx;
|
||||
struct wa_rpipe *rpipe;
|
||||
struct device *dev = &wa->usb_iface->dev;
|
||||
|
||||
rpipe = kzalloc(sizeof(*rpipe), gfp);
|
||||
if (rpipe == NULL)
|
||||
return -ENOMEM;
|
||||
rpipe_init(rpipe);
|
||||
|
||||
/* Look for an idle pipe */
|
||||
for (rpipe_idx = 0; rpipe_idx < wa->rpipes; rpipe_idx++) {
|
||||
rpipe_idx = rpipe_get_idx(wa, rpipe_idx);
|
||||
if (rpipe_idx >= wa->rpipes) /* no more pipes :( */
|
||||
break;
|
||||
result = __rpipe_get_descr(wa, &rpipe->descr, rpipe_idx);
|
||||
if (result < 0)
|
||||
dev_err(dev, "Can't get descriptor for rpipe %u: %d\n",
|
||||
rpipe_idx, result);
|
||||
else if ((rpipe->descr.bmCharacteristics & crs) != 0)
|
||||
goto found;
|
||||
rpipe_put_idx(wa, rpipe_idx);
|
||||
}
|
||||
*prpipe = NULL;
|
||||
kfree(rpipe);
|
||||
return -ENXIO;
|
||||
|
||||
found:
|
||||
set_bit(rpipe_idx, wa->rpipe_bm);
|
||||
rpipe->wa = wa_get(wa);
|
||||
*prpipe = rpipe;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __rpipe_reset(struct wahc *wa, unsigned index)
|
||||
{
|
||||
int result;
|
||||
struct device *dev = &wa->usb_iface->dev;
|
||||
|
||||
result = usb_control_msg(
|
||||
wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
|
||||
USB_REQ_RPIPE_RESET,
|
||||
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_RPIPE,
|
||||
0, index, NULL, 0, USB_CTRL_SET_TIMEOUT);
|
||||
if (result < 0)
|
||||
dev_err(dev, "rpipe %u: reset failed: %d\n",
|
||||
index, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fake companion descriptor for ep0
|
||||
*
|
||||
* See WUSB1.0[7.4.4], most of this is zero for bulk/int/ctl
|
||||
*/
|
||||
static struct usb_wireless_ep_comp_descriptor epc0 = {
|
||||
.bLength = sizeof(epc0),
|
||||
.bDescriptorType = USB_DT_WIRELESS_ENDPOINT_COMP,
|
||||
.bMaxBurst = 1,
|
||||
.bMaxSequence = 2,
|
||||
};
|
||||
|
||||
/*
|
||||
* Look for EP companion descriptor
|
||||
*
|
||||
* Get there, look for Inara in the endpoint's extra descriptors
|
||||
*/
|
||||
static struct usb_wireless_ep_comp_descriptor *rpipe_epc_find(
|
||||
struct device *dev, struct usb_host_endpoint *ep)
|
||||
{
|
||||
void *itr;
|
||||
size_t itr_size;
|
||||
struct usb_descriptor_header *hdr;
|
||||
struct usb_wireless_ep_comp_descriptor *epcd;
|
||||
|
||||
if (ep->desc.bEndpointAddress == 0) {
|
||||
epcd = &epc0;
|
||||
goto out;
|
||||
}
|
||||
itr = ep->extra;
|
||||
itr_size = ep->extralen;
|
||||
epcd = NULL;
|
||||
while (itr_size > 0) {
|
||||
if (itr_size < sizeof(*hdr)) {
|
||||
dev_err(dev, "HW Bug? ep 0x%02x: extra descriptors "
|
||||
"at offset %zu: only %zu bytes left\n",
|
||||
ep->desc.bEndpointAddress,
|
||||
itr - (void *) ep->extra, itr_size);
|
||||
break;
|
||||
}
|
||||
hdr = itr;
|
||||
if (hdr->bDescriptorType == USB_DT_WIRELESS_ENDPOINT_COMP) {
|
||||
epcd = itr;
|
||||
break;
|
||||
}
|
||||
if (hdr->bLength > itr_size) {
|
||||
dev_err(dev, "HW Bug? ep 0x%02x: extra descriptor "
|
||||
"at offset %zu (type 0x%02x) "
|
||||
"length %d but only %zu bytes left\n",
|
||||
ep->desc.bEndpointAddress,
|
||||
itr - (void *) ep->extra, hdr->bDescriptorType,
|
||||
hdr->bLength, itr_size);
|
||||
break;
|
||||
}
|
||||
itr += hdr->bLength;
|
||||
itr_size -= hdr->bLength;
|
||||
}
|
||||
out:
|
||||
return epcd;
|
||||
}
|
||||
|
||||
/*
|
||||
* Aim an rpipe to its device & endpoint destination
|
||||
*
|
||||
* Make sure we change the address to unauthenticated if the device
|
||||
* is WUSB and it is not authenticated.
|
||||
*/
|
||||
static int rpipe_aim(struct wa_rpipe *rpipe, struct wahc *wa,
|
||||
struct usb_host_endpoint *ep, struct urb *urb, gfp_t gfp)
|
||||
{
|
||||
int result = -ENOMSG; /* better code for lack of companion? */
|
||||
struct device *dev = &wa->usb_iface->dev;
|
||||
struct usb_device *usb_dev = urb->dev;
|
||||
struct usb_wireless_ep_comp_descriptor *epcd;
|
||||
u32 ack_window, epcd_max_sequence;
|
||||
u8 unauth;
|
||||
|
||||
epcd = rpipe_epc_find(dev, ep);
|
||||
if (epcd == NULL) {
|
||||
dev_err(dev, "ep 0x%02x: can't find companion descriptor\n",
|
||||
ep->desc.bEndpointAddress);
|
||||
goto error;
|
||||
}
|
||||
unauth = usb_dev->wusb && !usb_dev->authenticated ? 0x80 : 0;
|
||||
__rpipe_reset(wa, le16_to_cpu(rpipe->descr.wRPipeIndex));
|
||||
atomic_set(&rpipe->segs_available,
|
||||
le16_to_cpu(rpipe->descr.wRequests));
|
||||
/* FIXME: block allocation system; request with queuing and timeout */
|
||||
/* FIXME: compute so seg_size > ep->maxpktsize */
|
||||
rpipe->descr.wBlocks = cpu_to_le16(16); /* given */
|
||||
/* ep0 maxpktsize is 0x200 (WUSB1.0[4.8.1]) */
|
||||
if (usb_endpoint_xfer_isoc(&ep->desc))
|
||||
rpipe->descr.wMaxPacketSize = epcd->wOverTheAirPacketSize;
|
||||
else
|
||||
rpipe->descr.wMaxPacketSize = ep->desc.wMaxPacketSize;
|
||||
|
||||
rpipe->descr.hwa_bMaxBurst = max(min_t(unsigned int,
|
||||
epcd->bMaxBurst, 16U), 1U);
|
||||
rpipe->descr.hwa_bDeviceInfoIndex =
|
||||
wusb_port_no_to_idx(urb->dev->portnum);
|
||||
/* FIXME: use maximum speed as supported or recommended by device */
|
||||
rpipe->descr.bSpeed = usb_pipeendpoint(urb->pipe) == 0 ?
|
||||
UWB_PHY_RATE_53 : UWB_PHY_RATE_200;
|
||||
|
||||
dev_dbg(dev, "addr %u (0x%02x) rpipe #%u ep# %u speed %d\n",
|
||||
urb->dev->devnum, urb->dev->devnum | unauth,
|
||||
le16_to_cpu(rpipe->descr.wRPipeIndex),
|
||||
usb_pipeendpoint(urb->pipe), rpipe->descr.bSpeed);
|
||||
|
||||
rpipe->descr.hwa_reserved = 0;
|
||||
|
||||
rpipe->descr.bEndpointAddress = ep->desc.bEndpointAddress;
|
||||
/* FIXME: bDataSequence */
|
||||
rpipe->descr.bDataSequence = 0;
|
||||
|
||||
/* start with base window of hwa_bMaxBurst bits starting at 0. */
|
||||
ack_window = 0xFFFFFFFF >> (32 - rpipe->descr.hwa_bMaxBurst);
|
||||
rpipe->descr.dwCurrentWindow = cpu_to_le32(ack_window);
|
||||
epcd_max_sequence = max(min_t(unsigned int,
|
||||
epcd->bMaxSequence, 32U), 2U);
|
||||
rpipe->descr.bMaxDataSequence = epcd_max_sequence - 1;
|
||||
rpipe->descr.bInterval = ep->desc.bInterval;
|
||||
if (usb_endpoint_xfer_isoc(&ep->desc))
|
||||
rpipe->descr.bOverTheAirInterval = epcd->bOverTheAirInterval;
|
||||
else
|
||||
rpipe->descr.bOverTheAirInterval = 0; /* 0 if not isoc */
|
||||
/* FIXME: xmit power & preamble blah blah */
|
||||
rpipe->descr.bmAttribute = (ep->desc.bmAttributes &
|
||||
USB_ENDPOINT_XFERTYPE_MASK);
|
||||
/* rpipe->descr.bmCharacteristics RO */
|
||||
rpipe->descr.bmRetryOptions = (wa->wusb->retry_count & 0xF);
|
||||
/* FIXME: use for assessing link quality? */
|
||||
rpipe->descr.wNumTransactionErrors = 0;
|
||||
result = __rpipe_set_descr(wa, &rpipe->descr,
|
||||
le16_to_cpu(rpipe->descr.wRPipeIndex));
|
||||
if (result < 0) {
|
||||
dev_err(dev, "Cannot aim rpipe: %d\n", result);
|
||||
goto error;
|
||||
}
|
||||
result = 0;
|
||||
error:
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check an aimed rpipe to make sure it points to where we want
|
||||
*
|
||||
* We use bit 19 of the Linux USB pipe bitmap for unauth vs auth
|
||||
* space; when it is like that, we or 0x80 to make an unauth address.
|
||||
*/
|
||||
static int rpipe_check_aim(const struct wa_rpipe *rpipe, const struct wahc *wa,
|
||||
const struct usb_host_endpoint *ep,
|
||||
const struct urb *urb, gfp_t gfp)
|
||||
{
|
||||
int result = 0;
|
||||
struct device *dev = &wa->usb_iface->dev;
|
||||
u8 portnum = wusb_port_no_to_idx(urb->dev->portnum);
|
||||
|
||||
#define AIM_CHECK(rdf, val, text) \
|
||||
do { \
|
||||
if (rpipe->descr.rdf != (val)) { \
|
||||
dev_err(dev, \
|
||||
"rpipe aim discrepancy: " #rdf " " text "\n", \
|
||||
rpipe->descr.rdf, (val)); \
|
||||
result = -EINVAL; \
|
||||
WARN_ON(1); \
|
||||
} \
|
||||
} while (0)
|
||||
AIM_CHECK(hwa_bDeviceInfoIndex, portnum, "(%u vs %u)");
|
||||
AIM_CHECK(bSpeed, usb_pipeendpoint(urb->pipe) == 0 ?
|
||||
UWB_PHY_RATE_53 : UWB_PHY_RATE_200,
|
||||
"(%u vs %u)");
|
||||
AIM_CHECK(bEndpointAddress, ep->desc.bEndpointAddress, "(%u vs %u)");
|
||||
AIM_CHECK(bInterval, ep->desc.bInterval, "(%u vs %u)");
|
||||
AIM_CHECK(bmAttribute, ep->desc.bmAttributes & 0x03, "(%u vs %u)");
|
||||
#undef AIM_CHECK
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_BUG
|
||||
#define CONFIG_BUG 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Make sure there is an rpipe allocated for an endpoint
|
||||
*
|
||||
* If already allocated, we just refcount it; if not, we get an
|
||||
* idle one, aim it to the right location and take it.
|
||||
*
|
||||
* Attaches to ep->hcpriv and rpipe->ep to ep.
|
||||
*/
|
||||
int rpipe_get_by_ep(struct wahc *wa, struct usb_host_endpoint *ep,
|
||||
struct urb *urb, gfp_t gfp)
|
||||
{
|
||||
int result = 0;
|
||||
struct device *dev = &wa->usb_iface->dev;
|
||||
struct wa_rpipe *rpipe;
|
||||
u8 eptype;
|
||||
|
||||
mutex_lock(&wa->rpipe_mutex);
|
||||
rpipe = ep->hcpriv;
|
||||
if (rpipe != NULL) {
|
||||
if (CONFIG_BUG == 1) {
|
||||
result = rpipe_check_aim(rpipe, wa, ep, urb, gfp);
|
||||
if (result < 0)
|
||||
goto error;
|
||||
}
|
||||
__rpipe_get(rpipe);
|
||||
dev_dbg(dev, "ep 0x%02x: reusing rpipe %u\n",
|
||||
ep->desc.bEndpointAddress,
|
||||
le16_to_cpu(rpipe->descr.wRPipeIndex));
|
||||
} else {
|
||||
/* hmm, assign idle rpipe, aim it */
|
||||
result = -ENOBUFS;
|
||||
eptype = ep->desc.bmAttributes & 0x03;
|
||||
result = rpipe_get_idle(&rpipe, wa, 1 << eptype, gfp);
|
||||
if (result < 0)
|
||||
goto error;
|
||||
result = rpipe_aim(rpipe, wa, ep, urb, gfp);
|
||||
if (result < 0) {
|
||||
rpipe_put(rpipe);
|
||||
goto error;
|
||||
}
|
||||
ep->hcpriv = rpipe;
|
||||
rpipe->ep = ep;
|
||||
__rpipe_get(rpipe); /* for caching into ep->hcpriv */
|
||||
dev_dbg(dev, "ep 0x%02x: using rpipe %u\n",
|
||||
ep->desc.bEndpointAddress,
|
||||
le16_to_cpu(rpipe->descr.wRPipeIndex));
|
||||
}
|
||||
error:
|
||||
mutex_unlock(&wa->rpipe_mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate the bitmap for each rpipe.
|
||||
*/
|
||||
int wa_rpipes_create(struct wahc *wa)
|
||||
{
|
||||
wa->rpipes = le16_to_cpu(wa->wa_descr->wNumRPipes);
|
||||
wa->rpipe_bm = kzalloc(BITS_TO_LONGS(wa->rpipes)*sizeof(unsigned long),
|
||||
GFP_KERNEL);
|
||||
if (wa->rpipe_bm == NULL)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void wa_rpipes_destroy(struct wahc *wa)
|
||||
{
|
||||
struct device *dev = &wa->usb_iface->dev;
|
||||
|
||||
if (!bitmap_empty(wa->rpipe_bm, wa->rpipes)) {
|
||||
char buf[256];
|
||||
WARN_ON(1);
|
||||
bitmap_scnprintf(buf, sizeof(buf), wa->rpipe_bm, wa->rpipes);
|
||||
dev_err(dev, "BUG: pipes not released on exit: %s\n", buf);
|
||||
}
|
||||
kfree(wa->rpipe_bm);
|
||||
}
|
||||
|
||||
/*
|
||||
* Release resources allocated for an endpoint
|
||||
*
|
||||
* If there is an associated rpipe to this endpoint, Abort any pending
|
||||
* transfers and put it. If the rpipe ends up being destroyed,
|
||||
* __rpipe_destroy() will cleanup ep->hcpriv.
|
||||
*
|
||||
* This is called before calling hcd->stop(), so you don't need to do
|
||||
* anything else in there.
|
||||
*/
|
||||
void rpipe_ep_disable(struct wahc *wa, struct usb_host_endpoint *ep)
|
||||
{
|
||||
struct wa_rpipe *rpipe;
|
||||
|
||||
mutex_lock(&wa->rpipe_mutex);
|
||||
rpipe = ep->hcpriv;
|
||||
if (rpipe != NULL) {
|
||||
u16 index = le16_to_cpu(rpipe->descr.wRPipeIndex);
|
||||
|
||||
usb_control_msg(
|
||||
wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
|
||||
USB_REQ_RPIPE_ABORT,
|
||||
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_RPIPE,
|
||||
0, index, NULL, 0, USB_CTRL_SET_TIMEOUT);
|
||||
rpipe_put(rpipe);
|
||||
}
|
||||
mutex_unlock(&wa->rpipe_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rpipe_ep_disable);
|
||||
|
||||
/* Clear the stalled status of an RPIPE. */
|
||||
void rpipe_clear_feature_stalled(struct wahc *wa, struct usb_host_endpoint *ep)
|
||||
{
|
||||
struct wa_rpipe *rpipe;
|
||||
|
||||
mutex_lock(&wa->rpipe_mutex);
|
||||
rpipe = ep->hcpriv;
|
||||
if (rpipe != NULL) {
|
||||
u16 index = le16_to_cpu(rpipe->descr.wRPipeIndex);
|
||||
|
||||
usb_control_msg(
|
||||
wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
|
||||
USB_REQ_CLEAR_FEATURE,
|
||||
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_RPIPE,
|
||||
RPIPE_STALL, index, NULL, 0, USB_CTRL_SET_TIMEOUT);
|
||||
}
|
||||
mutex_unlock(&wa->rpipe_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rpipe_clear_feature_stalled);
|
2942
drivers/usb/wusbcore/wa-xfer.c
Normal file
2942
drivers/usb/wusbcore/wa-xfer.c
Normal file
File diff suppressed because it is too large
Load diff
514
drivers/usb/wusbcore/wusbhc.c
Normal file
514
drivers/usb/wusbcore/wusbhc.c
Normal file
|
@ -0,0 +1,514 @@
|
|||
/*
|
||||
* Wireless USB Host Controller
|
||||
* sysfs glue, wusbcore module support and life cycle management
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2005-2006 Intel Corporation
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* Creation/destruction of wusbhc is split in two parts; that that
|
||||
* doesn't require the HCD to be added (wusbhc_{create,destroy}) and
|
||||
* the one that requires (phase B, wusbhc_b_{create,destroy}).
|
||||
*
|
||||
* This is so because usb_add_hcd() will start the HC, and thus, all
|
||||
* the HC specific stuff has to be already initialized (like sysfs
|
||||
* thingies).
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include "wusbhc.h"
|
||||
|
||||
/**
|
||||
* Extract the wusbhc that corresponds to a USB Host Controller class device
|
||||
*
|
||||
* WARNING! Apply only if @dev is that of a
|
||||
* wusbhc.usb_hcd.self->class_dev; otherwise, you loose.
|
||||
*/
|
||||
static struct wusbhc *usbhc_dev_to_wusbhc(struct device *dev)
|
||||
{
|
||||
struct usb_bus *usb_bus = dev_get_drvdata(dev);
|
||||
struct usb_hcd *usb_hcd = bus_to_hcd(usb_bus);
|
||||
return usb_hcd_to_wusbhc(usb_hcd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Show & store the current WUSB trust timeout
|
||||
*
|
||||
* We don't do locking--it is an 'atomic' value.
|
||||
*
|
||||
* The units that we store/show are always MILLISECONDS. However, the
|
||||
* value of trust_timeout is jiffies.
|
||||
*/
|
||||
static ssize_t wusb_trust_timeout_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%u\n", wusbhc->trust_timeout);
|
||||
}
|
||||
|
||||
static ssize_t wusb_trust_timeout_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev);
|
||||
ssize_t result = -ENOSYS;
|
||||
unsigned trust_timeout;
|
||||
|
||||
result = sscanf(buf, "%u", &trust_timeout);
|
||||
if (result != 1) {
|
||||
result = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
wusbhc->trust_timeout = min_t(unsigned, trust_timeout, 500);
|
||||
cancel_delayed_work(&wusbhc->keep_alive_timer);
|
||||
flush_workqueue(wusbd);
|
||||
queue_delayed_work(wusbd, &wusbhc->keep_alive_timer,
|
||||
msecs_to_jiffies(wusbhc->trust_timeout / 2));
|
||||
out:
|
||||
return result < 0 ? result : size;
|
||||
}
|
||||
static DEVICE_ATTR(wusb_trust_timeout, 0644, wusb_trust_timeout_show,
|
||||
wusb_trust_timeout_store);
|
||||
|
||||
/*
|
||||
* Show the current WUSB CHID.
|
||||
*/
|
||||
static ssize_t wusb_chid_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev);
|
||||
const struct wusb_ckhdid *chid;
|
||||
ssize_t result = 0;
|
||||
|
||||
if (wusbhc->wuie_host_info != NULL)
|
||||
chid = &wusbhc->wuie_host_info->CHID;
|
||||
else
|
||||
chid = &wusb_ckhdid_zero;
|
||||
|
||||
result += ckhdid_printf(buf, PAGE_SIZE, chid);
|
||||
result += sprintf(buf + result, "\n");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Store a new CHID.
|
||||
*
|
||||
* - Write an all zeros CHID and it will stop the controller
|
||||
* - Write a non-zero CHID and it will start it.
|
||||
*
|
||||
* See wusbhc_chid_set() for more info.
|
||||
*/
|
||||
static ssize_t wusb_chid_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev);
|
||||
struct wusb_ckhdid chid;
|
||||
ssize_t result;
|
||||
|
||||
result = sscanf(buf,
|
||||
"%02hhx %02hhx %02hhx %02hhx "
|
||||
"%02hhx %02hhx %02hhx %02hhx "
|
||||
"%02hhx %02hhx %02hhx %02hhx "
|
||||
"%02hhx %02hhx %02hhx %02hhx\n",
|
||||
&chid.data[0] , &chid.data[1] ,
|
||||
&chid.data[2] , &chid.data[3] ,
|
||||
&chid.data[4] , &chid.data[5] ,
|
||||
&chid.data[6] , &chid.data[7] ,
|
||||
&chid.data[8] , &chid.data[9] ,
|
||||
&chid.data[10], &chid.data[11],
|
||||
&chid.data[12], &chid.data[13],
|
||||
&chid.data[14], &chid.data[15]);
|
||||
if (result != 16) {
|
||||
dev_err(dev, "Unrecognized CHID (need 16 8-bit hex digits): "
|
||||
"%d\n", (int)result);
|
||||
return -EINVAL;
|
||||
}
|
||||
result = wusbhc_chid_set(wusbhc, &chid);
|
||||
return result < 0 ? result : size;
|
||||
}
|
||||
static DEVICE_ATTR(wusb_chid, 0644, wusb_chid_show, wusb_chid_store);
|
||||
|
||||
|
||||
static ssize_t wusb_phy_rate_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", wusbhc->phy_rate);
|
||||
}
|
||||
|
||||
static ssize_t wusb_phy_rate_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev);
|
||||
uint8_t phy_rate;
|
||||
ssize_t result;
|
||||
|
||||
result = sscanf(buf, "%hhu", &phy_rate);
|
||||
if (result != 1)
|
||||
return -EINVAL;
|
||||
if (phy_rate >= UWB_PHY_RATE_INVALID)
|
||||
return -EINVAL;
|
||||
|
||||
wusbhc->phy_rate = phy_rate;
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR(wusb_phy_rate, 0644, wusb_phy_rate_show,
|
||||
wusb_phy_rate_store);
|
||||
|
||||
static ssize_t wusb_dnts_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev);
|
||||
|
||||
return sprintf(buf, "num slots: %d\ninterval: %dms\n",
|
||||
wusbhc->dnts_num_slots, wusbhc->dnts_interval);
|
||||
}
|
||||
|
||||
static ssize_t wusb_dnts_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev);
|
||||
uint8_t num_slots, interval;
|
||||
ssize_t result;
|
||||
|
||||
result = sscanf(buf, "%hhu %hhu", &num_slots, &interval);
|
||||
|
||||
if (result != 2)
|
||||
return -EINVAL;
|
||||
|
||||
wusbhc->dnts_num_slots = num_slots;
|
||||
wusbhc->dnts_interval = interval;
|
||||
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR(wusb_dnts, 0644, wusb_dnts_show, wusb_dnts_store);
|
||||
|
||||
static ssize_t wusb_retry_count_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", wusbhc->retry_count);
|
||||
}
|
||||
|
||||
static ssize_t wusb_retry_count_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev);
|
||||
uint8_t retry_count;
|
||||
ssize_t result;
|
||||
|
||||
result = sscanf(buf, "%hhu", &retry_count);
|
||||
|
||||
if (result != 1)
|
||||
return -EINVAL;
|
||||
|
||||
wusbhc->retry_count = max_t(uint8_t, retry_count,
|
||||
WUSB_RETRY_COUNT_MAX);
|
||||
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR(wusb_retry_count, 0644, wusb_retry_count_show,
|
||||
wusb_retry_count_store);
|
||||
|
||||
/* Group all the WUSBHC attributes */
|
||||
static struct attribute *wusbhc_attrs[] = {
|
||||
&dev_attr_wusb_trust_timeout.attr,
|
||||
&dev_attr_wusb_chid.attr,
|
||||
&dev_attr_wusb_phy_rate.attr,
|
||||
&dev_attr_wusb_dnts.attr,
|
||||
&dev_attr_wusb_retry_count.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group wusbhc_attr_group = {
|
||||
.name = NULL, /* we want them in the same directory */
|
||||
.attrs = wusbhc_attrs,
|
||||
};
|
||||
|
||||
/*
|
||||
* Create a wusbhc instance
|
||||
*
|
||||
* NOTEs:
|
||||
*
|
||||
* - assumes *wusbhc has been zeroed and wusbhc->usb_hcd has been
|
||||
* initialized but not added.
|
||||
*
|
||||
* - fill out ports_max, mmcies_max and mmcie_{add,rm} before calling.
|
||||
*
|
||||
* - fill out wusbhc->uwb_rc and refcount it before calling
|
||||
* - fill out the wusbhc->sec_modes array
|
||||
*/
|
||||
int wusbhc_create(struct wusbhc *wusbhc)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
/* set defaults. These can be overwritten using sysfs attributes. */
|
||||
wusbhc->trust_timeout = WUSB_TRUST_TIMEOUT_MS;
|
||||
wusbhc->phy_rate = UWB_PHY_RATE_INVALID - 1;
|
||||
wusbhc->dnts_num_slots = 4;
|
||||
wusbhc->dnts_interval = 2;
|
||||
wusbhc->retry_count = WUSB_RETRY_COUNT_INFINITE;
|
||||
|
||||
mutex_init(&wusbhc->mutex);
|
||||
result = wusbhc_mmcie_create(wusbhc);
|
||||
if (result < 0)
|
||||
goto error_mmcie_create;
|
||||
result = wusbhc_devconnect_create(wusbhc);
|
||||
if (result < 0)
|
||||
goto error_devconnect_create;
|
||||
result = wusbhc_rh_create(wusbhc);
|
||||
if (result < 0)
|
||||
goto error_rh_create;
|
||||
result = wusbhc_sec_create(wusbhc);
|
||||
if (result < 0)
|
||||
goto error_sec_create;
|
||||
return 0;
|
||||
|
||||
error_sec_create:
|
||||
wusbhc_rh_destroy(wusbhc);
|
||||
error_rh_create:
|
||||
wusbhc_devconnect_destroy(wusbhc);
|
||||
error_devconnect_create:
|
||||
wusbhc_mmcie_destroy(wusbhc);
|
||||
error_mmcie_create:
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wusbhc_create);
|
||||
|
||||
static inline struct kobject *wusbhc_kobj(struct wusbhc *wusbhc)
|
||||
{
|
||||
return &wusbhc->usb_hcd.self.controller->kobj;
|
||||
}
|
||||
|
||||
/*
|
||||
* Phase B of a wusbhc instance creation
|
||||
*
|
||||
* Creates fields that depend on wusbhc->usb_hcd having been
|
||||
* added. This is where we create the sysfs files in
|
||||
* /sys/class/usb_host/usb_hostX/.
|
||||
*
|
||||
* NOTE: Assumes wusbhc->usb_hcd has been already added by the upper
|
||||
* layer (hwahc or whci)
|
||||
*/
|
||||
int wusbhc_b_create(struct wusbhc *wusbhc)
|
||||
{
|
||||
int result = 0;
|
||||
struct device *dev = wusbhc->usb_hcd.self.controller;
|
||||
|
||||
result = sysfs_create_group(wusbhc_kobj(wusbhc), &wusbhc_attr_group);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "Cannot register WUSBHC attributes: %d\n",
|
||||
result);
|
||||
goto error_create_attr_group;
|
||||
}
|
||||
|
||||
return 0;
|
||||
error_create_attr_group:
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wusbhc_b_create);
|
||||
|
||||
void wusbhc_b_destroy(struct wusbhc *wusbhc)
|
||||
{
|
||||
wusbhc_pal_unregister(wusbhc);
|
||||
sysfs_remove_group(wusbhc_kobj(wusbhc), &wusbhc_attr_group);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wusbhc_b_destroy);
|
||||
|
||||
void wusbhc_destroy(struct wusbhc *wusbhc)
|
||||
{
|
||||
wusbhc_sec_destroy(wusbhc);
|
||||
wusbhc_rh_destroy(wusbhc);
|
||||
wusbhc_devconnect_destroy(wusbhc);
|
||||
wusbhc_mmcie_destroy(wusbhc);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wusbhc_destroy);
|
||||
|
||||
struct workqueue_struct *wusbd;
|
||||
EXPORT_SYMBOL_GPL(wusbd);
|
||||
|
||||
/*
|
||||
* WUSB Cluster ID allocation map
|
||||
*
|
||||
* Each WUSB bus in a channel is identified with a Cluster Id in the
|
||||
* unauth address pace (WUSB1.0[4.3]). We take the range 0xe0 to 0xff
|
||||
* (that's space for 31 WUSB controllers, as 0xff can't be taken). We
|
||||
* start taking from 0xff, 0xfe, 0xfd... (hence the += or -= 0xff).
|
||||
*
|
||||
* For each one we taken, we pin it in the bitap
|
||||
*/
|
||||
#define CLUSTER_IDS 32
|
||||
static DECLARE_BITMAP(wusb_cluster_id_table, CLUSTER_IDS);
|
||||
static DEFINE_SPINLOCK(wusb_cluster_ids_lock);
|
||||
|
||||
/*
|
||||
* Get a WUSB Cluster ID
|
||||
*
|
||||
* Need to release with wusb_cluster_id_put() when done w/ it.
|
||||
*/
|
||||
/* FIXME: coordinate with the choose_addres() from the USB stack */
|
||||
/* we want to leave the top of the 128 range for cluster addresses and
|
||||
* the bottom for device addresses (as we map them one on one with
|
||||
* ports). */
|
||||
u8 wusb_cluster_id_get(void)
|
||||
{
|
||||
u8 id;
|
||||
spin_lock(&wusb_cluster_ids_lock);
|
||||
id = find_first_zero_bit(wusb_cluster_id_table, CLUSTER_IDS);
|
||||
if (id >= CLUSTER_IDS) {
|
||||
id = 0;
|
||||
goto out;
|
||||
}
|
||||
set_bit(id, wusb_cluster_id_table);
|
||||
id = (u8) 0xff - id;
|
||||
out:
|
||||
spin_unlock(&wusb_cluster_ids_lock);
|
||||
return id;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wusb_cluster_id_get);
|
||||
|
||||
/*
|
||||
* Release a WUSB Cluster ID
|
||||
*
|
||||
* Obtained it with wusb_cluster_id_get()
|
||||
*/
|
||||
void wusb_cluster_id_put(u8 id)
|
||||
{
|
||||
id = 0xff - id;
|
||||
BUG_ON(id >= CLUSTER_IDS);
|
||||
spin_lock(&wusb_cluster_ids_lock);
|
||||
WARN_ON(!test_bit(id, wusb_cluster_id_table));
|
||||
clear_bit(id, wusb_cluster_id_table);
|
||||
spin_unlock(&wusb_cluster_ids_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wusb_cluster_id_put);
|
||||
|
||||
/**
|
||||
* wusbhc_giveback_urb - return an URB to the USB core
|
||||
* @wusbhc: the host controller the URB is from.
|
||||
* @urb: the URB.
|
||||
* @status: the URB's status.
|
||||
*
|
||||
* Return an URB to the USB core doing some additional WUSB specific
|
||||
* processing.
|
||||
*
|
||||
* - After a successful transfer, update the trust timeout timestamp
|
||||
* for the WUSB device.
|
||||
*
|
||||
* - [WUSB] sections 4.13 and 7.5.1 specify the stop retransmission
|
||||
* condition for the WCONNECTACK_IE is that the host has observed
|
||||
* the associated device responding to a control transfer.
|
||||
*/
|
||||
void wusbhc_giveback_urb(struct wusbhc *wusbhc, struct urb *urb, int status)
|
||||
{
|
||||
struct wusb_dev *wusb_dev = __wusb_dev_get_by_usb_dev(wusbhc,
|
||||
urb->dev);
|
||||
|
||||
if (status == 0 && wusb_dev) {
|
||||
wusb_dev->entry_ts = jiffies;
|
||||
|
||||
/* wusbhc_devconnect_acked() can't be called from
|
||||
atomic context so defer it to a work queue. */
|
||||
if (!list_empty(&wusb_dev->cack_node))
|
||||
queue_work(wusbd, &wusb_dev->devconnect_acked_work);
|
||||
else
|
||||
wusb_dev_put(wusb_dev);
|
||||
}
|
||||
|
||||
usb_hcd_giveback_urb(&wusbhc->usb_hcd, urb, status);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wusbhc_giveback_urb);
|
||||
|
||||
/**
|
||||
* wusbhc_reset_all - reset the HC hardware
|
||||
* @wusbhc: the host controller to reset.
|
||||
*
|
||||
* Request a full hardware reset of the chip. This will also reset
|
||||
* the radio controller and any other PALs.
|
||||
*/
|
||||
void wusbhc_reset_all(struct wusbhc *wusbhc)
|
||||
{
|
||||
if (wusbhc->uwb_rc)
|
||||
uwb_rc_reset_all(wusbhc->uwb_rc);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wusbhc_reset_all);
|
||||
|
||||
static struct notifier_block wusb_usb_notifier = {
|
||||
.notifier_call = wusb_usb_ncb,
|
||||
.priority = INT_MAX /* Need to be called first of all */
|
||||
};
|
||||
|
||||
static int __init wusbcore_init(void)
|
||||
{
|
||||
int result;
|
||||
result = wusb_crypto_init();
|
||||
if (result < 0)
|
||||
goto error_crypto_init;
|
||||
/* WQ is singlethread because we need to serialize notifications */
|
||||
wusbd = create_singlethread_workqueue("wusbd");
|
||||
if (wusbd == NULL) {
|
||||
result = -ENOMEM;
|
||||
printk(KERN_ERR "WUSB-core: Cannot create wusbd workqueue\n");
|
||||
goto error_wusbd_create;
|
||||
}
|
||||
usb_register_notify(&wusb_usb_notifier);
|
||||
bitmap_zero(wusb_cluster_id_table, CLUSTER_IDS);
|
||||
set_bit(0, wusb_cluster_id_table); /* reserve Cluster ID 0xff */
|
||||
return 0;
|
||||
|
||||
error_wusbd_create:
|
||||
wusb_crypto_exit();
|
||||
error_crypto_init:
|
||||
return result;
|
||||
|
||||
}
|
||||
module_init(wusbcore_init);
|
||||
|
||||
static void __exit wusbcore_exit(void)
|
||||
{
|
||||
clear_bit(0, wusb_cluster_id_table);
|
||||
if (!bitmap_empty(wusb_cluster_id_table, CLUSTER_IDS)) {
|
||||
char buf[256];
|
||||
bitmap_scnprintf(buf, sizeof(buf), wusb_cluster_id_table,
|
||||
CLUSTER_IDS);
|
||||
printk(KERN_ERR "BUG: WUSB Cluster IDs not released "
|
||||
"on exit: %s\n", buf);
|
||||
WARN_ON(1);
|
||||
}
|
||||
usb_unregister_notify(&wusb_usb_notifier);
|
||||
destroy_workqueue(wusbd);
|
||||
wusb_crypto_exit();
|
||||
}
|
||||
module_exit(wusbcore_exit);
|
||||
|
||||
MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
|
||||
MODULE_DESCRIPTION("Wireless USB core");
|
||||
MODULE_LICENSE("GPL");
|
501
drivers/usb/wusbcore/wusbhc.h
Normal file
501
drivers/usb/wusbcore/wusbhc.h
Normal file
|
@ -0,0 +1,501 @@
|
|||
/*
|
||||
* Wireless USB Host Controller
|
||||
* Common infrastructure for WHCI and HWA WUSB-HC drivers
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2005-2006 Intel Corporation
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* This driver implements parts common to all Wireless USB Host
|
||||
* Controllers (struct wusbhc, embedding a struct usb_hcd) and is used
|
||||
* by:
|
||||
*
|
||||
* - hwahc: HWA, USB-dongle that implements a Wireless USB host
|
||||
* controller, (Wireless USB 1.0 Host-Wire-Adapter specification).
|
||||
*
|
||||
* - whci: WHCI, a PCI card with a wireless host controller
|
||||
* (Wireless Host Controller Interface 1.0 specification).
|
||||
*
|
||||
* Check out the Design-overview.txt file in the source documentation
|
||||
* for other details on the implementation.
|
||||
*
|
||||
* Main blocks:
|
||||
*
|
||||
* rh Root Hub emulation (part of the HCD glue)
|
||||
*
|
||||
* devconnect Handle all the issues related to device connection,
|
||||
* authentication, disconnection, timeout, reseting,
|
||||
* keepalives, etc.
|
||||
*
|
||||
* mmc MMC IE broadcasting handling
|
||||
*
|
||||
* A host controller driver just initializes its stuff and as part of
|
||||
* that, creates a 'struct wusbhc' instance that handles all the
|
||||
* common WUSB mechanisms. Links in the function ops that are specific
|
||||
* to it and then registers the host controller. Ready to run.
|
||||
*/
|
||||
|
||||
#ifndef __WUSBHC_H__
|
||||
#define __WUSBHC_H__
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/uwb.h>
|
||||
#include <linux/usb/wusb.h>
|
||||
|
||||
/*
|
||||
* Time from a WUSB channel stop request to the last transmitted MMC.
|
||||
*
|
||||
* This needs to be > 4.096 ms in case no MMCs can be transmitted in
|
||||
* zone 0.
|
||||
*/
|
||||
#define WUSB_CHANNEL_STOP_DELAY_MS 8
|
||||
#define WUSB_RETRY_COUNT_MAX 15
|
||||
#define WUSB_RETRY_COUNT_INFINITE 0
|
||||
|
||||
/**
|
||||
* Wireless USB device
|
||||
*
|
||||
* Describe a WUSB device connected to the cluster. This struct
|
||||
* belongs to the 'struct wusb_port' it is attached to and it is
|
||||
* responsible for putting and clearing the pointer to it.
|
||||
*
|
||||
* Note this "complements" the 'struct usb_device' that the usb_hcd
|
||||
* keeps for each connected USB device. However, it extends some
|
||||
* information that is not available (there is no hcpriv ptr in it!)
|
||||
* *and* most importantly, it's life cycle is different. It is created
|
||||
* as soon as we get a DN_Connect (connect request notification) from
|
||||
* the device through the WUSB host controller; the USB stack doesn't
|
||||
* create the device until we authenticate it. FIXME: this will
|
||||
* change.
|
||||
*
|
||||
* @bos: This is allocated when the BOS descriptors are read from
|
||||
* the device and freed upon the wusb_dev struct dying.
|
||||
* @wusb_cap_descr: points into @bos, and has been verified to be size
|
||||
* safe.
|
||||
*/
|
||||
struct wusb_dev {
|
||||
struct kref refcnt;
|
||||
struct wusbhc *wusbhc;
|
||||
struct list_head cack_node; /* Connect-Ack list */
|
||||
struct list_head rekey_node; /* GTK rekey list */
|
||||
u8 port_idx;
|
||||
u8 addr;
|
||||
u8 beacon_type:4;
|
||||
struct usb_encryption_descriptor ccm1_etd;
|
||||
struct wusb_ckhdid cdid;
|
||||
unsigned long entry_ts;
|
||||
struct usb_bos_descriptor *bos;
|
||||
struct usb_wireless_cap_descriptor *wusb_cap_descr;
|
||||
struct uwb_mas_bm availability;
|
||||
struct work_struct devconnect_acked_work;
|
||||
struct usb_device *usb_dev;
|
||||
};
|
||||
|
||||
#define WUSB_DEV_ADDR_UNAUTH 0x80
|
||||
|
||||
static inline void wusb_dev_init(struct wusb_dev *wusb_dev)
|
||||
{
|
||||
kref_init(&wusb_dev->refcnt);
|
||||
/* no need to init the cack_node */
|
||||
}
|
||||
|
||||
extern void wusb_dev_destroy(struct kref *_wusb_dev);
|
||||
|
||||
static inline struct wusb_dev *wusb_dev_get(struct wusb_dev *wusb_dev)
|
||||
{
|
||||
kref_get(&wusb_dev->refcnt);
|
||||
return wusb_dev;
|
||||
}
|
||||
|
||||
static inline void wusb_dev_put(struct wusb_dev *wusb_dev)
|
||||
{
|
||||
kref_put(&wusb_dev->refcnt, wusb_dev_destroy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wireless USB Host Controller root hub "fake" ports
|
||||
* (state and device information)
|
||||
*
|
||||
* Wireless USB is wireless, so there are no ports; but we
|
||||
* fake'em. Each RC can connect a max of devices at the same time
|
||||
* (given in the Wireless Adapter descriptor, bNumPorts or WHCI's
|
||||
* caps), referred to in wusbhc->ports_max.
|
||||
*
|
||||
* See rh.c for more information.
|
||||
*
|
||||
* The @status and @change use the same bits as in USB2.0[11.24.2.7],
|
||||
* so we don't have to do much when getting the port's status.
|
||||
*
|
||||
* WUSB1.0[7.1], USB2.0[11.24.2.7.1,fig 11-10],
|
||||
* include/linux/usb_ch9.h (#define USB_PORT_STAT_*)
|
||||
*/
|
||||
struct wusb_port {
|
||||
u16 status;
|
||||
u16 change;
|
||||
struct wusb_dev *wusb_dev; /* connected device's info */
|
||||
u32 ptk_tkid;
|
||||
};
|
||||
|
||||
/**
|
||||
* WUSB Host Controller specifics
|
||||
*
|
||||
* All fields that are common to all Wireless USB controller types
|
||||
* (HWA and WHCI) are grouped here. Host Controller
|
||||
* functions/operations that only deal with general Wireless USB HC
|
||||
* issues use this data type to refer to the host.
|
||||
*
|
||||
* @usb_hcd Instantiation of a USB host controller
|
||||
* (initialized by upper layer [HWA=HC or WHCI].
|
||||
*
|
||||
* @dev Device that implements this; initialized by the
|
||||
* upper layer (HWA-HC, WHCI...); this device should
|
||||
* have a refcount.
|
||||
*
|
||||
* @trust_timeout After this time without hearing for device
|
||||
* activity, we consider the device gone and we have to
|
||||
* re-authenticate.
|
||||
*
|
||||
* Can be accessed w/o locking--however, read to a
|
||||
* local variable then use.
|
||||
*
|
||||
* @chid WUSB Cluster Host ID: this is supposed to be a
|
||||
* unique value that doesn't change across reboots (so
|
||||
* that your devices do not require re-association).
|
||||
*
|
||||
* Read/Write protected by @mutex
|
||||
*
|
||||
* @dev_info This array has ports_max elements. It is used to
|
||||
* give the HC information about the WUSB devices (see
|
||||
* 'struct wusb_dev_info').
|
||||
*
|
||||
* For HWA we need to allocate it in heap; for WHCI it
|
||||
* needs to be permanently mapped, so we keep it for
|
||||
* both and make it easy. Call wusbhc->dev_info_set()
|
||||
* to update an entry.
|
||||
*
|
||||
* @ports_max Number of simultaneous device connections (fake
|
||||
* ports) this HC will take. Read-only.
|
||||
*
|
||||
* @port Array of port status for each fake root port. Guaranteed to
|
||||
* always be the same length during device existence
|
||||
* [this allows for some unlocked but referenced reading].
|
||||
*
|
||||
* @mmcies_max Max number of Information Elements this HC can send
|
||||
* in its MMC. Read-only.
|
||||
*
|
||||
* @start Start the WUSB channel.
|
||||
*
|
||||
* @stop Stop the WUSB channel after the specified number of
|
||||
* milliseconds. Channel Stop IEs should be transmitted
|
||||
* as required by [WUSB] 4.16.2.1.
|
||||
*
|
||||
* @mmcie_add HC specific operation (WHCI or HWA) for adding an
|
||||
* MMCIE.
|
||||
*
|
||||
* @mmcie_rm HC specific operation (WHCI or HWA) for removing an
|
||||
* MMCIE.
|
||||
*
|
||||
* @set_ptk: Set the PTK and enable encryption for a device. Or, if
|
||||
* the supplied key is NULL, disable encryption for that
|
||||
* device.
|
||||
*
|
||||
* @set_gtk: Set the GTK to be used for all future broadcast packets
|
||||
* (i.e., MMCs). With some hardware, setting the GTK may start
|
||||
* MMC transmission.
|
||||
*
|
||||
* NOTE:
|
||||
*
|
||||
* - If wusb_dev->usb_dev is not NULL, then usb_dev is valid
|
||||
* (wusb_dev has a refcount on it). Likewise, if usb_dev->wusb_dev
|
||||
* is not NULL, usb_dev->wusb_dev is valid (usb_dev keeps a
|
||||
* refcount on it).
|
||||
*
|
||||
* Most of the times when you need to use it, it will be non-NULL,
|
||||
* so there is no real need to check for it (wusb_dev will
|
||||
* disappear before usb_dev).
|
||||
*
|
||||
* - The following fields need to be filled out before calling
|
||||
* wusbhc_create(): ports_max, mmcies_max, mmcie_{add,rm}.
|
||||
*
|
||||
* - there is no wusbhc_init() method, we do everything in
|
||||
* wusbhc_create().
|
||||
*
|
||||
* - Creation is done in two phases, wusbhc_create() and
|
||||
* wusbhc_create_b(); b are the parts that need to be called after
|
||||
* calling usb_hcd_add(&wusbhc->usb_hcd).
|
||||
*/
|
||||
struct wusbhc {
|
||||
struct usb_hcd usb_hcd; /* HAS TO BE 1st */
|
||||
struct device *dev;
|
||||
struct uwb_rc *uwb_rc;
|
||||
struct uwb_pal pal;
|
||||
|
||||
unsigned trust_timeout; /* in jiffies */
|
||||
struct wusb_ckhdid chid;
|
||||
uint8_t phy_rate;
|
||||
uint8_t dnts_num_slots;
|
||||
uint8_t dnts_interval;
|
||||
uint8_t retry_count;
|
||||
struct wuie_host_info *wuie_host_info;
|
||||
|
||||
struct mutex mutex; /* locks everything else */
|
||||
u16 cluster_id; /* Wireless USB Cluster ID */
|
||||
struct wusb_port *port; /* Fake port status handling */
|
||||
struct wusb_dev_info *dev_info; /* for Set Device Info mgmt */
|
||||
u8 ports_max;
|
||||
unsigned active:1; /* currently xmit'ing MMCs */
|
||||
struct wuie_keep_alive keep_alive_ie; /* protected by mutex */
|
||||
struct delayed_work keep_alive_timer;
|
||||
struct list_head cack_list; /* Connect acknowledging */
|
||||
size_t cack_count; /* protected by 'mutex' */
|
||||
struct wuie_connect_ack cack_ie;
|
||||
struct uwb_rsv *rsv; /* cluster bandwidth reservation */
|
||||
|
||||
struct mutex mmcie_mutex; /* MMC WUIE handling */
|
||||
struct wuie_hdr **mmcie; /* WUIE array */
|
||||
u8 mmcies_max;
|
||||
/* FIXME: make wusbhc_ops? */
|
||||
int (*start)(struct wusbhc *wusbhc);
|
||||
void (*stop)(struct wusbhc *wusbhc, int delay);
|
||||
int (*mmcie_add)(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt,
|
||||
u8 handle, struct wuie_hdr *wuie);
|
||||
int (*mmcie_rm)(struct wusbhc *wusbhc, u8 handle);
|
||||
int (*dev_info_set)(struct wusbhc *, struct wusb_dev *wusb_dev);
|
||||
int (*bwa_set)(struct wusbhc *wusbhc, s8 stream_index,
|
||||
const struct uwb_mas_bm *);
|
||||
int (*set_ptk)(struct wusbhc *wusbhc, u8 port_idx,
|
||||
u32 tkid, const void *key, size_t key_size);
|
||||
int (*set_gtk)(struct wusbhc *wusbhc,
|
||||
u32 tkid, const void *key, size_t key_size);
|
||||
int (*set_num_dnts)(struct wusbhc *wusbhc, u8 interval, u8 slots);
|
||||
|
||||
struct {
|
||||
struct usb_key_descriptor descr;
|
||||
u8 data[16]; /* GTK key data */
|
||||
} __attribute__((packed)) gtk;
|
||||
u8 gtk_index;
|
||||
u32 gtk_tkid;
|
||||
|
||||
/* workqueue for WUSB security related tasks. */
|
||||
struct workqueue_struct *wq_security;
|
||||
struct work_struct gtk_rekey_work;
|
||||
|
||||
struct usb_encryption_descriptor *ccm1_etd;
|
||||
};
|
||||
|
||||
#define usb_hcd_to_wusbhc(u) container_of((u), struct wusbhc, usb_hcd)
|
||||
|
||||
|
||||
extern int wusbhc_create(struct wusbhc *);
|
||||
extern int wusbhc_b_create(struct wusbhc *);
|
||||
extern void wusbhc_b_destroy(struct wusbhc *);
|
||||
extern void wusbhc_destroy(struct wusbhc *);
|
||||
extern int wusb_dev_sysfs_add(struct wusbhc *, struct usb_device *,
|
||||
struct wusb_dev *);
|
||||
extern void wusb_dev_sysfs_rm(struct wusb_dev *);
|
||||
extern int wusbhc_sec_create(struct wusbhc *);
|
||||
extern int wusbhc_sec_start(struct wusbhc *);
|
||||
extern void wusbhc_sec_stop(struct wusbhc *);
|
||||
extern void wusbhc_sec_destroy(struct wusbhc *);
|
||||
extern void wusbhc_giveback_urb(struct wusbhc *wusbhc, struct urb *urb,
|
||||
int status);
|
||||
void wusbhc_reset_all(struct wusbhc *wusbhc);
|
||||
|
||||
int wusbhc_pal_register(struct wusbhc *wusbhc);
|
||||
void wusbhc_pal_unregister(struct wusbhc *wusbhc);
|
||||
|
||||
/*
|
||||
* Return @usb_dev's @usb_hcd (properly referenced) or NULL if gone
|
||||
*
|
||||
* @usb_dev: USB device, UNLOCKED and referenced (or otherwise, safe ptr)
|
||||
*
|
||||
* This is a safe assumption as @usb_dev->bus is referenced all the
|
||||
* time during the @usb_dev life cycle.
|
||||
*/
|
||||
static inline
|
||||
struct usb_hcd *usb_hcd_get_by_usb_dev(struct usb_device *usb_dev)
|
||||
{
|
||||
struct usb_hcd *usb_hcd;
|
||||
usb_hcd = container_of(usb_dev->bus, struct usb_hcd, self);
|
||||
return usb_get_hcd(usb_hcd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Increment the reference count on a wusbhc.
|
||||
*
|
||||
* @wusbhc's life cycle is identical to that of the underlying usb_hcd.
|
||||
*/
|
||||
static inline struct wusbhc *wusbhc_get(struct wusbhc *wusbhc)
|
||||
{
|
||||
return usb_get_hcd(&wusbhc->usb_hcd) ? wusbhc : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the wusbhc associated to a @usb_dev
|
||||
*
|
||||
* @usb_dev: USB device, UNLOCKED and referenced (or otherwise, safe ptr)
|
||||
*
|
||||
* @returns: wusbhc for @usb_dev; NULL if the @usb_dev is being torn down.
|
||||
* WARNING: referenced at the usb_hcd level, unlocked
|
||||
*
|
||||
* FIXME: move offline
|
||||
*/
|
||||
static inline struct wusbhc *wusbhc_get_by_usb_dev(struct usb_device *usb_dev)
|
||||
{
|
||||
struct wusbhc *wusbhc = NULL;
|
||||
struct usb_hcd *usb_hcd;
|
||||
if (usb_dev->devnum > 1 && !usb_dev->wusb) {
|
||||
/* but root hubs */
|
||||
dev_err(&usb_dev->dev, "devnum %d wusb %d\n", usb_dev->devnum,
|
||||
usb_dev->wusb);
|
||||
BUG_ON(usb_dev->devnum > 1 && !usb_dev->wusb);
|
||||
}
|
||||
usb_hcd = usb_hcd_get_by_usb_dev(usb_dev);
|
||||
if (usb_hcd == NULL)
|
||||
return NULL;
|
||||
BUG_ON(usb_hcd->wireless == 0);
|
||||
return wusbhc = usb_hcd_to_wusbhc(usb_hcd);
|
||||
}
|
||||
|
||||
|
||||
static inline void wusbhc_put(struct wusbhc *wusbhc)
|
||||
{
|
||||
usb_put_hcd(&wusbhc->usb_hcd);
|
||||
}
|
||||
|
||||
int wusbhc_start(struct wusbhc *wusbhc);
|
||||
void wusbhc_stop(struct wusbhc *wusbhc);
|
||||
extern int wusbhc_chid_set(struct wusbhc *, const struct wusb_ckhdid *);
|
||||
|
||||
/* Device connect handling */
|
||||
extern int wusbhc_devconnect_create(struct wusbhc *);
|
||||
extern void wusbhc_devconnect_destroy(struct wusbhc *);
|
||||
extern int wusbhc_devconnect_start(struct wusbhc *wusbhc);
|
||||
extern void wusbhc_devconnect_stop(struct wusbhc *wusbhc);
|
||||
extern void wusbhc_handle_dn(struct wusbhc *, u8 srcaddr,
|
||||
struct wusb_dn_hdr *dn_hdr, size_t size);
|
||||
extern void __wusbhc_dev_disable(struct wusbhc *wusbhc, u8 port);
|
||||
extern int wusb_usb_ncb(struct notifier_block *nb, unsigned long val,
|
||||
void *priv);
|
||||
extern int wusb_set_dev_addr(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev,
|
||||
u8 addr);
|
||||
|
||||
/* Wireless USB fake Root Hub methods */
|
||||
extern int wusbhc_rh_create(struct wusbhc *);
|
||||
extern void wusbhc_rh_destroy(struct wusbhc *);
|
||||
|
||||
extern int wusbhc_rh_status_data(struct usb_hcd *, char *);
|
||||
extern int wusbhc_rh_control(struct usb_hcd *, u16, u16, u16, char *, u16);
|
||||
extern int wusbhc_rh_start_port_reset(struct usb_hcd *, unsigned);
|
||||
|
||||
/* MMC handling */
|
||||
extern int wusbhc_mmcie_create(struct wusbhc *);
|
||||
extern void wusbhc_mmcie_destroy(struct wusbhc *);
|
||||
extern int wusbhc_mmcie_set(struct wusbhc *, u8 interval, u8 repeat_cnt,
|
||||
struct wuie_hdr *);
|
||||
extern void wusbhc_mmcie_rm(struct wusbhc *, struct wuie_hdr *);
|
||||
|
||||
/* Bandwidth reservation */
|
||||
int wusbhc_rsv_establish(struct wusbhc *wusbhc);
|
||||
void wusbhc_rsv_terminate(struct wusbhc *wusbhc);
|
||||
|
||||
/*
|
||||
* I've always said
|
||||
* I wanted a wedding in a church...
|
||||
*
|
||||
* but lately I've been thinking about
|
||||
* the Botanical Gardens.
|
||||
*
|
||||
* We could do it by the tulips.
|
||||
* It'll be beautiful
|
||||
*
|
||||
* --Security!
|
||||
*/
|
||||
extern int wusb_dev_sec_add(struct wusbhc *, struct usb_device *,
|
||||
struct wusb_dev *);
|
||||
extern void wusb_dev_sec_rm(struct wusb_dev *) ;
|
||||
extern int wusb_dev_4way_handshake(struct wusbhc *, struct wusb_dev *,
|
||||
struct wusb_ckhdid *ck);
|
||||
void wusbhc_gtk_rekey(struct wusbhc *wusbhc);
|
||||
int wusb_dev_update_address(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev);
|
||||
|
||||
|
||||
/* WUSB Cluster ID handling */
|
||||
extern u8 wusb_cluster_id_get(void);
|
||||
extern void wusb_cluster_id_put(u8);
|
||||
|
||||
/*
|
||||
* wusb_port_by_idx - return the port associated to a zero-based port index
|
||||
*
|
||||
* NOTE: valid without locking as long as wusbhc is referenced (as the
|
||||
* number of ports doesn't change). The data pointed to has to
|
||||
* be verified though :)
|
||||
*/
|
||||
static inline struct wusb_port *wusb_port_by_idx(struct wusbhc *wusbhc,
|
||||
u8 port_idx)
|
||||
{
|
||||
return &wusbhc->port[port_idx];
|
||||
}
|
||||
|
||||
/*
|
||||
* wusb_port_no_to_idx - Convert port number (per usb_dev->portnum) to
|
||||
* a port_idx.
|
||||
*
|
||||
* USB stack USB ports are 1 based!!
|
||||
*
|
||||
* NOTE: only valid for WUSB devices!!!
|
||||
*/
|
||||
static inline u8 wusb_port_no_to_idx(u8 port_no)
|
||||
{
|
||||
return port_no - 1;
|
||||
}
|
||||
|
||||
extern struct wusb_dev *__wusb_dev_get_by_usb_dev(struct wusbhc *,
|
||||
struct usb_device *);
|
||||
|
||||
/*
|
||||
* Return a referenced wusb_dev given a @usb_dev
|
||||
*
|
||||
* Returns NULL if the usb_dev is being torn down.
|
||||
*
|
||||
* FIXME: move offline
|
||||
*/
|
||||
static inline
|
||||
struct wusb_dev *wusb_dev_get_by_usb_dev(struct usb_device *usb_dev)
|
||||
{
|
||||
struct wusbhc *wusbhc;
|
||||
struct wusb_dev *wusb_dev;
|
||||
wusbhc = wusbhc_get_by_usb_dev(usb_dev);
|
||||
if (wusbhc == NULL)
|
||||
return NULL;
|
||||
mutex_lock(&wusbhc->mutex);
|
||||
wusb_dev = __wusb_dev_get_by_usb_dev(wusbhc, usb_dev);
|
||||
mutex_unlock(&wusbhc->mutex);
|
||||
wusbhc_put(wusbhc);
|
||||
return wusb_dev;
|
||||
}
|
||||
|
||||
/* Misc */
|
||||
|
||||
extern struct workqueue_struct *wusbd;
|
||||
#endif /* #ifndef __WUSBHC_H__ */
|
Loading…
Add table
Add a link
Reference in a new issue