/*
* WUSB Wire Adapter: Control/Data Streaming Interface (WUSB[8])
* Device Connect handling
*
* 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
* FIXME: this file needs to be broken up, it's grown too big
*
*
* WUSB1.0[7.1, 7.5.1, ]
*
* WUSB device connection is kind of messy. Some background:
*
* When a device wants to connect it scans the UWB radio channels
* looking for a WUSB Channel; a WUSB channel is defined by MMCs
* (Micro Managed Commands or something like that) [see
* Design-overview for more on this] .
*
* So, device scans the radio, finds MMCs and thus a host and checks
* when the next DNTS is. It sends a Device Notification Connect
* (DN_Connect); the host picks it up (through nep.c and notif.c, ends
* up in wusb_devconnect_ack(), which creates a wusb_dev structure in
* wusbhc->port[port_number].wusb_dev), assigns an unauth address
* to the device (this means from 0x80 to 0xfe) and sends, in the MMC
* a Connect Ack Information Element (ConnAck IE).
*
* So now the device now has a WUSB address. From now on, we use
* that to talk to it in the RPipes.
*
* ASSUMPTIONS:
*
* - We use the the as device address the port number where it is
* connected (port 0 doesn't exist). For unauth, it is 128 + that.
*
* ROADMAP:
*
* This file contains the logic for doing that--entry points:
*
* wusb_devconnect_ack() Ack a device until _acked() called.
* Called by notif.c:wusb_handle_dn_connect()
* when a DN_Connect is received.
*
* wusb_devconnect_acked() Ack done, release resources.
*
* wusb_handle_dn_alive() Called by notif.c:wusb_handle_dn()
* for processing a DN_Alive pong from a device.
*
* wusb_handle_dn_disconnect()Called by notif.c:wusb_handle_dn() to
* process a disconenct request from a
* device.
*
* __wusb_dev_disable() Called by rh.c:wusbhc_rh_clear_port_feat() when
* disabling a port.
*
* wusb_devconnect_create() Called when creating the host by
* lc.c:wusbhc_create().
*
* wusb_devconnect_destroy() Cleanup called removing the host. Called
* by lc.c:wusbhc_destroy().
*
* Each Wireless USB host maintains a list of DN_Connect requests
* (actually we maintain a list of pending Connect Acks, the
* wusbhc->ca_list).
*
* LIFE CYCLE OF port->wusb_dev
*
* Before the @wusbhc structure put()s the reference it owns for
* port->wusb_dev [and clean the wusb_dev pointer], it needs to
* lock @wusbhc->mutex.
*/
#include <linux/jiffies.h>
#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/export.h>
#include "wusbhc.h"
static void wusbhc_devconnect_acked_work(struct work_struct *work);
static void wusb_dev_free(struct wusb_dev *wusb_dev)
{
if (wusb_dev) {
kfree(wusb_dev->set_gtk_req);
usb_free_urb(wusb_dev->set_gtk_urb);
kfree(wusb_dev);
}
}
static struct wusb_dev *wusb_dev_alloc(struct wusbhc *wusbhc)
{
struct wusb_dev *wusb_dev;
struct urb *urb;
struct usb_ctrlrequest *req;
wusb_dev = kzalloc(sizeof(*wusb_dev), GFP_KERNEL);
if (wusb_dev == NULL)
goto err;
wusb_dev->wusbhc = wusbhc;
INIT_WORK(&wusb_dev->devconnect_acked_work, wusbhc_devconnect_acked_work);
urb = usb_alloc_urb(0, GFP_KERNEL);
if (urb == NULL)
goto err;
wusb_dev->set_gtk_urb = urb;
req = kmalloc(sizeof(*req), GFP_KERNEL);
if (req == NULL)
goto err;
wusb_dev->set_gtk_req = req;
req->bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
req->bRequest = USB_REQ_SET_DESCRIPTOR;
req->wValue = cpu_to_le16(USB_DT_KEY << 8 | wusbhc->gtk_index);
req->wIndex = 0;
req->wLength = cpu_to_le16(wusbhc->gtk.descr.bLength);
return wusb_dev;
err:
wusb_dev_free(wusb_dev);
return NULL;
}
/*
* Using the Connect-Ack list, fill out the @wusbhc Connect-Ack WUSB IE
* properly so that it can be added to the MMC.
*
* We just get the @wusbhc->ca_list and fill out the first four ones or
* less (per-spec WUSB1.0[7.5, before T7-38). If the ConnectAck WUSB
* IE is not allocated, we alloc it.
*
* @wusbhc->mutex must be taken
*/
static void wusbhc_fill_cack_ie(struct wusbhc *wusbhc)
{
unsigned cnt;
struct wusb_dev *dev_itr;
struct wuie_connect_ack *cack_ie;
cack_ie = &wusbhc->cack_ie;
cnt = 0;
list_for_each_entry(dev_itr, &wusbhc->cack_list, cack_node) {
cack_ie->blk[cnt].CDID = dev_itr->cdid;
cack_ie->blk[cnt].bDeviceAddress = dev_itr->addr;
if (++cnt >= WUIE_ELT_MAX)
break;
}
cack_ie->hdr.bLength = sizeof(cack_ie->hdr)