/*
* 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)
{
kfree(wusb_dev);
}
static struct wusb_dev *wusb_dev_alloc(struct wusbhc *wusbhc)
{
struct wusb_dev *wusb_dev;
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);
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)
+ cnt * sizeof(cack_ie->blk[0]);
}
/*
* Register a new device that wants to connect
*
* A new device wants to connect, so we add it to the Connect-Ack
* list. We give it an address in the unauthorized range (bit 8 set);
* user space will have to drive authorization further on.
*
* @dev_addr: address to use for the device (which is also the port
* number).
*
* @wusbhc->mutex must be taken
*/
static struct wusb_dev *wusbhc_cack_add(struct wusbhc *wusbhc,
struct wusb_dn_connect *dnc,
const char *pr_cdid, u8 port_idx)
{
struct device *dev = wusbhc->dev;
struct wusb_dev *wusb_dev;
int new_connection = wusb_dn_connect_new_connection(dnc);
u8 dev_addr;
int result;
/* Is it registered already? */
list_for_each_entry(wusb_dev, &wusbhc->cack_list, cack_node)
if (!memcmp(&wusb_dev->cdid, &dnc->CDID,
sizeof(wusb_dev->cdid)))
return wusb_dev;
/* We don't have it, create an entry, register it */
wusb_dev = wusb_dev_alloc(wusbhc);
if (wusb_dev == NULL)
return NULL;
wusb_dev_init(wusb_dev);
wusb_dev->cdid = dnc->CDID;
wusb_dev->port_idx