Commit 51c7f5ed authored by Hante Meuleman's avatar Hante Meuleman Committed by John W. Linville

brcmfmac: Change USB probe routine to support Composite USB

Some of the USB devices also have Bluetooth inside. These devices
can with specific firmware result in a composite USB device. This
change will update the driver such that it will also accept the
correct interface of composite devices. It is backward compatible
with old non-composite USB fw.
Reviewed-by: default avatarArend Van Spriel <arend@broadcom.com>
Reviewed-by: default avatarFranky (Zhenhui) Lin <frankyl@broadcom.com>
Reviewed-by: default avatarPieter-Paul Giesberts <pieterpg@broadcom.com>
Reviewed-by: default avatarDaniel (Deognyoun) Kim <dekim@broadcom.com>
Signed-off-by: default avatarHante Meuleman <meuleman@broadcom.com>
Signed-off-by: default avatarArend van Spriel <arend@broadcom.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent d83f8fac
...@@ -29,33 +29,24 @@ ...@@ -29,33 +29,24 @@
#include "usb_rdl.h" #include "usb_rdl.h"
#include "usb.h" #include "usb.h"
#define IOCTL_RESP_TIMEOUT 2000 #define IOCTL_RESP_TIMEOUT 2000
#define BRCMF_USB_RESET_GETVER_SPINWAIT 100 /* in unit of ms */ #define BRCMF_USB_RESET_GETVER_SPINWAIT 100 /* in unit of ms */
#define BRCMF_USB_RESET_GETVER_LOOP_CNT 10 #define BRCMF_USB_RESET_GETVER_LOOP_CNT 10
#define BRCMF_POSTBOOT_ID 0xA123 /* ID to detect if dongle #define BRCMF_POSTBOOT_ID 0xA123 /* ID to detect if dongle
has boot up */ has boot up */
#define BRCMF_USB_NRXQ 50 #define BRCMF_USB_NRXQ 50
#define BRCMF_USB_NTXQ 50 #define BRCMF_USB_NTXQ 50
#define CONFIGDESC(usb) (&((usb)->actconfig)->desc) #define BRCMF_USB_CBCTL_WRITE 0
#define IFPTR(usb, idx) ((usb)->actconfig->interface[(idx)]) #define BRCMF_USB_CBCTL_READ 1
#define IFALTS(usb, idx) (IFPTR((usb), (idx))->altsetting[0]) #define BRCMF_USB_MAX_PKT_SIZE 1600
#define IFDESC(usb, idx) IFALTS((usb), (idx)).desc
#define IFEPDESC(usb, idx, ep) (IFALTS((usb), (idx)).endpoint[(ep)]).desc
#define CONTROL_IF 0 #define BRCMF_USB_43143_FW_NAME "brcm/brcmfmac43143.bin"
#define BULK_IF 0 #define BRCMF_USB_43236_FW_NAME "brcm/brcmfmac43236b.bin"
#define BRCMF_USB_43242_FW_NAME "brcm/brcmfmac43242a.bin"
#define BRCMF_USB_CBCTL_WRITE 0 #define BRCMF_USB_43569_FW_NAME "brcm/brcmfmac43569.bin"
#define BRCMF_USB_CBCTL_READ 1
#define BRCMF_USB_MAX_PKT_SIZE 1600
#define BRCMF_USB_43143_FW_NAME "brcm/brcmfmac43143.bin"
#define BRCMF_USB_43236_FW_NAME "brcm/brcmfmac43236b.bin"
#define BRCMF_USB_43242_FW_NAME "brcm/brcmfmac43242a.bin"
#define BRCMF_USB_43569_FW_NAME "brcm/brcmfmac43569.bin"
struct brcmf_usb_image { struct brcmf_usb_image {
struct list_head list; struct list_head list;
...@@ -71,7 +62,7 @@ struct brcmf_usbdev_info { ...@@ -71,7 +62,7 @@ struct brcmf_usbdev_info {
struct list_head rx_postq; struct list_head rx_postq;
struct list_head tx_freeq; struct list_head tx_freeq;
struct list_head tx_postq; struct list_head tx_postq;
uint rx_pipe, tx_pipe, rx_pipe2; uint rx_pipe, tx_pipe;
int rx_low_watermark; int rx_low_watermark;
int tx_low_watermark; int tx_low_watermark;
...@@ -98,6 +89,7 @@ struct brcmf_usbdev_info { ...@@ -98,6 +89,7 @@ struct brcmf_usbdev_info {
int ctl_completed; int ctl_completed;
wait_queue_head_t ioctl_resp_wait; wait_queue_head_t ioctl_resp_wait;
ulong ctl_op; ulong ctl_op;
u8 ifnum;
struct urb *bulk_urb; /* used for FW download */ struct urb *bulk_urb; /* used for FW download */
}; };
...@@ -577,7 +569,6 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb) ...@@ -577,7 +569,6 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
static int brcmf_usb_up(struct device *dev) static int brcmf_usb_up(struct device *dev)
{ {
struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
u16 ifnum;
brcmf_dbg(USB, "Enter\n"); brcmf_dbg(USB, "Enter\n");
if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP)
...@@ -590,21 +581,19 @@ static int brcmf_usb_up(struct device *dev) ...@@ -590,21 +581,19 @@ static int brcmf_usb_up(struct device *dev)
devinfo->ctl_in_pipe = usb_rcvctrlpipe(devinfo->usbdev, 0); devinfo->ctl_in_pipe = usb_rcvctrlpipe(devinfo->usbdev, 0);
devinfo->ctl_out_pipe = usb_sndctrlpipe(devinfo->usbdev, 0); devinfo->ctl_out_pipe = usb_sndctrlpipe(devinfo->usbdev, 0);
ifnum = IFDESC(devinfo->usbdev, CONTROL_IF).bInterfaceNumber;
/* CTL Write */ /* CTL Write */
devinfo->ctl_write.bRequestType = devinfo->ctl_write.bRequestType =
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
devinfo->ctl_write.bRequest = 0; devinfo->ctl_write.bRequest = 0;
devinfo->ctl_write.wValue = cpu_to_le16(0); devinfo->ctl_write.wValue = cpu_to_le16(0);
devinfo->ctl_write.wIndex = cpu_to_le16p(&ifnum); devinfo->ctl_write.wIndex = cpu_to_le16(devinfo->ifnum);
/* CTL Read */ /* CTL Read */
devinfo->ctl_read.bRequestType = devinfo->ctl_read.bRequestType =
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE; USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
devinfo->ctl_read.bRequest = 1; devinfo->ctl_read.bRequest = 1;
devinfo->ctl_read.wValue = cpu_to_le16(0); devinfo->ctl_read.wValue = cpu_to_le16(0);
devinfo->ctl_read.wIndex = cpu_to_le16p(&ifnum); devinfo->ctl_read.wIndex = cpu_to_le16(devinfo->ifnum);
} }
brcmf_usb_rx_fill_all(devinfo); brcmf_usb_rx_fill_all(devinfo);
return 0; return 0;
...@@ -643,19 +632,19 @@ brcmf_usb_sync_complete(struct urb *urb) ...@@ -643,19 +632,19 @@ brcmf_usb_sync_complete(struct urb *urb)
brcmf_usb_ioctl_resp_wake(devinfo); brcmf_usb_ioctl_resp_wake(devinfo);
} }
static bool brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd, static int brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd,
void *buffer, int buflen) void *buffer, int buflen)
{ {
int ret = 0; int ret;
char *tmpbuf; char *tmpbuf;
u16 size; u16 size;
if ((!devinfo) || (devinfo->ctl_urb == NULL)) if ((!devinfo) || (devinfo->ctl_urb == NULL))
return false; return -EINVAL;
tmpbuf = kmalloc(buflen, GFP_ATOMIC); tmpbuf = kmalloc(buflen, GFP_ATOMIC);
if (!tmpbuf) if (!tmpbuf)
return false; return -ENOMEM;
size = buflen; size = buflen;
devinfo->ctl_urb->transfer_buffer_length = size; devinfo->ctl_urb->transfer_buffer_length = size;
...@@ -676,14 +665,16 @@ static bool brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd, ...@@ -676,14 +665,16 @@ static bool brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd,
ret = usb_submit_urb(devinfo->ctl_urb, GFP_ATOMIC); ret = usb_submit_urb(devinfo->ctl_urb, GFP_ATOMIC);
if (ret < 0) { if (ret < 0) {
brcmf_err("usb_submit_urb failed %d\n", ret); brcmf_err("usb_submit_urb failed %d\n", ret);
kfree(tmpbuf); goto finalize;
return false;
} }
ret = brcmf_usb_ioctl_resp_wait(devinfo); if (!brcmf_usb_ioctl_resp_wait(devinfo))
memcpy(buffer, tmpbuf, buflen); ret = -ETIMEDOUT;
kfree(tmpbuf); else
memcpy(buffer, tmpbuf, buflen);
finalize:
kfree(tmpbuf);
return ret; return ret;
} }
...@@ -725,6 +716,7 @@ brcmf_usb_resetcfg(struct brcmf_usbdev_info *devinfo) ...@@ -725,6 +716,7 @@ brcmf_usb_resetcfg(struct brcmf_usbdev_info *devinfo)
{ {
struct bootrom_id_le id; struct bootrom_id_le id;
u32 loop_cnt; u32 loop_cnt;
int err;
brcmf_dbg(USB, "Enter\n"); brcmf_dbg(USB, "Enter\n");
...@@ -733,7 +725,9 @@ brcmf_usb_resetcfg(struct brcmf_usbdev_info *devinfo) ...@@ -733,7 +725,9 @@ brcmf_usb_resetcfg(struct brcmf_usbdev_info *devinfo)
mdelay(BRCMF_USB_RESET_GETVER_SPINWAIT); mdelay(BRCMF_USB_RESET_GETVER_SPINWAIT);
loop_cnt++; loop_cnt++;
id.chip = cpu_to_le32(0xDEAD); /* Get the ID */ id.chip = cpu_to_le32(0xDEAD); /* Get the ID */
brcmf_usb_dl_cmd(devinfo, DL_GETVER, &id, sizeof(id)); err = brcmf_usb_dl_cmd(devinfo, DL_GETVER, &id, sizeof(id));
if ((err) && (err != -ETIMEDOUT))
return err;
if (id.chip == cpu_to_le32(BRCMF_POSTBOOT_ID)) if (id.chip == cpu_to_le32(BRCMF_POSTBOOT_ID))
break; break;
} while (loop_cnt < BRCMF_USB_RESET_GETVER_LOOP_CNT); } while (loop_cnt < BRCMF_USB_RESET_GETVER_LOOP_CNT);
...@@ -795,8 +789,7 @@ brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen) ...@@ -795,8 +789,7 @@ brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen)
} }
/* 1) Prepare USB boot loader for runtime image */ /* 1) Prepare USB boot loader for runtime image */
brcmf_usb_dl_cmd(devinfo, DL_START, &state, brcmf_usb_dl_cmd(devinfo, DL_START, &state, sizeof(state));
sizeof(struct rdl_state_le));
rdlstate = le32_to_cpu(state.state); rdlstate = le32_to_cpu(state.state);
rdlbytes = le32_to_cpu(state.bytes); rdlbytes = le32_to_cpu(state.bytes);
...@@ -840,10 +833,10 @@ brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen) ...@@ -840,10 +833,10 @@ brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen)
dlpos += sendlen; dlpos += sendlen;
sent += sendlen; sent += sendlen;
} }
if (!brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state, err = brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state,
sizeof(struct rdl_state_le))) { sizeof(state));
brcmf_err("DL_GETSTATE Failed xxxx\n"); if (err) {
err = -EINVAL; brcmf_err("DL_GETSTATE Failed\n");
goto fail; goto fail;
} }
...@@ -899,13 +892,12 @@ static int brcmf_usb_dlrun(struct brcmf_usbdev_info *devinfo) ...@@ -899,13 +892,12 @@ static int brcmf_usb_dlrun(struct brcmf_usbdev_info *devinfo)
return -EINVAL; return -EINVAL;
/* Check we are runnable */ /* Check we are runnable */
brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state, state.state = 0;
sizeof(struct rdl_state_le)); brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state, sizeof(state));
/* Start the image */ /* Start the image */
if (state.state == cpu_to_le32(DL_RUNNABLE)) { if (state.state == cpu_to_le32(DL_RUNNABLE)) {
if (!brcmf_usb_dl_cmd(devinfo, DL_GO, &state, if (brcmf_usb_dl_cmd(devinfo, DL_GO, &state, sizeof(state)))
sizeof(struct rdl_state_le)))
return -ENODEV; return -ENODEV;
if (brcmf_usb_resetcfg(devinfo)) if (brcmf_usb_resetcfg(devinfo))
return -ENODEV; return -ENODEV;
...@@ -1228,13 +1220,13 @@ brcmf_usb_disconnect_cb(struct brcmf_usbdev_info *devinfo) ...@@ -1228,13 +1220,13 @@ brcmf_usb_disconnect_cb(struct brcmf_usbdev_info *devinfo)
static int static int
brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
{ {
int ep;
struct usb_endpoint_descriptor *endpoint;
int ret = 0;
struct usb_device *usb = interface_to_usbdev(intf); struct usb_device *usb = interface_to_usbdev(intf);
int num_of_eps;
u8 endpoint_num;
struct brcmf_usbdev_info *devinfo; struct brcmf_usbdev_info *devinfo;
struct usb_interface_descriptor *desc;
struct usb_endpoint_descriptor *endpoint;
int ret = 0;
u32 num_of_eps;
u8 endpoint_num, ep;
brcmf_dbg(USB, "Enter 0x%04x:0x%04x\n", id->idVendor, id->idProduct); brcmf_dbg(USB, "Enter 0x%04x:0x%04x\n", id->idVendor, id->idProduct);
...@@ -1244,92 +1236,71 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) ...@@ -1244,92 +1236,71 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
devinfo->usbdev = usb; devinfo->usbdev = usb;
devinfo->dev = &usb->dev; devinfo->dev = &usb->dev;
usb_set_intfdata(intf, devinfo); usb_set_intfdata(intf, devinfo);
/* Check that the device supports only one configuration */ /* Check that the device supports only one configuration */
if (usb->descriptor.bNumConfigurations != 1) { if (usb->descriptor.bNumConfigurations != 1) {
ret = -1; brcmf_err("Number of configurations: %d not supported\n",
goto fail; usb->descriptor.bNumConfigurations);
} ret = -ENODEV;
if (usb->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) {
ret = -1;
goto fail;
}
/*
* Only the BDC interface configuration is supported:
* Device class: USB_CLASS_VENDOR_SPEC
* if0 class: USB_CLASS_VENDOR_SPEC
* if0/ep0: control
* if0/ep1: bulk in
* if0/ep2: bulk out (ok if swapped with bulk in)
*/
if (CONFIGDESC(usb)->bNumInterfaces != 1) {
ret = -1;
goto fail; goto fail;
} }
/* Check interface */ if ((usb->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) &&
if (IFDESC(usb, CONTROL_IF).bInterfaceClass != USB_CLASS_VENDOR_SPEC || (usb->descriptor.bDeviceClass != USB_CLASS_MISC) &&
IFDESC(usb, CONTROL_IF).bInterfaceSubClass != 2 || (usb->descriptor.bDeviceClass != USB_CLASS_WIRELESS_CONTROLLER)) {
IFDESC(usb, CONTROL_IF).bInterfaceProtocol != 0xff) { brcmf_err("Device class: 0x%x not supported\n",
brcmf_err("invalid control interface: class %d, subclass %d, proto %d\n", usb->descriptor.bDeviceClass);
IFDESC(usb, CONTROL_IF).bInterfaceClass, ret = -ENODEV;
IFDESC(usb, CONTROL_IF).bInterfaceSubClass,
IFDESC(usb, CONTROL_IF).bInterfaceProtocol);
ret = -1;
goto fail; goto fail;
} }
/* Check control endpoint */ desc = &intf->altsetting[0].desc;
endpoint = &IFEPDESC(usb, CONTROL_IF, 0); if ((desc->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) (desc->bInterfaceSubClass != 2) ||
!= USB_ENDPOINT_XFER_INT) { (desc->bInterfaceProtocol != 0xff)) {
brcmf_err("invalid control endpoint %d\n", brcmf_err("non WLAN interface %d: 0x%x:0x%x:0x%x\n",
endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); desc->bInterfaceNumber, desc->bInterfaceClass,
ret = -1; desc->bInterfaceSubClass, desc->bInterfaceProtocol);
ret = -ENODEV;
goto fail; goto fail;
} }
devinfo->rx_pipe = 0; num_of_eps = desc->bNumEndpoints;
devinfo->rx_pipe2 = 0; for (ep = 0; ep < num_of_eps; ep++) {
devinfo->tx_pipe = 0; endpoint = &intf->altsetting[0].endpoint[ep].desc;
num_of_eps = IFDESC(usb, BULK_IF).bNumEndpoints - 1; endpoint_num = usb_endpoint_num(endpoint);
if (!usb_endpoint_xfer_bulk(endpoint))
/* Check data endpoints and get pipes */ continue;
for (ep = 1; ep <= num_of_eps; ep++) { if (usb_endpoint_dir_in(endpoint)) {
endpoint = &IFEPDESC(usb, BULK_IF, ep); if (!devinfo->rx_pipe)
if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
USB_ENDPOINT_XFER_BULK) {
brcmf_err("invalid data endpoint %d\n", ep);
ret = -1;
goto fail;
}
endpoint_num = endpoint->bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
== USB_DIR_IN) {
if (!devinfo->rx_pipe) {
devinfo->rx_pipe = devinfo->rx_pipe =
usb_rcvbulkpipe(usb, endpoint_num); usb_rcvbulkpipe(usb, endpoint_num);
} else {
devinfo->rx_pipe2 =
usb_rcvbulkpipe(usb, endpoint_num);
}
} else { } else {
devinfo->tx_pipe = usb_sndbulkpipe(usb, endpoint_num); if (!devinfo->tx_pipe)
devinfo->tx_pipe =
usb_sndbulkpipe(usb, endpoint_num);
} }
} }
if (devinfo->rx_pipe == 0) {
brcmf_err("No RX (in) Bulk EP found\n");
ret = -ENODEV;
goto fail;
}
if (devinfo->tx_pipe == 0) {
brcmf_err("No TX (out) Bulk EP found\n");
ret = -ENODEV;
goto fail;
}
devinfo->ifnum = desc->bInterfaceNumber;
if (usb->speed == USB_SPEED_SUPER) if (usb->speed == USB_SPEED_SUPER)
brcmf_dbg(USB, "Broadcom super speed USB wireless device detected\n"); brcmf_dbg(USB, "Broadcom super speed USB WLAN interface detected\n");
else if (usb->speed == USB_SPEED_HIGH) else if (usb->speed == USB_SPEED_HIGH)
brcmf_dbg(USB, "Broadcom high speed USB wireless device detected\n"); brcmf_dbg(USB, "Broadcom high speed USB WLAN interface detected\n");
else else
brcmf_dbg(USB, "Broadcom full speed USB wireless device detected\n"); brcmf_dbg(USB, "Broadcom full speed USB WLAN interface detected\n");
ret = brcmf_usb_probe_cb(devinfo); ret = brcmf_usb_probe_cb(devinfo);
if (ret) if (ret)
...@@ -1339,11 +1310,9 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) ...@@ -1339,11 +1310,9 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
return 0; return 0;
fail: fail:
brcmf_err("failed with errno %d\n", ret);
kfree(devinfo); kfree(devinfo);
usb_set_intfdata(intf, NULL); usb_set_intfdata(intf, NULL);
return ret; return ret;
} }
static void static void
...@@ -1388,6 +1357,7 @@ static int brcmf_usb_reset_resume(struct usb_interface *intf) ...@@ -1388,6 +1357,7 @@ static int brcmf_usb_reset_resume(struct usb_interface *intf)
{ {
struct usb_device *usb = interface_to_usbdev(intf); struct usb_device *usb = interface_to_usbdev(intf);
struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev); struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev);
brcmf_dbg(USB, "Enter\n"); brcmf_dbg(USB, "Enter\n");
return brcmf_fw_get_firmwares(&usb->dev, 0, return brcmf_fw_get_firmwares(&usb->dev, 0,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment