cregit-Linux how code gets into the kernel

Release 4.7 drivers/uwb/hwa-rc.c

Directory: drivers/uwb
/*
 * WUSB Host Wire Adapter: Radio Control Interface (WUSB[8.6])
 * Radio Control command/event transport
 *
 * 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.
 *
 *
 * Initialize the Radio Control interface Driver.
 *
 * For each device probed, creates an 'struct hwarc' which contains
 * just the representation of the UWB Radio Controller, and the logic
 * for reading notifications and passing them to the UWB Core.
 *
 * So we initialize all of those, register the UWB Radio Controller
 * and setup the notification/event handle to pipe the notifications
 * to the UWB management Daemon.
 *
 * Command and event filtering.
 *
 * This is the driver for the Radio Control Interface described in WUSB
 * 1.0. The core UWB module assumes that all drivers are compliant to the
 * WHCI 0.95 specification. We thus create a filter that parses all
 * incoming messages from the (WUSB 1.0) device and manipulate them to
 * conform to the WHCI 0.95 specification. Similarly, outgoing messages
 * are parsed and manipulated to conform to the WUSB 1.0 compliant messages
 * that the device expects. Only a few messages are affected:
 * Affected events:
 *    UWB_RC_EVT_BEACON
 *    UWB_RC_EVT_BP_SLOT_CHANGE
 *    UWB_RC_EVT_DRP_AVAIL
 *    UWB_RC_EVT_DRP
 * Affected commands:
 *    UWB_RC_CMD_SCAN
 *    UWB_RC_CMD_SET_DRP_IE
 *
 *
 *
 */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/usb/wusb.h>
#include <linux/usb/wusb-wa.h>
#include <linux/uwb.h>

#include "uwb-internal.h"

/* The device uses commands and events from the WHCI specification, although
 * reporting itself as WUSB compliant. */

#define WUSB_QUIRK_WHCI_CMD_EVT		0x01

/**
 * Descriptor for an instance of the UWB Radio Control Driver that
 * attaches to the RCI interface of the Host Wired Adapter.
 *
 * Unless there is a lock specific to the 'data members', all access
 * is protected by uwb_rc->mutex.
 *
 * The NEEP (Notification/Event EndPoint) URB (@neep_urb) writes to
 * @rd_buffer. Note there is no locking because it is perfectly (heh!)
 * serialized--probe() submits an URB, callback is called, processes
 * the data (synchronously), submits another URB, and so on. There is
 * no concurrent access to the buffer.
 */

struct hwarc {
	
struct usb_device *usb_dev;
	
struct usb_interface *usb_iface;
	
struct uwb_rc *uwb_rc;		/* UWB host controller */
	
struct urb *neep_urb;		/* Notification endpoint handling */
	
struct edc neep_edc;
	
void *rd_buffer;		/* NEEP read buffer */
};


/* Beacon received notification (WUSB 1.0 [8.6.3.2]) */

struct uwb_rc_evt_beacon_WUSB_0100 {
	
struct uwb_rceb rceb;
	
u8	bChannelNumber;
	
__le16	wBPSTOffset;
	
u8	bLQI;
	
u8	bRSSI;
	
__le16	wBeaconInfoLength;
	
u8	BeaconInfo[];
} __attribute__((packed));

/**
 * Filter WUSB 1.0 BEACON RCV notification to be WHCI 0.95
 *
 * @header: the incoming event
 * @buf_size: size of buffer containing incoming event
 * @new_size: size of event after filtering completed
 *
 * The WHCI 0.95 spec has a "Beacon Type" field. This value is unknown at
 * the time we receive the beacon from WUSB so we just set it to
 * UWB_RC_BEACON_TYPE_NEIGHBOR as a default.
 * The solution below allocates memory upon receipt of every beacon from a
 * WUSB device. This will deteriorate performance. What is the right way to
 * do this?
 */

static int hwarc_filter_evt_beacon_WUSB_0100(struct uwb_rc *rc, struct uwb_rceb **header, const size_t buf_size, size_t *new_size) { struct uwb_rc_evt_beacon_WUSB_0100 *be; struct uwb_rc_evt_beacon *newbe; size_t bytes_left, ielength; struct device *dev = &rc->uwb_dev.dev; be = container_of(*header, struct uwb_rc_evt_beacon_WUSB_0100, rceb); bytes_left = buf_size; if (bytes_left < sizeof(*be)) { dev_err(dev, "Beacon Received Notification: Not enough data " "to decode for filtering (%zu vs %zu bytes needed)\n", bytes_left, sizeof(*be)); return -EINVAL; } bytes_left -= sizeof(*be); ielength = le16_to_cpu(be->wBeaconInfoLength); if (bytes_left < ielength) { dev_err(dev, "Beacon Received Notification: Not enough data " "to decode IEs (%zu vs %zu bytes needed)\n", bytes_left, ielength); return -EINVAL; } newbe = kzalloc(sizeof(*newbe) + ielength, GFP_ATOMIC); if (newbe == NULL) return -ENOMEM; newbe->rceb = be->rceb; newbe->bChannelNumber = be->bChannelNumber; newbe->bBeaconType = UWB_RC_BEACON_TYPE_NEIGHBOR; newbe->wBPSTOffset = be->wBPSTOffset; newbe->bLQI = be->bLQI; newbe->bRSSI = be->bRSSI; newbe->wBeaconInfoLength = be->wBeaconInfoLength; memcpy(newbe->BeaconInfo, be->BeaconInfo, ielength); *header = &newbe->rceb; *new_size = sizeof(*newbe) + ielength; return 1; /* calling function will free memory */ }

Contributors

PersonTokensPropCommitsCommitProp
inaky perez-gonzalezinaky perez-gonzalez257100.00%1100.00%
Total257100.00%1100.00%

/* DRP Availability change notification (WUSB 1.0 [8.6.3.8]) */ struct uwb_rc_evt_drp_avail_WUSB_0100 { struct uwb_rceb rceb; __le16 wIELength; u8 IEData[]; } __attribute__((packed)); /** * Filter WUSB 1.0 DRP AVAILABILITY CHANGE notification to be WHCI 0.95 * * @header: the incoming event * @buf_size: size of buffer containing incoming event * @new_size: size of event after filtering completed */
static int hwarc_filter_evt_drp_avail_WUSB_0100(struct uwb_rc *rc, struct uwb_rceb **header, const size_t buf_size, size_t *new_size) { struct uwb_rc_evt_drp_avail_WUSB_0100 *da; struct uwb_rc_evt_drp_avail *newda; struct uwb_ie_hdr *ie_hdr; size_t bytes_left, ielength; struct device *dev = &rc->uwb_dev.dev; da = container_of(*header, struct uwb_rc_evt_drp_avail_WUSB_0100, rceb); bytes_left = buf_size; if (bytes_left < sizeof(*da)) { dev_err(dev, "Not enough data to decode DRP Avail " "Notification for filtering. Expected %zu, " "received %zu.\n", (size_t)sizeof(*da), bytes_left); return -EINVAL; } bytes_left -= sizeof(*da); ielength = le16_to_cpu(da->wIELength); if (bytes_left < ielength) { dev_err(dev, "DRP Avail Notification filter: IE length " "[%zu bytes] does not match actual length " "[%zu bytes].\n", ielength, bytes_left); return -EINVAL; } if (ielength < sizeof(*ie_hdr)) { dev_err(dev, "DRP Avail Notification filter: Not enough " "data to decode IE [%zu bytes, %zu needed]\n", ielength, sizeof(*ie_hdr)); return -EINVAL; } ie_hdr = (void *) da->IEData; if (ie_hdr->length > 32) { dev_err(dev, "DRP Availability Change event has unexpected " "length for filtering. Expected < 32 bytes, " "got %zu bytes.\n", (size_t)ie_hdr->length); return -EINVAL; } newda = kzalloc(sizeof(*newda), GFP_ATOMIC); if (newda == NULL) return -ENOMEM; newda->rceb = da->rceb; memcpy(newda->bmp, (u8 *) ie_hdr + sizeof(*ie_hdr), ie_hdr->length); *header = &newda->rceb; *new_size = sizeof(*newda); return 1; /* calling function will free memory */ }

Contributors

PersonTokensPropCommitsCommitProp
inaky perez-gonzalezinaky perez-gonzalez299100.00%1100.00%
Total299100.00%1100.00%

