diff options
-rw-r--r-- | Documentation/usb/wusb-cbaf | 60 | ||||
-rw-r--r-- | drivers/usb/wusbcore/Kconfig | 23 | ||||
-rw-r--r-- | drivers/usb/wusbcore/Makefile | 8 | ||||
-rw-r--r-- | drivers/usb/wusbcore/cbaf.c | 521 |
4 files changed, 350 insertions, 262 deletions
diff --git a/Documentation/usb/wusb-cbaf b/Documentation/usb/wusb-cbaf index a385478ba12e..2e78b70f3adc 100644 --- a/Documentation/usb/wusb-cbaf +++ b/Documentation/usb/wusb-cbaf @@ -70,32 +70,42 @@ EOF # FIXME: CHID should come from a database :), band group from the host host_CHID="00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff" host_band_group="0001" -host_name="Linux-WUSB" +host_name=$(hostname) devs="$(echo /sys/bus/usb/drivers/wusb-cbaf/[0-9]*)" -hdevs="$(find /sys -name wusb_chid -printf "%h\n")" +hdevs="$(for h in /sys/class/uwb_rc/*/wusbhc; do readlink -f $h; done)" result=0 case $1 in start) for dev in ${2:-$hdevs} do - uwb_rc=$(find $(dirname $(dirname $dev)) -iname uwb_rc:uwb*) - if cat $uwb_rc/uwb_rc/beacon | grep -q "channel: -1" + uwb_rc=$(readlink -f $dev/uwb_rc) + if cat $uwb_rc/beacon | grep -q -- "-1" then - echo 13 0 | cat > $uwb_rc/uwb_rc/beacon - echo I: started beaconing on ch 13 in host $(basename $uwb_rc) + echo 13 0 > $uwb_rc/beacon + echo I: started beaconing on ch 13 on $(basename $uwb_rc) >&2 fi - echo $host_CHID | cat > $dev/wusb_chid - echo I: started host $(basename $dev) + echo $host_CHID > $dev/wusb_chid + echo I: started host $(basename $dev) >&2 + done + ;; + stop) + for dev in ${2:-$hdevs} + do + echo 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > $dev/wusb_chid + echo I: stopped host $(basename $dev) >&2 + uwb_rc=$(readlink -f $dev/uwb_rc) + echo -1 | cat > $uwb_rc/beacon + echo I: stopped beaconing on $(basename $uwb_rc) >&2 done ;; set-chid) shift - for dev in ${2:-$devs} - do - echo "${2:-$host_CHID}" "${3:-$host_band_group}" "${4:-$host_name}" \ - | cat > $dev/wusb_host_info + for dev in ${2:-$devs}; do + echo "${4:-$host_name}" > $dev/wusb_host_name + echo "${3:-$host_band_group}" > $dev/wusb_host_band_groups + echo ${2:-$host_CHID} > $dev/wusb_chid done ;; get-cdid) @@ -105,21 +115,17 @@ case $1 in done ;; set-cc) - for dev in ${2:-$devs} - do - shift - CDID="$(head --bytes=16 /dev/urandom | od -tx1 -An)" - CK="$(head --bytes=16 /dev/urandom | od -tx1 -An)" - cat > $dev/wusb_cc <<EOF -CDID:$CDID -CK:$CK -EOF - cat <<EOF -I: CC set -CHID: $host_CHID -CDID:$CDID -CK: $CK -EOF + for dev in ${2:-$devs}; do + shift + CDID="$(head --bytes=16 /dev/urandom | od -tx1 -An)" + CK="$(head --bytes=16 /dev/urandom | od -tx1 -An)" + echo "$CDID" > $dev/wusb_cdid + echo "$CK" > $dev/wusb_ck + + echo I: CC set >&2 + echo "CHID: $(cat $dev/wusb_chid)" + echo "CDID:$CDID" + echo "CK: $CK" done ;; help|h|--help|-h) diff --git a/drivers/usb/wusbcore/Kconfig b/drivers/usb/wusbcore/Kconfig index add077e5c5d0..4ea76693ad46 100644 --- a/drivers/usb/wusbcore/Kconfig +++ b/drivers/usb/wusbcore/Kconfig @@ -15,3 +15,26 @@ config USB_WUSB 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. + diff --git a/drivers/usb/wusbcore/Makefile b/drivers/usb/wusbcore/Makefile index 7a4d00724039..75f1ade66258 100644 --- a/drivers/usb/wusbcore/Makefile +++ b/drivers/usb/wusbcore/Makefile @@ -1,5 +1,7 @@ -obj-$(CONFIG_USB_WUSB) += wusbcore.o wusb-cbaf.o +obj-$(CONFIG_USB_WUSB) += wusbcore.o obj-$(CONFIG_USB_HWA_HCD) += wusb-wa.o +obj-$(CONFIG_USB_WUSB_CBAF) += wusb-cbaf.o + wusbcore-objs := \ crypto.o \ @@ -18,3 +20,7 @@ wusb-wa-objs := wa-hc.o \ wa-nep.o \ wa-rpipe.o \ wa-xfer.o + +ifeq ($(CONFIG_USB_WUSB_CBAF_DEBUG),y) +EXTRA_CFLAGS += -DDEBUG +endif diff --git a/drivers/usb/wusbcore/cbaf.c b/drivers/usb/wusbcore/cbaf.c index 584eabe274b3..ab4788d1785a 100644 --- a/drivers/usb/wusbcore/cbaf.c +++ b/drivers/usb/wusbcore/cbaf.c @@ -4,6 +4,7 @@ * * 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 @@ -20,14 +21,13 @@ * 02110-1301, USA. * * - * WUSB devices have to be paired (authenticated in WUSB lingo) so + * 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 Authentication, devices - * that can connect via wired or wireless USB. First time you plug - * them with a cable, pairing is done between host and device and - * subsequent times, you can connect wirelessly without having to - * pair. That's the idea. + * 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 @@ -42,56 +42,49 @@ * * The process goes like this: * - * 1. device plugs, cbaf is loaded, notifications happen + * 1. Device plugs, cbaf is loaded, notifications happen. * - * 2. the connection manager sees a device with CBAF capability (the - * wusb_{host_info,cdid,cc} files are in /sys/device/blah/OURDEVICE). + * 2. The connection manager (CM) sees a device with CBAF capability + * (the wusb_chid etc. files in /sys/devices/blah/OURDEVICE). * - * 3. CM (connection manager) writes the CHID (host ID) and a host - * name into the wusb_host_info file. This gets sent to the device. + * 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. CM cats the wusb_cdid file; this asks the device if it has any - * CDID associated to the CHDI we just wrote before. If it does, it - * is printed, along with the device 'friendly name' and the band - * groups the device supports. + * 4. The CM can verify that the device's supported band groups + * (wusb_device_band_groups) are compatible with the host. * - * 5. CM looks up its database + * 5. The CM reads the wusb_cdid file. * - * 5.1 If it has a matching CHID,CDID entry, the device has been - * authorized before (paired). Now we can optionally ask the user - * if he wants to allow the device to connect. Then we generate a - * new CDID and CK, send it to the device and update the database - * (writing to the wusb_cc file so they are uploaded to the device). + * 6. The CM looks up its database * - * 5.2 If the CDID is zero (or we didn't find a matching CDID in our - * database), we assume the device is not known. We ask the user - * if s/he wants to allow the device to be connected wirelessly - * to the system. If nope, nothing else is done (FIXME: maybe - * send a zero CDID to clean up our CHID?). If yes, we generate - * random CDID and CKs (and write them to the wusb_cc file so - * they are uploaded to the device). + * 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. device is unplugged + * 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). * - * When the device tries to connect wirelessly, it will present it's - * CDID to the WUSB host controller with ID CHID, which will query the - * database. If found, the host will (with a 4way 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 crypted. + * CMD may choose to prompt the user before associating with a new + * device. * + * 7. Device is unplugged. * - * NOTES ABOUT THE IMPLEMENTATION + * 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. * - * The descriptors sent back and forth use this horrible format from - * hell on which each field is actually a field ID, field length and - * then the field itself. How stupid can that get, taking into account - * the structures are defined by the spec?? oh well. - * - * - * FIXME: we don't provide a way to tell the device the pairing failed - * (ie: send a CC_DATA_FAIL). Should add some day. + * 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> @@ -105,9 +98,7 @@ #include <linux/usb/wusb.h> #include <linux/usb/association.h> -#undef D_LOCAL -#define D_LOCAL 6 -#include <linux/uwb/debug.h> +#define CBA_NAME_LEN 0x40 /* [WUSB-AM] table 4-7 */ /* An instance of a Cable-Based-Association-Framework device */ struct cbaf { @@ -116,24 +107,27 @@ struct cbaf { void *buffer; size_t buffer_size; - struct wusb_ckhdid chid;/* Host Information */ - char host_name[65]; /* max length: - Assoc Models Suplement 1.0[T4-7] */ + struct wusb_ckhdid chid; + char host_name[CBA_NAME_LEN]; u16 host_band_groups; - struct wusb_ckhdid cdid;/* Device Information */ - char device_name[65]; /* max length: - Assoc Models Suplement 1.0[T4-7] */ + struct wusb_ckhdid cdid; + char device_name[CBA_NAME_LEN]; u16 device_band_groups; - struct wusb_ckhdid ck; /* Connection Key */ + + struct wusb_ckhdid ck; }; /* * Verify that a CBAF USB-interface has what we need * - * (like we care, we are going to fail the enumeration if not :) + * According to [WUSB-AM], CBA devices should provide at least two + * interfaces: + * - RETRIEVE_HOST_INFO + * - ASSOCIATE * - * FIXME: ugly function, need to split + * If the device doesn't provide these interfaces, we do not know how + * to deal with it. */ static int cbaf_check(struct cbaf *cbaf) { @@ -143,8 +137,7 @@ static int cbaf_check(struct cbaf *cbaf) struct wusb_cbaf_assoc_request *assoc_request; size_t assoc_size; void *itr, *top; - unsigned ar_index; - int ar_rhi_idx = -1, ar_assoc_idx = -1; + int ar_rhi = 0, ar_assoc = 0; result = usb_control_msg( cbaf->usb_dev, usb_rcvctrlpipe(cbaf->usb_dev, 0), @@ -153,93 +146,91 @@ static int cbaf_check(struct cbaf *cbaf) 0, cbaf->usb_iface->cur_altsetting->desc.bInterfaceNumber, cbaf->buffer, cbaf->buffer_size, 1000 /* FIXME: arbitrary */); if (result < 0) { - dev_err(dev, "cannot get available association types: %d\n", + dev_err(dev, "Cannot get available association types: %d\n", result); - goto error_get_assoc_types; + return result; } + assoc_info = cbaf->buffer; if (result < sizeof(*assoc_info)) { - dev_err(dev, "not enough data to decode association info " + dev_err(dev, "Not enough data to decode association info " "header (%zu vs %zu bytes required)\n", (size_t)result, sizeof(*assoc_info)); - goto error_bad_header; + return result; } + assoc_size = le16_to_cpu(assoc_info->Length); if (result < assoc_size) { - dev_err(dev, "not enough data to decode association info " + dev_err(dev, "Not enough data to decode association info " "(%zu vs %zu bytes required)\n", (size_t)assoc_size, sizeof(*assoc_info)); - goto error_bad_data; + 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. */ - ar_index = 0; itr = cbaf->buffer + sizeof(*assoc_info); top = cbaf->buffer + assoc_size; - d_printf(1, dev, "Found %u association requests (%zu bytes)\n", + 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 associaton " + dev_err(dev, "Not enough data to decode associaton " "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 AMS1.0 */ + /* 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_idx = ar_index; + ar_name = "RETRIEVE_HOST_INFO"; + ar_rhi = 1; break; case AR_TYPE_WUSB_ASSOCIATE: /* send assoc data */ - ar_name = "associate"; - ar_assoc_idx = ar_index; + ar_name = "ASSOCIATE"; + ar_assoc = 1; break; - default: - ar_name = "unknown"; }; break; - default: - ar_name = "unknown"; }; - d_printf(1, dev, "association request #%02u: 0x%04x/%04x " + + 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); - ar_index++; } - if (ar_rhi_idx == -1) { + + if (!ar_rhi) { dev_err(dev, "Missing RETRIEVE_HOST_INFO association " "request\n"); - goto error_bad_reqs; + return -EINVAL; } - if (ar_assoc_idx == -1) { + if (!ar_assoc) { dev_err(dev, "Missing ASSOCIATE association request\n"); - goto error_bad_reqs; + return -EINVAL; } - return 0; -error_bad_header: -error_bad_data: -error_bad_reqs: -error_get_assoc_types: - return -EINVAL; + return 0; } static const struct wusb_cbaf_host_info cbaf_host_info_defaults = { @@ -256,6 +247,7 @@ static const struct wusb_cbaf_host_info cbaf_host_info_defaults = { 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; @@ -263,11 +255,11 @@ static int cbaf_send_host_info(struct cbaf *cbaf) *hi = cbaf_host_info_defaults; hi->CHID = cbaf->chid; hi->LangID = 0; /* FIXME: I guess... */ - strncpy(hi->HostFriendlyName, cbaf->host_name, - hi->HostFriendlyName_hdr.len); - hi->HostFriendlyName_hdr.len = - cpu_to_le16(strlen(hi->HostFriendlyName)); - hi_size = sizeof(*hi) + strlen(hi->HostFriendlyName); + 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, @@ -276,8 +268,47 @@ static int cbaf_send_host_info(struct cbaf *cbaf) hi, hi_size, 1000 /* FIXME: arbitrary */); } -/* Show current CHID info we have set from user space */ -static ssize_t cbaf_wusb_host_info_show(struct device *dev, +/* + * 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, 1000 /* FIXME: arbitrary */); + 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 result; + } + + 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) { @@ -286,17 +317,10 @@ static ssize_t cbaf_wusb_host_info_show(struct device *dev, char pr_chid[WUSB_CKHDID_STRSIZE]; ckhdid_printf(pr_chid, sizeof(pr_chid), &cbaf->chid); - return scnprintf(buf, PAGE_SIZE, "CHID: %s\nName: %s\n", - pr_chid, cbaf->host_name); + return scnprintf(buf, PAGE_SIZE, "%s\n", pr_chid); } -/* - * Get a host info CHID from user space and send it to the device. - * - * The user can recover a CC from the device associated to that CHID - * by cat'ing wusb_connection_context. - */ -static ssize_t cbaf_wusb_host_info_store(struct device *dev, +static ssize_t cbaf_wusb_chid_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { @@ -308,8 +332,7 @@ static ssize_t cbaf_wusb_host_info_store(struct device *dev, "%02hhx %02hhx %02hhx %02hhx " "%02hhx %02hhx %02hhx %02hhx " "%02hhx %02hhx %02hhx %02hhx " - "%02hhx %02hhx %02hhx %02hhx " - "%04hx %64s\n", + "%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], @@ -317,24 +340,79 @@ static ssize_t cbaf_wusb_host_info_store(struct device *dev, &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], - &cbaf->host_band_groups, cbaf->host_name); - if (result != 18) { - dev_err(dev, "Unrecognized CHID (need 16 8-bit hex digits, " - "a 16 bit hex band group mask " - "and a host name, got only %d)\n", (int)result); + &cbaf->chid.data[14], &cbaf->chid.data[15]); + + if (result != 16) return -EINVAL; - } + result = cbaf_send_host_info(cbaf); if (result < 0) - dev_err(dev, "Couldn't send host information to device: %d\n", - (int)result); - else - d_printf(1, dev, "HI sent, wusb_cc can be read now\n"); - return result < 0 ? result : size; + 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_info, 0600, cbaf_wusb_host_info_show, - cbaf_wusb_host_info_store); + +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, @@ -344,77 +422,72 @@ static const struct wusb_cbaf_device_info cbaf_device_info_defaults = { .DeviceFriendlyName_hdr = WUSB_AR_DeviceFriendlyName, }; -/* - * 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) +static ssize_t cbaf_wusb_cdid_show(struct device *dev, + struct device_attribute *attr, char *buf) { - int result; - struct device *dev = &cbaf->usb_iface->dev; - struct wusb_cbaf_device_info *di; - size_t needed, dev_name_size; + struct usb_interface *iface = to_usb_interface(dev); + struct cbaf *cbaf = usb_get_intfdata(iface); + char pr_cdid[WUSB_CKHDID_STRSIZE]; - 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, 1000 /* FIXME: arbitrary */); - if (result < 0) { - dev_err(dev, "Cannot request device information: %d\n", result); - goto error_req_di; - } - 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); - goto error_bad_di; - } - cbaf->cdid = di->CDID; - dev_name_size = le16_to_cpu(di->DeviceFriendlyName_hdr.len); - dev_name_size = dev_name_size > 65 - 1 ? 65 - 1 : dev_name_size; - memcpy(cbaf->device_name, di->DeviceFriendlyName, dev_name_size); - cbaf->device_name[dev_name_size] = 0; - cbaf->device_band_groups = le16_to_cpu(di->BandGroups); - result = 0; -error_req_di: -error_bad_di: - return result; + ckhdid_printf(pr_cdid, sizeof(pr_cdid), &cbaf->cdid); + return scnprintf(buf, PAGE_SIZE, "%s\n", pr_cdid); } -/* - * Get device information and print it to sysfs - * - * See cbaf_cdid_get() - */ -static ssize_t cbaf_wusb_cdid_show(struct device *dev, - struct device_attribute *attr, char *buf) +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); - char pr_cdid[WUSB_CKHDID_STRSIZE]; + struct wusb_ckhdid cdid; - result = cbaf_cdid_get(cbaf); - if (result < 0) { - dev_err(dev, "Cannot read device information: %d\n", - (int)result); - goto error_get_di; - } - ckhdid_printf(pr_cdid, sizeof(pr_cdid), &cbaf->cdid); - result = scnprintf(buf, PAGE_SIZE, - "CDID: %s\nName: %s\nBand_groups: 0x%04x\n", - pr_cdid, cbaf->device_name, - cbaf->device_band_groups); -error_get_di: - return result; + 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_cdid, 0600, cbaf_wusb_cdid_show, NULL); +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, @@ -435,9 +508,7 @@ static const struct wusb_cbaf_cc_data_fail cbaf_cc_data_fail_defaults = { }; /* - * Send a new CC to the device - * - * So we update the CK and send the whole thing to the device + * Send a new CC to the device. */ static int cbaf_cc_upload(struct cbaf *cbaf) { @@ -452,30 +523,25 @@ static int cbaf_cc_upload(struct cbaf *cbaf) 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), 1000 /* FIXME: arbitrary */); - d_printf(1, dev, "Uploaded CC:\n"); - ckhdid_printf(pr_cdid, sizeof(pr_cdid), &ccd->CHID); - d_printf(1, dev, " CHID %s\n", pr_cdid); - ckhdid_printf(pr_cdid, sizeof(pr_cdid), &ccd->CDID); - d_printf(1, dev, " CDID %s\n", pr_cdid); - ckhdid_printf(pr_cdid, sizeof(pr_cdid), &ccd->CK); - d_printf(1, dev, " CK %s\n", pr_cdid); - d_printf(1, dev, " bandgroups 0x%04x\n", cbaf->host_band_groups); + return result; } -/* - * Send a new CC to the device - * - * We take the CDID and CK from user space, the rest from the info we - * set with host_info. - */ -static ssize_t cbaf_wusb_cc_store(struct device *dev, +static ssize_t cbaf_wusb_ck_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { @@ -484,23 +550,10 @@ static ssize_t cbaf_wusb_cc_store(struct device *dev, struct cbaf *cbaf = usb_get_intfdata(iface); result = sscanf(buf, - "CDID: %02hhx %02hhx %02hhx %02hhx " - "%02hhx %02hhx %02hhx %02hhx " "%02hhx %02hhx %02hhx %02hhx " - "%02hhx %02hhx %02hhx %02hhx\n" - "CK: %02hhx %02hhx %02hhx %02hhx " "%02hhx %02hhx %02hhx %02hhx " "%02hhx %02hhx %02hhx %02hhx " - "%02hhx %02hhx %02hhx %02hhx\n", - &cbaf->cdid.data[0] , &cbaf->cdid.data[1], - &cbaf->cdid.data[2] , &cbaf->cdid.data[3], - &cbaf->cdid.data[4] , &cbaf->cdid.data[5], - &cbaf->cdid.data[6] , &cbaf->cdid.data[7], - &cbaf->cdid.data[8] , &cbaf->cdid.data[9], - &cbaf->cdid.data[10], &cbaf->cdid.data[11], - &cbaf->cdid.data[12], &cbaf->cdid.data[13], - &cbaf->cdid.data[14], &cbaf->cdid.data[15], - + "%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], @@ -509,25 +562,25 @@ static ssize_t cbaf_wusb_cc_store(struct device *dev, &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 != 32) { - dev_err(dev, "Unrecognized CHID/CK (need 32 8-bit " - "hex digits, got only %d)\n", (int)result); + if (result != 16) return -EINVAL; - } + result = cbaf_cc_upload(cbaf); if (result < 0) - dev_err(dev, "Couldn't upload connection context: %d\n", - (int)result); - else - d_printf(1, dev, "Connection context uploaded\n"); - return result < 0 ? result : size; + return result; + + return size; } -static DEVICE_ATTR(wusb_cc, 0600, NULL, cbaf_wusb_cc_store); +static DEVICE_ATTR(wusb_ck, 0600, NULL, cbaf_wusb_ck_store); static struct attribute *cbaf_dev_attrs[] = { - &dev_attr_wusb_host_info.attr, + &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_cc.attr, + &dev_attr_wusb_device_name.attr, + &dev_attr_wusb_device_band_groups.attr, + &dev_attr_wusb_ck.attr, NULL, }; @@ -539,32 +592,33 @@ static struct attribute_group cbaf_dev_attr_group = { static int cbaf_probe(struct usb_interface *iface, const struct usb_device_id *id) { - int result; struct cbaf *cbaf; struct device *dev = &iface->dev; + int result = -ENOMEM; - result = -ENOMEM; cbaf = kzalloc(sizeof(*cbaf), GFP_KERNEL); - if (cbaf == NULL) { - dev_err(dev, "Unable to allocate instance\n"); + 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) + 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); - d_printf(2, dev, "CBA attached\n"); return 0; error_create_group: @@ -587,7 +641,6 @@ static void cbaf_disconnect(struct usb_interface *iface) /* paranoia: clean up crypto keys */ memset(cbaf, 0, sizeof(*cbaf)); kfree(cbaf); - d_printf(1, dev, "CBA detached\n"); } static struct usb_device_id cbaf_id_table[] = { |