/* DRP notification (WUSB 1.0 [8.6.3.9]) */ struct uwb_rc_evt_drp_WUSB_0100 { struct uwb_rceb rceb; struct uwb_dev_addr wSrcAddr; u8 bExplicit; __le16 wIELength; u8 IEData[]; } __attribute__((packed)); /** * Filter WUSB 1.0 DRP Notification to be WHCI 0.95 * * @header: the incoming event * @buf_size: size of buffer containing incoming event * @new_size: size of event after filtering completed * * It is hard to manage DRP reservations without having a Reason code. * Unfortunately there is none in the WUSB spec. We just set the default to * DRP IE RECEIVED. * We do not currently use the bBeaconSlotNumber value, so we set this to * zero for now. */
static int hwarc_filter_evt_drp_WUSB_0100(struct uwb_rc *rc, struct uwb_rceb **header, const size_t buf_size, size_t *new_size) { struct uwb_rc_evt_drp_WUSB_0100 *drpev; struct uwb_rc_evt_drp *newdrpev; size_t bytes_left, ielength; struct device *dev = &rc->uwb_dev.dev; drpev = container_of(*header, struct uwb_rc_evt_drp_WUSB_0100, rceb); bytes_left = buf_size; if (bytes_left < sizeof(*drpev)) { dev_err(dev, "Not enough data to decode DRP Notification " "for filtering. Expected %zu, received %zu.\n", (size_t)sizeof(*drpev), bytes_left); return -EINVAL; } ielength = le16_to_cpu(drpev->wIELength); bytes_left -= sizeof(*drpev); if (bytes_left < ielength) { dev_err(dev, "DRP Notification filter: header length [%zu " "bytes] does not match actual length [%zu " "bytes].\n", ielength, bytes_left); return -EINVAL; } newdrpev = kzalloc(sizeof(*newdrpev) + ielength, GFP_ATOMIC); if (newdrpev == NULL) return -ENOMEM; newdrpev->rceb = drpev->rceb; newdrpev->src_addr = drpev->wSrcAddr; newdrpev->reason = UWB_DRP_NOTIF_DRP_IE_RCVD; newdrpev->beacon_slot_number = 0; newdrpev->ie_length = drpev->wIELength; memcpy(newdrpev->ie_data, drpev->IEData, ielength); *header = &newdrpev->rceb; *new_size = sizeof(*newdrpev) + ielength; return 1; /* calling function will free memory */ }

Contributors

PersonTokensPropCommitsCommitProp
inaky perez-gonzalezinaky perez-gonzalez243100.00%1100.00%
Total243100.00%1100.00%

/* Scan Command (WUSB 1.0 [8.6.2.5]) */ struct uwb_rc_cmd_scan_WUSB_0100 { struct uwb_rccb rccb; u8 bChannelNumber; u8 bScanState; } __attribute__((packed)); /** * Filter WHCI 0.95 SCAN command to be WUSB 1.0 SCAN command * * @header: command sent to device (compliant to WHCI 0.95) * @size: size of command sent to device * * We only reduce the size by two bytes because the WUSB 1.0 scan command * does not have the last field (wStarttime). Also, make sure we don't send * the device an unexpected scan type. */
static int hwarc_filter_cmd_scan_WUSB_0100(struct uwb_rc *rc, struct uwb_rccb **header, size_t *size) { struct uwb_rc_cmd_scan *sc; sc = container_of(*header, struct uwb_rc_cmd_scan, rccb); if (sc->bScanState == UWB_SCAN_ONLY_STARTTIME) sc->bScanState = UWB_SCAN_ONLY; /* Don't send the last two bytes. */ *size -= 2; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
inaky perez-gonzalezinaky perez-gonzalez62100.00%1100.00%
Total62100.00%1100.00%

/* SET DRP IE command (WUSB 1.0 [8.6.2.7]) */ struct uwb_rc_cmd_set_drp_ie_WUSB_0100 { struct uwb_rccb rccb; u8 bExplicit; __le16 wIELength; struct uwb_ie_drp IEData[]; } __attribute__((packed)); /** * Filter WHCI 0.95 SET DRP IE command to be WUSB 1.0 SET DRP IE command * * @header: command sent to device (compliant to WHCI 0.95) * @size: size of command sent to device * * WUSB has an extra bExplicit field - we assume always explicit * negotiation so this field is set. The command expected by the device is * thus larger than the one prepared by the driver so we need to * reallocate memory to accommodate this. * We trust the driver to send us the correct data so no checking is done * on incoming data - evn though it is variable length. */
static int hwarc_filter_cmd_set_drp_ie_WUSB_0100(struct uwb_rc *rc, struct uwb_rccb **header, size_t *size) { struct uwb_rc_cmd_set_drp_ie *orgcmd; struct uwb_rc_cmd_set_drp_ie_WUSB_0100 *cmd; size_t ielength; orgcmd = container_of(*header, struct uwb_rc_cmd_set_drp_ie, rccb); ielength = le16_to_cpu(orgcmd->wIELength); cmd = kzalloc(sizeof(*cmd) + ielength, GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->rccb = orgcmd->rccb; cmd->bExplicit = 0; cmd->wIELength = orgcmd->wIELength; memcpy(cmd->IEData, orgcmd->IEData, ielength); *header = &cmd->rccb; *size = sizeof(*cmd) + ielength; return 1; /* calling function will free memory */ }

Contributors

PersonTokensPropCommitsCommitProp
inaky perez-gonzalezinaky perez-gonzalez139100.00%1100.00%
Total139100.00%1100.00%

/** * Filter data from WHCI driver to WUSB device * * @header: WHCI 0.95 compliant command from driver * @size: length of command * * The routine managing commands to the device (uwb_rc_cmd()) will call the * filtering function pointer (if it exists) before it passes any data to * the device. At this time the command has been formatted according to * WHCI 0.95 and is ready to be sent to the device. * * The filter function will be provided with the current command and its * length. The function will manipulate the command if necessary and * potentially reallocate memory for a command that needed more memory that * the given command. If new memory was created the function will return 1 * to indicate to the calling function that the memory need to be freed * when not needed any more. The size will contain the new length of the * command. * If memory has not been allocated we rely on the original mechanisms to * free the memory of the command - even when we reduce the value of size. */
static int hwarc_filter_cmd_WUSB_0100(struct uwb_rc *rc, struct uwb_rccb **header, size_t *size) { int result; struct uwb_rccb *rccb = *header; int cmd = le16_to_cpu(rccb->wCommand); switch (cmd) { case UWB_RC_CMD_SCAN: result = hwarc_filter_cmd_scan_WUSB_0100(rc, header, size); break; case UWB_RC_CMD_SET_DRP_IE: result = hwarc_filter_cmd_set_drp_ie_WUSB_0100(rc, header, size); break; default: result = -ENOANO; break; } return result; }

Contributors

PersonTokensPropCommitsCommitProp
inaky perez-gonzalezinaky perez-gonzalez88100.00%1100.00%
Total88100.00%1100.00%

/** * Filter data from WHCI driver to WUSB device * * @header: WHCI 0.95 compliant command from driver * @size: length of command * * Filter commands based on which protocol the device supports. The WUSB * errata should be the same as WHCI 0.95 so we do not filter that here - * only WUSB 1.0. */
static int hwarc_filter_cmd(struct uwb_rc *rc, struct uwb_rccb **header, size_t *size) { int result = -ENOANO; if (rc->version == 0x0100) result = hwarc_filter_cmd_WUSB_0100(rc, header, size); return result; }

Contributors

PersonTokensPropCommitsCommitProp
inaky perez-gonzalezinaky perez-gonzalez49100.00%1100.00%
Total49100.00%1100.00%

/** * Compute return value as sum of incoming value and value at given offset * * @rceb: event for which we compute the size, it contains a variable * length field. * @core_size: size of the "non variable" part of the event * @offset: place in event where the length of the variable part is stored * @buf_size: total length of buffer in which event arrived - we need to make * sure we read the offset in memory that is still part of the event */
static ssize_t hwarc_get_event_size(struct uwb_rc *rc, const struct uwb_rceb *rceb, size_t core_size, size_t offset, const size_t buf_size) { ssize_t size = -ENOSPC; const void *ptr = rceb; size_t type_size = sizeof(__le16); struct device *dev = &rc->uwb_dev.dev; if (offset + type_size >= buf_size) { dev_err(dev, "Not enough data to read extra size of event " "0x%02x/%04x/%02x, only got %zu bytes.\n", rceb->bEventType, le16_to_cpu(rceb->wEvent), rceb->bEventContext, buf_size); goto out; } ptr += offset; size = core_size + le16_to_cpu(*(__le16 *)ptr); out: return size; }

Contributors

PersonTokensPropCommitsCommitProp
inaky perez-gonzalezinaky perez-gonzalez121100.00%1100.00%
Total121100.00%1100.00%

/* Beacon slot change notification (WUSB 1.0 [8.6.3.5]) */ struct uwb_rc_evt_bp_slot_change_WUSB_0100 { struct uwb_rceb rceb; u8 bSlotNumber; } __attribute__((packed)); /** * Filter data from WUSB device to WHCI driver * * @header: incoming event * @buf_size: size of buffer in which event arrived * @_event_size: actual size of event in the buffer * @new_size: size of event after filtered * * We don't know how the buffer is constructed - there may be more than one * event in it so buffer length does not determine event length. We first * determine the expected size of the incoming event. This value is passed * back only if the actual filtering succeeded (so we know the computed * expected size is correct). This value will be zero if * the event did not need any filtering. * * WHCI interprets the BP Slot Change event's data differently than * WUSB. The event sizes are exactly the same. The data field * indicates the new beacon slot in which a RC is transmitting its * beacon. The maximum value of this is 96 (wMacBPLength ECMA-368 * 17.16 (Table 117)). We thus know that the WUSB value will not set * the bit bNoSlot, so we don't really do anything (placeholder). */
static int hwarc_filter_event_WUSB_0100(struct uwb_rc *rc, struct uwb_rceb **header, const size_t buf_size, size_t *_real_size, size_t *_new_size) { int result = -ENOANO; struct uwb_rceb *rceb = *header; int event = le16_to_cpu(rceb->wEvent); ssize_t event_size; size_t core_size, offset; if (rceb->bEventType != UWB_RC_CET_GENERAL) goto out; switch (event) { case UWB_RC_EVT_BEACON: core_size = sizeof(struct uwb_rc_evt_beacon_WUSB_0100); offset = offsetof(struct uwb_rc_evt_beacon_WUSB_0100, wBeaconInfoLength); event_size = hwarc_get_event_size(rc, rceb, core_size, offset, buf_size); if (event_size < 0) goto out; *_real_size = event_size; result = hwarc_filter_evt_beacon_WUSB_0100(rc, header, buf_size, _new_size); break; case UWB_RC_EVT_BP_SLOT_CHANGE: *_new_size = *_real_size = sizeof(struct uwb_rc_evt_bp_slot_change_WUSB_0100); result = 0; break; case UWB_RC_EVT_DRP_AVAIL: core_size = sizeof(struct uwb_rc_evt_drp_avail_WUSB_0100); offset = offsetof(struct uwb_rc_evt_drp_avail_WUSB_0100, wIELength); event_size = hwarc_get_event_size(rc, rceb, core_size, offset, buf_size); if (event_size < 0) goto out; *_real_size = event_size; result = hwarc_filter_evt_drp_avail_WUSB_0100( rc, header, buf_size, _new_size); break; case UWB_RC_EVT_DRP: core_size = sizeof(struct uwb_rc_evt_drp_WUSB_0100); offset = offsetof(struct uwb_rc_evt_drp_WUSB_0100, wIELength); event_size = hwarc_get_event_size(rc, rceb, core_size, offset, buf_size); if (event_size < 0) goto out; *_real_size = event_size; result = hwarc_filter_evt_drp_WUSB_0100(rc, header, buf_size, _new_size); break; default: break; } out: return result; }

Contributors

PersonTokensPropCommitsCommitProp
inaky perez-gonzalezinaky perez-gonzalez29699.66%150.00%
roel kluinroel kluin10.34%150.00%
Total297100.00%2100.00%

/** * Filter data from WUSB device to WHCI driver * * @header: incoming event * @buf_size: size of buffer in which event arrived * @_event_size: actual size of event in the buffer * @_new_size: size of event after filtered * * Filter events based on which protocol the device supports. The WUSB * errata should be the same as WHCI 0.95 so we do not filter that here - * only WUSB 1.0. * * If we don't handle it, we return -ENOANO (why the weird error code? * well, so if I get it, I can pinpoint in the code that raised * it...after all, not too many places use the higher error codes). */
static int hwarc_filter_event(struct uwb_rc *rc, struct uwb_rceb **header, const size_t buf_size, size_t *_real_size, size_t *_new_size) { int result = -ENOANO; if (rc->version == 0x0100) result = hwarc_filter_event_WUSB_0100( rc, header, buf_size, _real_size, _new_size); return result; }

Contributors

PersonTokensPropCommitsCommitProp
inaky perez-gonzalezinaky perez-gonzalez61100.00%1100.00%
Total61100.00%1100.00%

/** * Execute an UWB RC command on HWA * * @rc: Instance of a Radio Controller that is a HWA * @cmd: Buffer containing the RCCB and payload to execute * @cmd_size: Size of the command buffer. * * NOTE: rc's mutex has to be locked */
static int hwarc_cmd(struct uwb_rc *uwb_rc, const struct uwb_rccb *cmd, size_t cmd_size) { struct hwarc *hwarc = uwb_rc->priv; return usb_control_msg( hwarc->usb_dev, usb_sndctrlpipe(hwarc->usb_dev, 0), WA_EXEC_RC_CMD, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, hwarc->usb_iface->cur_altsetting->desc.bInterfaceNumber, (void *) cmd, cmd_size, 100 /* FIXME: this is totally arbitrary */); }

Contributors

PersonTokensPropCommitsCommitProp
inaky perez-gonzalezinaky perez-gonzalez77100.00%1100.00%
Total77100.00%1100.00%


static int hwarc_reset(struct uwb_rc *uwb_rc) { struct hwarc *hwarc = uwb_rc->priv; int result; /* device lock must be held when calling usb_reset_device. */ result = usb_lock_device_for_reset(hwarc->usb_dev, NULL); if (result >= 0) { result = usb_reset_device(hwarc->usb_dev); usb_unlock_device(hwarc->usb_dev); } return result; }

Contributors

PersonTokensPropCommitsCommitProp
thomas pugliesethomas pugliese3556.45%150.00%
inaky perez-gonzalezinaky perez-gonzalez2743.55%150.00%
Total62100.00%2100.00%

/** * Callback for the notification and event endpoint * * Check's that everything is fine and then passes the read data to * the notification/event handling mechanism (neh). */
static void hwarc_neep_cb(struct urb *urb) { struct hwarc *hwarc = urb->context; struct usb_interface *usb_iface = hwarc->usb_iface; struct device *dev = &usb_iface->dev; int result; switch (result = urb->status) { case 0: uwb_rc_neh_grok(hwarc->uwb_rc, urb->transfer_buffer, urb->actual_length); break; case -ECONNRESET: /* Not an error, but a controlled situation; */ case -ENOENT: /* (we killed the URB)...so, no broadcast */ goto out; case -ESHUTDOWN: /* going away! */ goto out; default: /* On general errors, retry unless it gets ugly */ if (edc_inc(&hwarc->neep_edc, EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) goto error_exceeded; dev_err(dev, "NEEP: URB error %d\n", urb->status); } result = usb_submit_urb(urb, GFP_ATOMIC); if (result < 0 && result != -ENODEV && result != -EPERM) { /* ignoring unrecoverable errors */ dev_err(dev, "NEEP: Can't resubmit URB (%d) resetting device\n", result); goto error; } out: return; error_exceeded: dev_err(dev, "NEEP: URB max acceptable errors " "exceeded, resetting device\n"); error: uwb_rc_neh_error(hwarc->uwb_rc, result); uwb_rc_reset_all(hwarc->uwb_rc); return; }

Contributors

PersonTokensPropCommitsCommitProp
inaky perez-gonzalezinaky perez-gonzalez18394.33%150.00%
oliver neukumoliver neukum115.67%150.00%
Total194100.00%2100.00%


static void hwarc_init(struct hwarc *hwarc) { edc_init(&hwarc->neep_edc); }

Contributors

PersonTokensPropCommitsCommitProp
inaky perez-gonzalezinaky perez-gonzalez19100.00%1100.00%
Total19100.00%1100.00%

/** * Initialize the notification/event endpoint stuff * * Note this is effectively a parallel thread; it knows that * hwarc->uwb_rc always exists because the existence of a 'hwarc' * means that there is a reverence on the hwarc->uwb_rc (see * _probe()), and thus _neep_cb() can execute safely. */
static int hwarc_neep_init(struct uwb_rc *rc) { struct hwarc *hwarc = rc->priv; struct usb_interface *iface = hwarc->usb_iface; struct usb_device *usb_dev = interface_to_usbdev(iface); struct device *dev = &iface->dev; int result; struct usb_endpoint_descriptor *epd; epd = &iface->cur_altsetting->endpoint[0].desc; hwarc->rd_buffer = (void *) __get_free_page(GFP_KERNEL); if (hwarc->rd_buffer == NULL) { dev_err(dev, "Unable to allocate notification's read buffer\n"); goto error_rd_buffer; } hwarc->neep_urb = usb_alloc_urb(0, GFP_KERNEL); if (hwarc->neep_urb == NULL) { dev_err(dev, "Unable to allocate notification URB\n"); goto error_urb_alloc; } usb_fill_int_urb(hwarc->neep_urb, usb_dev, usb_rcvintpipe(usb_dev, epd->bEndpointAddress), hwarc->rd_buffer, PAGE_SIZE, hwarc_neep_cb, hwarc, epd->bInterval); result = usb_submit_urb(hwarc->neep_urb, GFP_ATOMIC); if (result < 0) { dev_err(dev, "Cannot submit notification URB: %d\n", result); goto error_neep_submit; } return 0; error_neep_submit: usb_free_urb(hwarc->neep_urb); hwarc->neep_urb = NULL; error_urb_alloc: free_page((unsigned long)hwarc->rd_buffer); hwarc->rd_buffer = NULL; error_rd_buffer: return -ENOMEM; }

Contributors

PersonTokensPropCommitsCommitProp
inaky perez-gonzalezinaky perez-gonzalez22995.02%150.00%
thomas pugliesethomas pugliese124.98%150.00%
Total241100.00%2100.00%

/** Clean up all the notification endpoint resources */
static void hwarc_neep_release(struct uwb_rc *rc) { struct hwarc *hwarc = rc->priv; usb_kill_urb(hwarc->neep_urb); usb_free_urb(hwarc->neep_urb); hwarc->neep_urb = NULL; free_page((unsigned long)hwarc->rd_buffer); hwarc->rd_buffer = NULL; }

Contributors

PersonTokensPropCommitsCommitProp
inaky perez-gonzalezinaky perez-gonzalez4578.95%150.00%
thomas pugliesethomas pugliese1221.05%150.00%
Total57100.00%2100.00%

/** * Get the version from class-specific descriptor * * NOTE: this descriptor comes with the big bundled configuration * descriptor that includes the interfaces' and endpoints', so * we just look for it in the cached copy kept by the USB stack. * * NOTE2: We convert LE fields to CPU order. */
static int hwarc_get_version(struct uwb_rc *rc) { int result; struct hwarc *hwarc = rc->priv; struct uwb_rc_control_intf_class_desc *descr; struct device *dev = &rc->uwb_dev.dev; struct usb_device *usb_dev = hwarc->usb_dev; char *itr; struct usb_descriptor_header *hdr; size_t itr_size, actconfig_idx; u16 version; actconfig_idx = (usb_dev->actconfig - usb_dev->config) / sizeof(usb_dev->config[0]); itr = usb_dev->rawdescriptors[actconfig_idx]; itr_size = le16_to_cpu(usb_dev->actconfig->desc.wTotalLength); while (itr_size >= sizeof(*hdr)) { hdr = (struct usb_descriptor_header *) itr; dev_dbg(dev, "Extra device descriptor: " "type %02x/%u bytes @ %zu (%zu left)\n", hdr->bDescriptorType, hdr->bLength, (itr - usb_dev->rawdescriptors[actconfig_idx]), itr_size); if (hdr->bDescriptorType == USB_DT_CS_RADIO_CONTROL) goto found; itr += hdr->bLength; itr_size -= hdr->bLength; } dev_err(dev, "cannot find Radio Control Interface Class descriptor\n"); return -ENODEV; found: result = -EINVAL; if (hdr->bLength > itr_size) { /* is it available? */ dev_err(dev, "incomplete Radio Control Interface Class " "descriptor (%zu bytes left, %u needed)\n", itr_size, hdr->bLength); goto error; } if (hdr->bLength < sizeof(*descr)) { dev_err(dev, "short Radio Control Interface Class " "descriptor\n"); goto error; } descr = (struct uwb_rc_control_intf_class_desc *) hdr; /* Make LE fields CPU order */ version = __le16_to_cpu(descr->bcdRCIVersion); if (version != 0x0100) { dev_err(dev, "Device reports protocol version 0x%04x. We " "do not support that. \n", version); result = -EINVAL; goto error; } rc->version = version; dev_dbg(dev, "Device supports WUSB protocol version 0x%04x \n", rc->version); result = 0; error: return result; }

Contributors

PersonTokensPropCommitsCommitProp
inaky perez-gonzalezinaky perez-gonzalez32399.38%150.00%
david vrabeldavid vrabel20.62%150.00%
Total325100.00%2100.00%

/* * By creating a 'uwb_rc', we have a reference on it -- that reference * is the one we drop when we disconnect. * * No need to switch altsettings; according to WUSB1.0[8.6.1.1], there * is only one altsetting allowed. */
static int hwarc_probe(struct usb_interface *iface, const struct usb_device_id *id) { int result; struct uwb_rc *uwb_rc; struct hwarc *hwarc; struct device *dev = &iface->dev; result = -ENOMEM; uwb_rc = uwb_rc_alloc(); if (uwb_rc == NULL) { dev_err(dev, "unable to allocate RC instance\n"); goto error_rc_alloc; } hwarc = kzalloc(sizeof(*hwarc), GFP_KERNEL); if (hwarc == NULL) { dev_err(dev, "unable to allocate HWA RC instance\n"); goto error_alloc; } hwarc_init(hwarc); hwarc->usb_dev = usb_get_dev(interface_to_usbdev(iface)); hwarc->usb_iface = usb_get_intf(iface); hwarc->uwb_rc = uwb_rc; uwb_rc->owner = THIS_MODULE; uwb_rc->start = hwarc_neep_init; uwb_rc->stop = hwarc_neep_release; uwb_rc->cmd = hwarc_cmd; uwb_rc->reset = hwarc_reset; if (id->driver_info & WUSB_QUIRK_WHCI_CMD_EVT) { uwb_rc->filter_cmd = NULL; uwb_rc->filter_event = NULL; } else { uwb_rc->filter_cmd = hwarc_filter_cmd; uwb_rc->filter_event = hwarc_filter_event; } result = uwb_rc_add(uwb_rc, dev, hwarc); if (result < 0) goto error_rc_add; result = hwarc_get_version(uwb_rc); if (result < 0) { dev_err(dev, "cannot retrieve version of RC \n"); goto error_get_version; } usb_set_intfdata(iface, hwarc); return 0; error_get_version: uwb_rc_rm(uwb_rc); error_rc_add: usb_put_intf(iface); usb_put_dev(hwarc->usb_dev); error_alloc: uwb_rc_put(uwb_rc); error_rc_alloc: return result; }

Contributors

PersonTokensPropCommitsCommitProp
inaky perez-gonzalezinaky perez-gonzalez26191.26%150.00%
anderson lizardoanderson lizardo258.74%150.00%
Total286100.00%2100.00%


static void hwarc_disconnect(struct usb_interface *iface) { struct hwarc *hwarc = usb_get_intfdata(iface); struct uwb_rc *uwb_rc = hwarc->uwb_rc; usb_set_intfdata(hwarc->usb_iface, NULL); uwb_rc_rm(uwb_rc); usb_put_intf(hwarc->usb_iface); usb_put_dev(hwarc->usb_dev); kfree(hwarc); uwb_rc_put(uwb_rc); /* when creating the device, refcount = 1 */ }

Contributors

PersonTokensPropCommitsCommitProp
inaky perez-gonzalezinaky perez-gonzalez69100.00%1100.00%
Total69100.00%1100.00%


static int hwarc_pre_reset(struct usb_interface *iface) { struct hwarc *hwarc = usb_get_intfdata(iface); struct uwb_rc *uwb_rc = hwarc->uwb_rc; uwb_rc_pre_reset(uwb_rc); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
david vrabeldavid vrabel38100.00%1100.00%
Total38100.00%1100.00%


static int hwarc_post_reset(struct usb_interface *iface) { struct hwarc *hwarc = usb_get_intfdata(iface); struct uwb_rc *uwb_rc = hwarc->uwb_rc; return uwb_rc_post_reset(uwb_rc); }

Contributors

PersonTokensPropCommitsCommitProp
david vrabeldavid vrabel36100.00%2100.00%
Total36100.00%2100.00%

/** USB device ID's that we handle */ static const struct usb_device_id hwarc_id_table[] = { /* D-Link DUB-1210 */ { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3d02, 0xe0, 0x01, 0x02), .driver_info = WUSB_QUIRK_WHCI_CMD_EVT }, /* Intel i1480 (using firmware 1.3PA2-20070828) */ { USB_DEVICE_AND_INTERFACE_INFO(0x8086, 0x0c3b, 0xe0, 0x01, 0x02), .driver_info = WUSB_QUIRK_WHCI_CMD_EVT }, /* Alereon 5310 */ { USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5310, 0xe0, 0x01, 0x02), .driver_info = WUSB_QUIRK_WHCI_CMD_EVT }, /* Alereon 5611 */ { USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5611, 0xe0, 0x01, 0x02), .driver_info = WUSB_QUIRK_WHCI_CMD_EVT }, /* Generic match for the Radio Control interface */ { USB_INTERFACE_INFO(0xe0, 0x01, 0x02), }, { }, }; MODULE_DEVICE_TABLE(usb, hwarc_id_table); static struct usb_driver hwarc_driver = { .name = "hwa-rc", .id_table = hwarc_id_table, .probe = hwarc_probe, .disconnect = hwarc_disconnect, .pre_reset = hwarc_pre_reset, .post_reset = hwarc_post_reset, }; module_usb_driver(hwarc_driver); MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>"); MODULE_DESCRIPTION("Host Wireless Adapter Radio Control Driver"); MODULE_LICENSE("GPL");

Overall Contributors

PersonTokensPropCommitsCommitProp
inaky perez-gonzalezinaky perez-gonzalez315291.87%17.69%
thomas pugliesethomas pugliese1012.94%215.38%
david vrabeldavid vrabel882.56%323.08%
anderson lizardoanderson lizardo732.13%215.38%
oliver neukumoliver neukum110.32%17.69%
tejun heotejun heo30.09%17.69%
marton nemethmarton nemeth10.03%17.69%
greg kroah-hartmangreg kroah-hartman10.03%17.69%
roel kluinroel kluin10.03%17.69%
Total3431100.00%13100.00%
Directory: drivers/uwb
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}