| /** @file moal_usb.c |
| * |
| * @brief This file contains the interfaceing to USB bus |
| * driver. |
| * |
| * |
| * Copyright 2008-2021 NXP |
| * |
| * This software file (the File) is distributed by NXP |
| * under the terms of the GNU General Public License Version 2, June 1991 |
| * (the License). You may use, redistribute and/or modify the File in |
| * accordance with the terms and conditions of the License, a copy of which |
| * is available by writing to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the |
| * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. |
| * |
| * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE |
| * ARE EXPRESSLY DISCLAIMED. The License provides additional details about |
| * this warranty disclaimer. |
| * |
| */ |
| |
| /******************************************************** |
| Change log: |
| 10/21/2008: initial version |
| ********************************************************/ |
| |
| #include "moal_main.h" |
| #include "moal_usb.h" |
| extern struct semaphore AddRemoveCardSem; |
| |
| /******************************************************** |
| Local Variables |
| ********************************************************/ |
| |
| #if defined(USB8997) || defined(USB9098) || defined(USB9097) || defined(USB8978) |
| /** Card-type detection frame response */ |
| typedef struct { |
| /** 32-bit ACK+WINNER field */ |
| t_u32 ack_winner; |
| /** 32-bit Sequence number */ |
| t_u32 seq; |
| /** 32-bit extend */ |
| t_u32 extend; |
| /** 32-bit chip-revision code */ |
| t_u32 chip_rev; |
| /** 32-bit strap setting */ |
| t_u32 strap; |
| } usb_ack_pkt; |
| #endif |
| |
| /** NXP USB device */ |
| #define NXP_USB_DEVICE(vid, pid, name) \ |
| USB_DEVICE(vid, pid), .driver_info = (t_ptr)name |
| |
| /** Name of the USB driver */ |
| static const char usbdriver_name[] = "usbxxx"; |
| |
| /** This structure contains the device signature */ |
| static struct usb_device_id woal_usb_table[] = { |
| /* Enter the device signature inside */ |
| #ifdef USB8801 |
| {NXP_USB_DEVICE(USB8801_VID_1, USB8801_PID_1, "NXP WLAN USB Adapter")}, |
| {NXP_USB_DEVICE(USB8801_VID_1, USB8801_PID_2, "NXP WLAN USB Adapter")}, |
| #endif |
| #ifdef USB8897 |
| {NXP_USB_DEVICE(USB8897_VID_1, USB8897_PID_1, "NXP WLAN USB Adapter")}, |
| {NXP_USB_DEVICE(USB8897_VID_1, USB8897_PID_2, "NXP WLAN USB Adapter")}, |
| #endif |
| #ifdef USB8997 |
| {NXP_USB_DEVICE(USB8997_VID_1, USB8997_PID_1, "NXP WLAN USB Adapter")}, |
| {NXP_USB_DEVICE(USB8997_VID_1, USB8997V2_PID_1, |
| "NXP WLAN USB Adapter")}, |
| {NXP_USB_DEVICE(USB8997_VID_1, USB8997_PID_2, "NXP WLAN USB Adapter")}, |
| {NXP_USB_DEVICE(USB8997_VID_1, USB8997_PID_3, "NXP WLAN USB Adapter")}, |
| {NXP_USB_DEVICE(USB8997_VID_1, USB8997_PID_4, "NXP WLAN USB Adapter")}, |
| {NXP_USB_DEVICE(USB8997_VID_1, USB8997_PID_5, "NXP WLAN USB Adapter")}, |
| {NXP_USB_DEVICE(USB8997_VID_1, USB8997_PID_6, "NXP WLAN USB Adapter")}, |
| #endif |
| #ifdef USB8978 |
| {NXP_USB_DEVICE(USB8978_VID_1, USB8978_PID_1, "NXP WLAN USB Adapter")}, |
| {NXP_USB_DEVICE(USB8978_VID_1, USB8978_PID_1_BT, |
| "NXP WLAN USB Adapter")}, |
| {NXP_USB_DEVICE(USB8978_VID_1, USB8978_PID_2, "NXP WLAN USB Adapter")}, |
| {NXP_USB_DEVICE(USB8978_VID_1, USB8978_PID_2_BT, |
| "NXP WLAN USB Adapter")}, |
| #endif |
| #ifdef USB9098 |
| {NXP_USB_DEVICE(USB9098_VID_1, USB9098_PID_1, "NXP WLAN USB Adapter")}, |
| {NXP_USB_DEVICE(USB9098_VID_1, USB9098_PID_2, "NXP WLAN USB Adapter")}, |
| #endif |
| #ifdef USB9097 |
| {NXP_USB_DEVICE(USB9097_VID_1, USB9097_PID_1, "NXP WLAN USB Adapter")}, |
| {NXP_USB_DEVICE(USB9097_VID_1, USB9097_PID_2, "NXP WLAN USB Adapter")}, |
| #endif |
| /* Terminating entry */ |
| {}, |
| }; |
| |
| /** This structure contains the device signature */ |
| static struct usb_device_id woal_usb_table_skip_fwdnld[] = { |
| /* Enter the device signature inside */ |
| #ifdef USB8801 |
| {NXP_USB_DEVICE(USB8801_VID_1, USB8801_PID_2, "NXP WLAN USB Adapter")}, |
| #endif |
| #ifdef USB8897 |
| {NXP_USB_DEVICE(USB8897_VID_1, USB8897_PID_2, "NXP WLAN USB Adapter")}, |
| #endif |
| #ifdef USB8997 |
| {NXP_USB_DEVICE(USB8997_VID_1, USB8997_PID_2, "NXP WLAN USB Adapter")}, |
| #endif |
| #ifdef USB8978 |
| {NXP_USB_DEVICE(USB8978_VID_1, USB8978_PID_2, "NXP WLAN USB Adapter")}, |
| {NXP_USB_DEVICE(USB8978_VID_1, USB8978_PID_2_BT, |
| "NXP WLAN USB Adapter")}, |
| #endif |
| #ifdef USB9098 |
| {NXP_USB_DEVICE(USB9098_VID_1, USB9098_PID_2, "NXP WLAN USB Adapter")}, |
| #endif |
| #ifdef USB9097 |
| {NXP_USB_DEVICE(USB9097_VID_1, USB9097_PID_2, "NXP WLAN USB Adapter")}, |
| #endif |
| /* Terminating entry */ |
| {}, |
| }; |
| |
| static mlan_status woal_usb_submit_rx_urb(urb_context *ctx, int size); |
| static int woal_usb_probe(struct usb_interface *intf, |
| const struct usb_device_id *id); |
| static void woal_usb_disconnect(struct usb_interface *intf); |
| static mlan_status woal_usb_write_data_sync(moal_handle *handle, |
| mlan_buffer *pmbuf, t_u32 endpoint, |
| t_u32 timeout); |
| static mlan_status woal_usb_read_data_sync(moal_handle *handle, |
| mlan_buffer *pmbuf, t_u32 endpoint, |
| t_u32 timeout); |
| #ifdef CONFIG_PM |
| static int woal_usb_suspend(struct usb_interface *intf, pm_message_t message); |
| static int woal_usb_resume(struct usb_interface *intf); |
| #endif /* CONFIG_PM */ |
| |
| /** woal_usb_driver */ |
| static struct usb_driver REFDATA woal_usb_driver = { |
| /* Driver name */ |
| .name = usbdriver_name, |
| |
| /* Probe function name */ |
| .probe = woal_usb_probe, |
| |
| /* Disconnect function name */ |
| .disconnect = woal_usb_disconnect, |
| |
| /* Device signature table */ |
| .id_table = woal_usb_table, |
| #ifdef CONFIG_PM |
| /* Suspend function name */ |
| .suspend = woal_usb_suspend, |
| |
| /* Resume function name */ |
| .resume = woal_usb_resume, |
| |
| /* Reset resume function name */ |
| .reset_resume = woal_usb_resume, |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) |
| /* Driver supports autosuspend */ |
| .supports_autosuspend = 1, |
| #endif |
| #endif /* CONFIG_PM */ |
| }; |
| |
| MODULE_DEVICE_TABLE(usb, woal_usb_table); |
| MODULE_DEVICE_TABLE(usb, woal_usb_table_skip_fwdnld); |
| |
| /* moal interface ops */ |
| static moal_if_ops usb_ops; |
| |
| /******************************************************** |
| Global Variables |
| ********************************************************/ |
| |
| /******************************************************** |
| Local Functions |
| ********************************************************/ |
| |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) |
| /** |
| * @brief This function receive packet of the data/cmd/event packet |
| * and pass to MLAN |
| * |
| * @param urb Pointer to struct urb |
| * @param regs Registers |
| * |
| * @return N/A |
| */ |
| static void |
| woal_usb_receive(struct urb *urb, struct pt_regs *regs) |
| #else |
| /** |
| * @brief This function receive packet of the data/cmd/event packet |
| * and pass to MLAN |
| * |
| * @param urb Pointer to struct urb |
| * |
| * @return N/A |
| */ |
| static void |
| woal_usb_receive(struct urb *urb) |
| #endif |
| { |
| urb_context *context = NULL; |
| moal_handle *handle = NULL; |
| mlan_buffer *pmbuf = NULL; |
| struct usb_card_rec *cardp = NULL; |
| int recv_length; |
| int size; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| if (!urb || !urb->context) { |
| PRINTM(MERROR, "URB or URB context is not valid in USB Rx\n"); |
| LEAVE(); |
| return; |
| } |
| context = (urb_context *)urb->context; |
| handle = context->handle; |
| pmbuf = context->pmbuf; |
| recv_length = urb->actual_length; |
| |
| if (!handle || !handle->card || !pmbuf) { |
| PRINTM(MERROR, |
| "moal handle, card structure or mlan_buffer is not valid in USB Rx\n"); |
| LEAVE(); |
| return; |
| } |
| cardp = (struct usb_card_rec *)handle->card; |
| if (cardp->rx_cmd_ep == context->ep) |
| atomic_dec(&cardp->rx_cmd_urb_pending); |
| else |
| atomic_dec(&cardp->rx_data_urb_pending); |
| |
| if (recv_length) { |
| if (urb->status || (handle->surprise_removed == MTRUE)) { |
| if (handle->surprise_removed || handle->is_suspended) { |
| woal_free_mlan_buffer(handle, pmbuf); |
| context->pmbuf = NULL; |
| goto rx_exit; |
| } else { |
| PRINTM(MERROR, |
| "EP %d Rx URB status failure: %d\n", |
| context->ep, urb->status); |
| /* Do not free mlan_buffer in case of command ep |
| */ |
| if (cardp->rx_cmd_ep != context->ep) |
| woal_free_mlan_buffer(handle, pmbuf); |
| goto setup_for_next; |
| } |
| } |
| pmbuf->data_len = recv_length; |
| pmbuf->flags |= MLAN_BUF_FLAG_RX_DEAGGR; |
| /* Send packet to MLAN */ |
| atomic_inc(&handle->rx_pending); |
| status = mlan_recv(handle->pmlan_adapter, pmbuf, context->ep); |
| PRINTM(MINFO, "Receive length = 0x%x, status=%d\n", recv_length, |
| status); |
| if (status == MLAN_STATUS_PENDING) { |
| queue_work(handle->workqueue, &handle->main_work); |
| /* urb for data_ep is re-submitted now, unless we reach |
| * USB_HIGH_RX_PENDING */ |
| /* urb for cmd_ep will be re-submitted in callback |
| * moal_recv_complete */ |
| if (cardp->rx_cmd_ep == context->ep) |
| goto rx_exit; |
| else if (atomic_read(&handle->rx_pending) >= |
| USB_HIGH_RX_PENDING) { |
| context->pmbuf = NULL; |
| goto rx_exit; |
| } |
| } else { |
| atomic_dec(&handle->rx_pending); |
| if (status == MLAN_STATUS_FAILURE) { |
| PRINTM(MERROR, |
| "MLAN fail to process the receive data\n"); |
| } else if ((status == MLAN_STATUS_SUCCESS) && |
| (pmbuf->flags & |
| MLAN_BUF_FLAG_SLEEPCFM_RESP)) { |
| pmbuf->flags &= ~MLAN_BUF_FLAG_SLEEPCFM_RESP; |
| queue_work(handle->workqueue, |
| &handle->main_work); |
| } |
| /* Do not free mlan_buffer in case of command ep */ |
| if (cardp->rx_cmd_ep != context->ep) |
| woal_free_mlan_buffer(handle, pmbuf); |
| } |
| } else if (urb->status) { |
| if (!((cardp->rx_data_ep == context->ep) && |
| (cardp->resubmit_urbs == 1))) { |
| if (!handle->is_suspended) { |
| PRINTM(MMSG, "Card is removed: %d\n", |
| urb->status); |
| handle->surprise_removed = MTRUE; |
| } |
| } |
| woal_free_mlan_buffer(handle, pmbuf); |
| context->pmbuf = NULL; |
| goto rx_exit; |
| } else { |
| /* Do not free mlan_buffer in case of command ep */ |
| if (cardp->rx_cmd_ep != context->ep) |
| woal_free_mlan_buffer(handle, pmbuf); |
| goto setup_for_next; |
| } |
| |
| setup_for_next: |
| if (cardp->rx_cmd_ep == context->ep) { |
| size = MLAN_RX_CMD_BUF_SIZE; |
| } else { |
| if (cardp->rx_deaggr_ctrl.enable) { |
| size = cardp->rx_deaggr_ctrl.aggr_max; |
| if (cardp->rx_deaggr_ctrl.aggr_mode == |
| MLAN_USB_AGGR_MODE_NUM) { |
| size *= MAX(MLAN_USB_MAX_PKT_SIZE, |
| cardp->rx_deaggr_ctrl.aggr_align); |
| size = MAX(size, MLAN_RX_DATA_BUF_SIZE); |
| } |
| } else |
| size = MLAN_RX_DATA_BUF_SIZE; |
| } |
| woal_usb_submit_rx_urb(context, size); |
| |
| rx_exit: |
| LEAVE(); |
| return; |
| } |
| |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) |
| /** |
| * @brief Call back function to handle the status of the Tx data URB |
| * |
| * @param urb Pointer to urb structure |
| * @param regs Registers |
| * |
| * @return N/A |
| */ |
| static void |
| woal_usb_tx_complete(struct urb *urb, struct pt_regs *regs) |
| #else |
| /** |
| * @brief Call back function to handle the status of the Tx data URB |
| * |
| * @param urb Pointer to urb structure |
| * |
| * @return N/A |
| */ |
| static void |
| woal_usb_tx_complete(struct urb *urb) |
| #endif |
| { |
| urb_context *context = NULL; |
| moal_handle *handle = NULL; |
| struct usb_card_rec *cardp = NULL; |
| |
| ENTER(); |
| |
| if (!urb || !urb->context) { |
| PRINTM(MERROR, |
| "URB or URB context is not valid in USB Tx complete\n"); |
| LEAVE(); |
| return; |
| } |
| context = (urb_context *)urb->context; |
| handle = context->handle; |
| |
| if (!handle || !handle->card || !context->pmbuf) { |
| PRINTM(MERROR, |
| "moal handle, card structure or mlan_buffer is not valid in USB Tx complete\n"); |
| LEAVE(); |
| return; |
| } |
| cardp = handle->card; |
| |
| /* Handle the transmission complete validations */ |
| if (urb->status) { |
| PRINTM(MERROR, "EP %d Tx URB status failure: %d\n", context->ep, |
| urb->status); |
| mlan_write_data_async_complete(handle->pmlan_adapter, |
| context->pmbuf, context->ep, |
| MLAN_STATUS_FAILURE); |
| } else { |
| mlan_write_data_async_complete(handle->pmlan_adapter, |
| context->pmbuf, context->ep, |
| MLAN_STATUS_SUCCESS); |
| } |
| |
| /* Decrease pending URB counter */ |
| if (context->ep == cardp->tx_cmd_ep) |
| atomic_dec(&cardp->tx_cmd_urb_pending); |
| else if (context->ep == cardp->tx_data_ep) |
| atomic_dec(&cardp->tx_data_urb_pending); |
| |
| queue_work(handle->workqueue, &handle->main_work); |
| |
| LEAVE(); |
| return; |
| } |
| |
| /** |
| * @brief This function sets up the data to receive |
| * |
| * @param ctx Pointer to urb_context structure |
| * @param size Skb size |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| static mlan_status |
| woal_usb_submit_rx_urb(urb_context *ctx, int size) |
| { |
| moal_handle *handle = ctx->handle; |
| struct usb_card_rec *cardp = (struct usb_card_rec *)handle->card; |
| mlan_status ret = MLAN_STATUS_FAILURE; |
| t_u8 *data = NULL; |
| |
| ENTER(); |
| |
| if (handle->surprise_removed || handle->is_suspended) { |
| if ((cardp->rx_cmd_ep == ctx->ep) && ctx->pmbuf) { |
| woal_free_mlan_buffer(handle, ctx->pmbuf); |
| ctx->pmbuf = NULL; |
| } |
| PRINTM(MERROR, |
| "Card removed/suspended, EP %d Rx URB submit skipped\n", |
| ctx->ep); |
| goto rx_ret; |
| } |
| |
| if (cardp->rx_cmd_ep != ctx->ep) { |
| ctx->pmbuf = woal_alloc_mlan_buffer(handle, size); |
| if (!ctx->pmbuf) { |
| PRINTM(MERROR, |
| "Fail to submit Rx URB due to no memory/skb\n"); |
| goto rx_ret; |
| } |
| ctx->pmbuf->data_offset = MLAN_RX_HEADER_LEN; |
| data = ctx->pmbuf->pbuf + ctx->pmbuf->data_offset; |
| } else { |
| ctx->pmbuf->data_offset = 0; |
| data = ctx->pmbuf->pbuf + ctx->pmbuf->data_offset; |
| } |
| |
| if (cardp->rx_cmd_ep == ctx->ep && |
| cardp->rx_cmd_ep_type == USB_ENDPOINT_XFER_INT) |
| usb_fill_int_urb(ctx->urb, cardp->udev, |
| usb_rcvintpipe(cardp->udev, ctx->ep), data, |
| size - ctx->pmbuf->data_offset, |
| woal_usb_receive, (void *)ctx, |
| cardp->rx_cmd_interval); |
| else |
| usb_fill_bulk_urb(ctx->urb, cardp->udev, |
| usb_rcvbulkpipe(cardp->udev, ctx->ep), data, |
| size - ctx->pmbuf->data_offset, |
| woal_usb_receive, (void *)ctx); |
| if (cardp->rx_cmd_ep == ctx->ep) |
| atomic_inc(&cardp->rx_cmd_urb_pending); |
| else |
| atomic_inc(&cardp->rx_data_urb_pending); |
| if (usb_submit_urb(ctx->urb, GFP_ATOMIC)) { |
| /* Submit URB failure */ |
| PRINTM(MERROR, "Submit EP %d Rx URB failed: %d\n", ctx->ep, |
| ret); |
| woal_free_mlan_buffer(handle, ctx->pmbuf); |
| if (cardp->rx_cmd_ep == ctx->ep) |
| atomic_dec(&cardp->rx_cmd_urb_pending); |
| else |
| atomic_dec(&cardp->rx_data_urb_pending); |
| ctx->pmbuf = NULL; |
| ret = MLAN_STATUS_FAILURE; |
| } else { |
| ret = MLAN_STATUS_SUCCESS; |
| } |
| rx_ret: |
| LEAVE(); |
| return ret; |
| } |
| |
| /******************************************************** |
| Global Functions |
| ********************************************************/ |
| |
| #if defined(USB8997) || defined(USB9098) || defined(USB9097) || defined(USB8978) |
| /** |
| * @brief Check chip revision |
| * |
| * @param handle A pointer to moal_handle structure |
| * @param usb_chip_rev A pointer to usb_chip_rev variable |
| * @param usb_strap A pointer to usb_strap |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| static mlan_status |
| woal_check_chip_revision(moal_handle *handle, t_u32 *usb_chip_rev, |
| t_u32 *usb_strap) |
| { |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| mlan_buffer mbuf; |
| t_u8 *tx_buff = NULL; |
| t_u8 *recv_buff = NULL; |
| usb_ack_pkt ack_pkt; |
| t_u32 extend_ver; |
| t_u8 tx_size = CHIP_REV_TX_BUF_SIZE; |
| struct usb_card_rec *cardp = handle->card; |
| |
| ENTER(); |
| |
| /* Allocate memory for transmit */ |
| tx_buff = kzalloc(tx_size, GFP_ATOMIC | GFP_DMA); |
| if (tx_buff == NULL) { |
| PRINTM(MERROR, |
| "Could not allocate buffer for chip revision check frame transmission\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto cleanup; |
| } |
| |
| /* Allocate memory for receive */ |
| recv_buff = kzalloc(CHIP_REV_RX_BUF_SIZE, GFP_ATOMIC | GFP_DMA); |
| if (recv_buff == NULL) { |
| PRINTM(MERROR, |
| "Could not allocate buffer for chip revision check frame response\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto cleanup; |
| } |
| |
| /* The struct is initialised to all zero */ |
| memset(&ack_pkt, 0, sizeof(usb_ack_pkt)); |
| |
| /* Send pseudo data to check winner status first */ |
| memset(&mbuf, 0, sizeof(mlan_buffer)); |
| mbuf.pbuf = (t_u8 *)tx_buff; |
| mbuf.data_len = tx_size; |
| |
| /* Send the chip revision check frame */ |
| ret = woal_usb_write_data_sync(handle, &mbuf, cardp->tx_cmd_ep, |
| MLAN_USB_BULK_MSG_TIMEOUT); |
| if (ret != MLAN_STATUS_SUCCESS) { |
| PRINTM(MERROR, |
| "Chip revision check frame dnld: write_data failed, ret %d\n", |
| ret); |
| ret = MLAN_STATUS_FAILURE; |
| goto cleanup; |
| } |
| |
| memset(&mbuf, 0, sizeof(mlan_buffer)); |
| mbuf.pbuf = (t_u8 *)recv_buff; |
| mbuf.data_len = CHIP_REV_RX_BUF_SIZE; |
| |
| /* Receive the chip revision check frame response */ |
| ret = woal_usb_read_data_sync(handle, &mbuf, cardp->rx_cmd_ep, |
| MLAN_USB_BULK_MSG_TIMEOUT); |
| if (ret != MLAN_STATUS_SUCCESS) { |
| PRINTM(MERROR, |
| "Chip revision check frame response: read_data failed, ret %d\n", |
| ret); |
| ret = MLAN_STATUS_FAILURE; |
| goto cleanup; |
| } |
| moal_memcpy_ext(handle, &ack_pkt, recv_buff, |
| sizeof(usb_ack_pkt), sizeof(ack_pkt)); |
| ack_pkt.ack_winner = woal_le32_to_cpu(ack_pkt.ack_winner); |
| ack_pkt.seq = woal_le32_to_cpu(ack_pkt.seq); |
| ack_pkt.extend = woal_le32_to_cpu(ack_pkt.extend); |
| ack_pkt.chip_rev = woal_le32_to_cpu(ack_pkt.chip_rev); |
| ack_pkt.strap = woal_le32_to_cpu(ack_pkt.strap); |
| |
| if ((ack_pkt.extend & 0xffff0000) == EXTEND_HDR) { |
| extend_ver = ack_pkt.extend & 0x0000ffff; |
| *usb_chip_rev = ack_pkt.chip_rev & 0x000000ff; |
| if (extend_ver >= EXTEND_V2) { |
| PRINTM(MINFO, "chip_rev=0x%x, strap=0x%x\n", |
| *usb_chip_rev, ack_pkt.strap); |
| *usb_strap = ack_pkt.strap & 0x7; |
| } else |
| PRINTM(MINFO, "chip_rev=0x%x\n", *usb_chip_rev); |
| } |
| cleanup: |
| kfree(recv_buff); |
| kfree(tx_buff); |
| |
| LEAVE(); |
| return ret; |
| } |
| #endif |
| |
| /** |
| * @brief This function unlink urb |
| * |
| * @param handle A pointer to moal_handle structure |
| * @return N/A |
| */ |
| static void |
| woal_usb_unlink_urb(void *card_desc) |
| { |
| struct usb_card_rec *cardp = (struct usb_card_rec *)card_desc; |
| int i; |
| ENTER(); |
| if (cardp) { |
| /* Unlink Rx cmd URB */ |
| if (atomic_read(&cardp->rx_cmd_urb_pending) && |
| cardp->rx_cmd.urb) { |
| usb_kill_urb(cardp->rx_cmd.urb); |
| } |
| /* Unlink Rx data URBs */ |
| if (atomic_read(&cardp->rx_data_urb_pending)) { |
| for (i = 0; i < MVUSB_RX_DATA_URB; i++) { |
| if (cardp->rx_data_list[i].urb) |
| usb_kill_urb(cardp->rx_data_list[i]. |
| urb); |
| } |
| } |
| /* Unlink Tx cmd URB */ |
| if (atomic_read(&cardp->tx_cmd_urb_pending) && |
| cardp->tx_cmd.urb) { |
| usb_kill_urb(cardp->tx_cmd.urb); |
| } |
| /* Unlink Tx data URBs */ |
| if (atomic_read(&cardp->tx_data_urb_pending)) { |
| for (i = 0; i < MVUSB_TX_HIGH_WMARK; i++) { |
| if (cardp->tx_data_list[i].urb) { |
| usb_kill_urb(cardp->tx_data_list[i]. |
| urb); |
| } |
| } |
| } |
| } |
| LEAVE(); |
| } |
| |
| /** |
| * @brief Free Tx/Rx urb, skb and Rx buffer |
| * |
| * @param cardp Pointer usb_card_rec |
| * |
| * @return N/A |
| */ |
| void |
| woal_usb_free(struct usb_card_rec *cardp) |
| { |
| int i; |
| |
| ENTER(); |
| |
| woal_usb_unlink_urb(cardp); |
| /* Free Rx data URBs */ |
| for (i = 0; i < MVUSB_RX_DATA_URB; i++) { |
| if (cardp->rx_data_list[i].urb) { |
| usb_free_urb(cardp->rx_data_list[i].urb); |
| cardp->rx_data_list[i].urb = NULL; |
| } |
| } |
| /* Free Rx cmd URB */ |
| if (cardp->rx_cmd.urb) { |
| usb_free_urb(cardp->rx_cmd.urb); |
| cardp->rx_cmd.urb = NULL; |
| } |
| |
| /* Free Tx data URBs */ |
| for (i = 0; i < MVUSB_TX_HIGH_WMARK; i++) { |
| if (cardp->tx_data_list[i].urb) { |
| usb_free_urb(cardp->tx_data_list[i].urb); |
| cardp->tx_data_list[i].urb = NULL; |
| } |
| } |
| /* Free Tx cmd URB */ |
| if (cardp->tx_cmd.urb) { |
| usb_free_urb(cardp->tx_cmd.urb); |
| cardp->tx_cmd.urb = NULL; |
| } |
| |
| LEAVE(); |
| return; |
| } |
| |
| static t_u16 |
| woal_update_card_type(t_void *card) |
| { |
| struct usb_card_rec *cardp_usb = (struct usb_card_rec *)card; |
| t_u16 card_type = 0; |
| |
| /* Update card type */ |
| #ifdef USB8801 |
| if (woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == |
| (__force __le16) USB8801_PID_1 || |
| woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == |
| (__force __le16) USB8801_PID_2) { |
| card_type = CARD_TYPE_USB8801; |
| moal_memcpy_ext(NULL, driver_version, CARD_USB8801, |
| strlen(CARD_USB8801), strlen(driver_version)); |
| moal_memcpy_ext(NULL, |
| driver_version + strlen(INTF_CARDTYPE) + |
| strlen(KERN_VERSION), V14, strlen(V14), |
| strlen(driver_version) - strlen(INTF_CARDTYPE) - |
| strlen(KERN_VERSION)); |
| } |
| #endif |
| #ifdef USB8897 |
| if (woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == |
| (__force __le16) USB8897_PID_1 || |
| woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == |
| (__force __le16) USB8897_PID_2) { |
| card_type = CARD_TYPE_USB8897; |
| moal_memcpy_ext(NULL, driver_version, CARD_USB8897, |
| strlen(CARD_USB8897), strlen(driver_version)); |
| moal_memcpy_ext(NULL, |
| driver_version + strlen(INTF_CARDTYPE) + |
| strlen(KERN_VERSION), |
| V15, strlen(V15), |
| strlen(driver_version) - strlen(INTF_CARDTYPE) - |
| strlen(KERN_VERSION)); |
| } |
| #endif |
| #ifdef USB8997 |
| if (woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == |
| (__force __le16) USB8997_PID_1 || |
| woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == |
| (__force __le16) USB8997_PID_2 || |
| woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == |
| (__force __le16) USB8997_PID_3 || |
| woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == |
| (__force __le16) USB8997_PID_4 || |
| woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == |
| (__force __le16) USB8997_PID_5 || |
| woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == |
| (__force __le16) USB8997_PID_6 || |
| woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == |
| (__force __le16) USB8997V2_PID_1) { |
| card_type = CARD_TYPE_USB8997; |
| moal_memcpy_ext(NULL, driver_version, CARD_USB8997, |
| strlen(CARD_USB8997), strlen(driver_version)); |
| moal_memcpy_ext(NULL, |
| driver_version + strlen(INTF_CARDTYPE) + |
| strlen(KERN_VERSION), |
| V16, strlen(V16), |
| strlen(driver_version) - strlen(INTF_CARDTYPE) - |
| strlen(KERN_VERSION)); |
| } |
| #endif |
| #ifdef USB8978 |
| if (woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == |
| (__force __le16) USB8978_PID_1 || |
| woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == |
| (__force __le16) USB8978_PID_2) { |
| card_type = CARD_TYPE_USB8978; |
| moal_memcpy_ext(NULL, driver_version, "USBIW416", |
| strlen("USBIW416"), strlen(driver_version)); |
| moal_memcpy_ext(NULL, |
| driver_version + strlen(INTF_CARDTYPE) + |
| strlen(KERN_VERSION), |
| V16, strlen(V16), |
| strlen(driver_version) - strlen(INTF_CARDTYPE) - |
| strlen(KERN_VERSION)); |
| } |
| #endif |
| #ifdef USB9098 |
| if (woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == |
| (__force __le16) USB9098_PID_1 || |
| woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == |
| (__force __le16) USB9098_PID_2) { |
| card_type = CARD_TYPE_USB9098; |
| moal_memcpy_ext(NULL, driver_version, CARD_USB9098, |
| strlen(CARD_USB9098), strlen(driver_version)); |
| moal_memcpy_ext(NULL, |
| driver_version + strlen(INTF_CARDTYPE) + |
| strlen(KERN_VERSION), |
| V17, strlen(V17), |
| strlen(driver_version) - strlen(INTF_CARDTYPE) - |
| strlen(KERN_VERSION)); |
| } |
| #endif |
| #ifdef USB9097 |
| if (woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == |
| (__force __le16) USB9097_PID_1 || |
| woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == |
| (__force __le16) USB9097_PID_2) { |
| card_type = CARD_TYPE_USB9097; |
| moal_memcpy_ext(NULL, driver_version, CARD_USBIW620, |
| strlen(CARD_USBIW620), strlen(driver_version)); |
| moal_memcpy_ext(NULL, |
| driver_version + strlen(INTF_CARDTYPE) + |
| strlen(KERN_VERSION), |
| V17, strlen(V17), |
| strlen(driver_version) - strlen(INTF_CARDTYPE) - |
| strlen(KERN_VERSION)); |
| } |
| #endif |
| return card_type; |
| } |
| |
| /** |
| * @brief Sets the configuration values |
| * |
| * @param intf Pointer to usb_interface |
| * @param id Pointer to usb_device_id |
| * |
| * @return Address of variable usb_cardp, error code otherwise |
| */ |
| static int |
| woal_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) |
| { |
| struct usb_device *udev; |
| struct usb_host_interface *iface_desc; |
| struct usb_endpoint_descriptor *endpoint; |
| int i; |
| struct usb_card_rec *usb_cardp = NULL; |
| t_u16 card_type = 0; |
| |
| ENTER(); |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) |
| PRINTM(MMSG, |
| "USB probe: idVendor=%x idProduct=%x bInterfaceNumber=%d\n", |
| id->idVendor, id->idProduct, id->bInterfaceNumber); |
| #endif |
| |
| udev = interface_to_usbdev(intf); |
| usb_cardp = kzalloc(sizeof(struct usb_card_rec), GFP_KERNEL); |
| if (!usb_cardp) { |
| LEAVE(); |
| return -ENOMEM; |
| } |
| |
| /* Check probe is for our device */ |
| for (i = 0; woal_usb_table[i].idVendor; i++) { |
| if (woal_cpu_to_le16(udev->descriptor.idVendor) == |
| (__force __le16) woal_usb_table[i].idVendor && |
| woal_cpu_to_le16(udev->descriptor.idProduct) == |
| (__force __le16) woal_usb_table[i].idProduct) { |
| PRINTM(MMSG, "VID/PID = %X/%X, Boot2 version = %X\n", |
| woal_cpu_to_le16(udev->descriptor.idVendor), |
| woal_cpu_to_le16(udev->descriptor.idProduct), |
| woal_cpu_to_le16(udev->descriptor.bcdDevice)); |
| switch (woal_cpu_to_le16(udev->descriptor.idProduct)) { |
| #ifdef USB8801 |
| case (__force __le16) USB8801_PID_1: |
| #endif /* USB8801 */ |
| #ifdef USB8897 |
| case (__force __le16) USB8897_PID_1: |
| #endif /* USB8897 */ |
| #ifdef USB8997 |
| case (__force __le16) USB8997_PID_1: |
| case (__force __le16) USB8997V2_PID_1: |
| #endif /* USB8997 */ |
| #ifdef USB8978 |
| case (__force __le16) USB8978_PID_1: |
| case (__force __le16) USB8978_PID_1_BT: |
| #endif /* USB8978 */ |
| #ifdef USB9098 |
| case (__force __le16) USB9098_PID_1: |
| #endif /* USB9098 */ |
| #ifdef USB9097 |
| case (__force __le16) USB9097_PID_1: |
| #endif /* USB9097 */ |
| /* If skip FW is set, we must return error so |
| * the next driver can download the FW */ |
| if (skip_fwdnld) |
| goto error; |
| else |
| usb_cardp->boot_state = USB_FW_DNLD; |
| break; |
| #ifdef USB8801 |
| case (__force __le16) USB8801_PID_2: |
| #endif /* USB8801 */ |
| #ifdef USB8897 |
| case (__force __le16) USB8897_PID_2: |
| #endif /* USB8897 */ |
| #ifdef USB8997 |
| case (__force __le16) USB8997_PID_2: |
| #endif /* USB8997 */ |
| #ifdef USB8978 |
| case (__force __le16) USB8978_PID_2: |
| case (__force __le16) USB8978_PID_2_BT: |
| #endif /* USB8978 */ |
| #ifdef USB9098 |
| case (__force __le16) USB9098_PID_2: |
| #endif /* USB9098 */ |
| #ifdef USB9097 |
| case (__force __le16) USB9097_PID_2: |
| #endif /* USB9097 */ |
| usb_cardp->boot_state = USB_FW_READY; |
| break; |
| } |
| /*To do, get card type */ |
| /* if |
| (woal_cpu_to_le16(udev->descriptor.idProduct) == |
| USB8897_PID_2) usb_cardp->card_type = |
| CARD_TYPE_USB8897; else if |
| (woal_cpu_to_le16(udev->descriptor.idProduct) == |
| USB8997_PID_2) usb_cardp->card_type = |
| CARD_TYPE_USB997; |
| */ |
| break; |
| } |
| } |
| |
| if (woal_usb_table[i].idVendor) { |
| usb_cardp->udev = udev; |
| iface_desc = intf->cur_altsetting; |
| usb_cardp->intf = intf; |
| |
| PRINTM(MINFO, |
| "bcdUSB = 0x%X bDeviceClass = 0x%X" |
| " bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n", |
| woal_cpu_to_le16(udev->descriptor.bcdUSB), |
| udev->descriptor.bDeviceClass, |
| udev->descriptor.bDeviceSubClass, |
| udev->descriptor.bDeviceProtocol); |
| |
| for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { |
| endpoint = &iface_desc->endpoint[i].desc; |
| if ((usb_endpoint_is_bulk_in(endpoint) || |
| usb_endpoint_is_int_in(endpoint)) && |
| (usb_endpoint_num(endpoint) == MLAN_USB_EP_CMD_EVENT |
| || usb_endpoint_num(endpoint) == |
| MLAN_USB_EP_CMD_EVENT_IF2)) { |
| usb_cardp->rx_cmd_ep_type = |
| usb_endpoint_type(endpoint); |
| usb_cardp->rx_cmd_interval = |
| endpoint->bInterval; |
| /* We found a bulk in command/event endpoint */ |
| PRINTM(MCMND, |
| "Rx CMD/EVT: max packet size = %d, address = %d ep_type=%d\n", |
| woal_le16_to_cpu(endpoint-> |
| wMaxPacketSize), |
| endpoint->bEndpointAddress, |
| usb_cardp->rx_cmd_ep_type); |
| usb_cardp->rx_cmd_ep = |
| (endpoint-> |
| bEndpointAddress & |
| USB_ENDPOINT_NUMBER_MASK); |
| |
| atomic_set(&usb_cardp->rx_cmd_urb_pending, 0); |
| if (usb_endpoint_num(endpoint) == |
| MLAN_USB_EP_CMD_EVENT_IF2) |
| usb_cardp->second_mac = MTRUE; |
| } |
| if (usb_endpoint_is_bulk_in(endpoint) && |
| (usb_endpoint_num(endpoint) == MLAN_USB_EP_DATA |
| || |
| usb_endpoint_num(endpoint) == |
| MLAN_USB_EP_DATA_IF2)) { |
| /* We found a bulk in data endpoint */ |
| PRINTM(MINFO, |
| "Bulk IN: max packet size = %d, address = %d\n", |
| woal_le16_to_cpu(endpoint-> |
| wMaxPacketSize), |
| endpoint->bEndpointAddress); |
| usb_cardp->rx_data_ep = |
| (endpoint-> |
| bEndpointAddress & |
| USB_ENDPOINT_NUMBER_MASK); |
| atomic_set(&usb_cardp->rx_data_urb_pending, 0); |
| } |
| if (usb_endpoint_is_bulk_out(endpoint) && |
| (usb_endpoint_num(endpoint) == MLAN_USB_EP_DATA |
| || |
| usb_endpoint_num(endpoint) == |
| MLAN_USB_EP_DATA_IF2)) { |
| /* We found a bulk out data endpoint */ |
| PRINTM(MCMND, |
| "Bulk OUT: max packet size = %d, address = %d\n", |
| woal_le16_to_cpu(endpoint-> |
| wMaxPacketSize), |
| endpoint->bEndpointAddress); |
| usb_cardp->tx_data_ep = |
| endpoint->bEndpointAddress; |
| atomic_set(&usb_cardp->tx_data_urb_pending, 0); |
| usb_cardp->tx_data_maxpktsize = |
| (__force int)woal_le16_to_cpu(endpoint-> |
| wMaxPacketSize); |
| } |
| |
| if ((usb_endpoint_is_bulk_out(endpoint) || |
| usb_endpoint_is_int_out(endpoint)) && |
| (usb_endpoint_num(endpoint) == MLAN_USB_EP_CMD_EVENT |
| || usb_endpoint_num(endpoint) == |
| MLAN_USB_EP_CMD_EVENT_IF2)) { |
| usb_cardp->tx_cmd_ep_type = |
| usb_endpoint_type(endpoint); |
| usb_cardp->tx_cmd_interval = |
| endpoint->bInterval; |
| /* We found a bulk out command/event endpoint */ |
| PRINTM(MCMND, |
| "Tx CMD: max packet size = %d, address = %d ep_type=%d\n", |
| woal_le16_to_cpu(endpoint-> |
| wMaxPacketSize), |
| endpoint->bEndpointAddress, |
| usb_cardp->tx_cmd_ep_type); |
| usb_cardp->tx_cmd_ep = |
| endpoint->bEndpointAddress; |
| atomic_set(&usb_cardp->tx_cmd_urb_pending, 0); |
| usb_cardp->tx_cmd_maxpktsize = |
| (__force int)woal_le16_to_cpu(endpoint-> |
| wMaxPacketSize); |
| } |
| } |
| |
| if (usb_cardp->boot_state == USB_FW_DNLD) { |
| if (!usb_cardp->tx_cmd_ep || !usb_cardp->rx_cmd_ep) |
| goto error; |
| } else if (usb_cardp->boot_state == USB_FW_READY) { |
| if (!usb_cardp->tx_cmd_ep || !usb_cardp->tx_data_ep || |
| !usb_cardp->rx_cmd_ep || !usb_cardp->rx_data_ep) { |
| PRINTM(MERROR, |
| "%s: invalid endpoint assignment\n", |
| __FUNCTION__); |
| goto error; |
| } |
| } |
| |
| usb_cardp->tx_aggr_ctrl.enable = MFALSE; |
| usb_cardp->tx_aggr_ctrl.aggr_mode = MLAN_USB_AGGR_MODE_NUM; |
| usb_cardp->tx_aggr_ctrl.aggr_align = |
| MAX(max_tx_buf, MLAN_USB_TX_AGGR_ALIGN); |
| usb_cardp->tx_aggr_ctrl.aggr_max = MLAN_USB_TX_MAX_AGGR_NUM; |
| usb_cardp->tx_aggr_ctrl.aggr_tmo = |
| MLAN_USB_TX_AGGR_TIMEOUT_MSEC * 1000; |
| usb_cardp->rx_deaggr_ctrl.enable = MFALSE; |
| usb_cardp->rx_deaggr_ctrl.aggr_mode = MLAN_USB_AGGR_MODE_NUM; |
| usb_cardp->rx_deaggr_ctrl.aggr_align = MLAN_USB_RX_ALIGN_SIZE; |
| usb_cardp->rx_deaggr_ctrl.aggr_max = MLAN_USB_RX_MAX_AGGR_NUM; |
| usb_cardp->rx_deaggr_ctrl.aggr_tmo = |
| MLAN_USB_RX_DEAGGR_TIMEOUT_USEC; |
| usb_set_intfdata(intf, usb_cardp); |
| |
| card_type = woal_update_card_type(usb_cardp); |
| if (!card_type) { |
| PRINTM(MERROR, |
| "usb probe: woal_update_card_type() failed\n"); |
| goto error; |
| } |
| |
| /* At this point wlan_add_card() will be called */ |
| if (!(woal_add_card(usb_cardp, &usb_cardp->udev->dev, &usb_ops, |
| card_type))) { |
| PRINTM(MERROR, "%s: woal_add_card failed\n", |
| __FUNCTION__); |
| goto error; |
| } |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) |
| /* Note: From 2.6.34.2 onwards, remote wakeup is NOT enabled by |
| * default. So drivers wanting remote wakeup will have to enable |
| * this using - |
| * device_set_wakeup_enable(&udev->dev, 1); |
| * It has been observed that some cards having device attr = |
| * 0xa0 do not support remote wakeup. These cards come |
| * immediately out of the suspend when power/wakeup file is set |
| * to 'enabled'. To support all types of cards i.e. with/without |
| * remote wakeup, we are NOT setting the 'power/wakeup' file |
| * from here. Also in principle, we are not supposed to change |
| * the wakeup policy, which is purely a userspace decision. |
| */ |
| /* if (udev->actconfig->desc.bmAttributes & |
| USB_CONFIG_ATT_WAKEUP) intf->needs_remote_wakeup = 1; */ |
| #endif |
| usb_get_dev(udev); |
| LEAVE(); |
| return 0; |
| } else { |
| PRINTM(MINFO, "Discard the Probe request\n"); |
| PRINTM(MINFO, "VID = 0x%X PID = 0x%X\n", |
| woal_cpu_to_le16(udev->descriptor.idVendor), |
| woal_cpu_to_le16(udev->descriptor.idProduct)); |
| } |
| error: |
| kfree(usb_cardp); |
| usb_cardp = NULL; |
| LEAVE(); |
| return -ENXIO; |
| } |
| |
| /** |
| * @brief Free resource and cleanup |
| * |
| * @param intf Pointer to usb_interface |
| * |
| * @return N/A |
| */ |
| static void |
| woal_usb_disconnect(struct usb_interface *intf) |
| { |
| struct usb_card_rec *cardp = usb_get_intfdata(intf); |
| moal_handle *phandle = NULL; |
| ENTER(); |
| if (!cardp || !cardp->phandle) { |
| PRINTM(MERROR, "Card or phandle is not valid\n"); |
| LEAVE(); |
| return; |
| } |
| phandle = (moal_handle *)cardp->phandle; |
| |
| /* |
| * Update Surprise removed to TRUE |
| * Free all the URB's allocated |
| */ |
| phandle->surprise_removed = MTRUE; |
| |
| /* Card is removed and we can call wlan_remove_card */ |
| PRINTM(MINFO, "Call remove card\n"); |
| woal_remove_card(cardp); |
| |
| usb_set_intfdata(intf, NULL); |
| usb_put_dev(interface_to_usbdev(intf)); |
| kfree(cardp); |
| LEAVE(); |
| return; |
| } |
| |
| /** |
| * @brief killall pending urbs |
| * |
| * @param handle Pointer to moal_handle |
| * |
| * |
| * @return N/A |
| */ |
| void |
| woal_kill_urbs(moal_handle *handle) |
| { |
| ENTER(); |
| handle->is_suspended = MTRUE; |
| woal_usb_unlink_urb(handle->card); |
| LEAVE(); |
| } |
| |
| /** |
| * @brief resubmit urbs |
| * |
| * @param handle Pointer to moal_handle |
| * |
| * |
| * @return N/A |
| */ |
| void |
| woal_resubmit_urbs(moal_handle *handle) |
| { |
| struct usb_card_rec *cardp = handle->card; |
| |
| ENTER(); |
| handle->is_suspended = MFALSE; |
| |
| if (!atomic_read(&cardp->rx_data_urb_pending)) { |
| /* Submit multiple Rx data URBs */ |
| woal_usb_submit_rx_data_urbs(handle); |
| } |
| if (!atomic_read(&cardp->rx_cmd_urb_pending)) { |
| cardp->rx_cmd.pmbuf = |
| woal_alloc_mlan_buffer(handle, MLAN_RX_CMD_BUF_SIZE); |
| if (cardp->rx_cmd.pmbuf) |
| woal_usb_submit_rx_urb(&cardp->rx_cmd, |
| MLAN_RX_CMD_BUF_SIZE); |
| } |
| LEAVE(); |
| } |
| |
| #ifdef CONFIG_PM |
| /** |
| * @brief Handle suspend |
| * |
| * @param intf Pointer to usb_interface |
| * @param message Pointer to pm_message_t structure |
| * |
| * @return MLAN_STATUS_SUCCESS |
| */ |
| static int |
| woal_usb_suspend(struct usb_interface *intf, pm_message_t message) |
| { |
| struct usb_card_rec *cardp = usb_get_intfdata(intf); |
| moal_handle *handle = NULL; |
| int i; |
| int ret = 0; |
| |
| ENTER(); |
| |
| PRINTM(MCMND, "<--- Enter woal_usb_suspend --->\n"); |
| if (!cardp || !cardp->phandle) { |
| PRINTM(MERROR, "Card or moal_handle structure is not valid\n"); |
| ret = 0; |
| goto done; |
| } |
| handle = cardp->phandle; |
| if (handle->is_suspended == MTRUE) { |
| PRINTM(MWARN, "Device already suspended\n"); |
| ret = 0; |
| goto done; |
| } |
| #ifdef STA_SUPPORT |
| for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { |
| if (handle->priv[i] && |
| (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA)) |
| woal_cancel_scan(handle->priv[i], MOAL_IOCTL_WAIT); |
| } |
| #endif |
| /* Enable Host Sleep */ |
| woal_enable_hs(woal_get_priv(handle, MLAN_BSS_ROLE_ANY)); |
| |
| /* Indicate device suspended */ |
| /* The flag must be set here before the usb_kill_urb() calls. |
| * Reason: In the complete handlers, urb->status(= -ENOENT) and |
| * 'is_suspended' flag is used in combination to distinguish |
| * between a suspended state and a 'disconnect' one. |
| */ |
| handle->is_suspended = MTRUE; |
| for (i = 0; i < handle->priv_num; i++) |
| netif_carrier_off(handle->priv[i]->netdev); |
| |
| /* Unlink Rx cmd URB */ |
| if (atomic_read(&cardp->rx_cmd_urb_pending) && cardp->rx_cmd.urb) { |
| usb_kill_urb(cardp->rx_cmd.urb); |
| } |
| /* Unlink Rx data URBs */ |
| if (atomic_read(&cardp->rx_data_urb_pending)) { |
| for (i = 0; i < MVUSB_RX_DATA_URB; i++) { |
| if (cardp->rx_data_list[i].urb) { |
| usb_kill_urb(cardp->rx_data_list[i].urb); |
| usb_init_urb(cardp->rx_data_list[i].urb); |
| } |
| } |
| } |
| |
| /* Unlink Tx data URBs */ |
| for (i = 0; i < MVUSB_TX_HIGH_WMARK; i++) { |
| if (cardp->tx_data_list[i].urb) { |
| usb_kill_urb(cardp->tx_data_list[i].urb); |
| } |
| } |
| /* Unlink Tx cmd URB */ |
| if (cardp->tx_cmd.urb) { |
| usb_kill_urb(cardp->tx_cmd.urb); |
| } |
| |
| handle->suspend_wait_q_woken = MTRUE; |
| wake_up_interruptible(&handle->suspend_wait_q); |
| |
| done: |
| PRINTM(MCMND, "<--- Leave woal_usb_suspend --->\n"); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Handle resume |
| * |
| * @param intf Pointer to usb_interface |
| * |
| * @return MLAN_STATUS_SUCCESS |
| */ |
| static int |
| woal_usb_resume(struct usb_interface *intf) |
| { |
| struct usb_card_rec *cardp = usb_get_intfdata(intf); |
| moal_handle *handle = NULL; |
| int i; |
| |
| ENTER(); |
| |
| PRINTM(MCMND, "<--- Enter woal_usb_resume --->\n"); |
| if (!cardp || !cardp->phandle) { |
| PRINTM(MERROR, "Card or adapter structure is not valid\n"); |
| goto done; |
| } |
| handle = cardp->phandle; |
| |
| if (handle->is_suspended == MFALSE) { |
| PRINTM(MWARN, "Device already resumed\n"); |
| goto done; |
| } |
| |
| /* Indicate device resumed. |
| * The netdev queue will be resumed only after the urbs |
| * have been resubmitted */ |
| handle->is_suspended = MFALSE; |
| |
| if (!atomic_read(&cardp->rx_data_urb_pending)) { |
| /* Submit multiple Rx data URBs */ |
| woal_usb_submit_rx_data_urbs(handle); |
| } |
| if (!atomic_read(&cardp->rx_cmd_urb_pending)) { |
| cardp->rx_cmd.pmbuf = |
| woal_alloc_mlan_buffer(handle, MLAN_RX_CMD_BUF_SIZE); |
| if (cardp->rx_cmd.pmbuf) |
| woal_usb_submit_rx_urb(&cardp->rx_cmd, |
| MLAN_RX_CMD_BUF_SIZE); |
| } |
| |
| for (i = 0; i < handle->priv_num; i++) |
| if (handle->priv[i]->media_connected == MTRUE) |
| netif_carrier_on(handle->priv[i]->netdev); |
| |
| /* Disable Host Sleep */ |
| if (handle->hs_activated) |
| woal_cancel_hs(woal_get_priv(handle, MLAN_BSS_ROLE_ANY), |
| MOAL_NO_WAIT); |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) |
| /* Resume handler may be called due to remote wakeup, |
| force to exit suspend anyway */ |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) |
| cardp->udev->autosuspend_disabled = 1; |
| #else |
| #ifdef CONFIG_PM_RUNTIME |
| cardp->udev->dev.power.runtime_auto = 0; |
| #endif |
| #endif /* < 2.6.35 */ |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) |
| cardp->udev->autoresume_disabled = 0; |
| #endif /* < 2.6.33 */ |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) |
| #ifdef CONFIG_PM_RUNTIME |
| atomic_inc(&(cardp->udev)->dev.power.usage_count); |
| #endif |
| #endif /* >= 2.6.34 */ |
| #endif /* >= 2.6.24 */ |
| |
| done: |
| PRINTM(MCMND, "<--- Leave woal_usb_resume --->\n"); |
| LEAVE(); |
| return 0; |
| } |
| #endif /* CONFIG_PM */ |
| |
| /** |
| * @brief This function initialize the tx URBs |
| * |
| * @param handle Pointer to moal_handle structure |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| mlan_status |
| woal_usb_tx_init(moal_handle *handle) |
| { |
| struct usb_card_rec *cardp = (struct usb_card_rec *)handle->card; |
| int i; |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| cardp->tx_cmd.handle = handle; |
| cardp->tx_cmd.ep = cardp->tx_cmd_ep; |
| /* Allocate URB for command */ |
| cardp->tx_cmd.urb = usb_alloc_urb(0, GFP_KERNEL); |
| if (!cardp->tx_cmd.urb) { |
| PRINTM(MERROR, "Tx command URB allocation failed\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto init_exit; |
| } |
| |
| cardp->tx_data_ix = 0; |
| for (i = 0; i < MVUSB_TX_HIGH_WMARK; i++) { |
| cardp->tx_data_list[i].handle = handle; |
| cardp->tx_data_list[i].ep = cardp->tx_data_ep; |
| /* Allocate URB for data */ |
| cardp->tx_data_list[i].urb = usb_alloc_urb(0, GFP_KERNEL); |
| if (!cardp->tx_data_list[i].urb) { |
| PRINTM(MERROR, "Tx data URB allocation failed\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto init_exit; |
| } |
| } |
| |
| init_exit: |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief This function submits the rx data URBs |
| * |
| * @param handle Pointer to moal_handle structure |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| mlan_status |
| woal_usb_submit_rx_data_urbs(moal_handle *handle) |
| { |
| struct usb_card_rec *cardp = (struct usb_card_rec *)handle->card; |
| int i; |
| mlan_status ret = MLAN_STATUS_FAILURE; |
| t_u32 buffer_len = MLAN_RX_DATA_BUF_SIZE; |
| |
| ENTER(); |
| |
| if (cardp->rx_deaggr_ctrl.enable) { |
| buffer_len = cardp->rx_deaggr_ctrl.aggr_max; |
| if (cardp->rx_deaggr_ctrl.aggr_mode == MLAN_USB_AGGR_MODE_NUM) { |
| buffer_len *= MAX(MLAN_USB_MAX_PKT_SIZE, |
| cardp->rx_deaggr_ctrl.aggr_align); |
| buffer_len = MAX(buffer_len, MLAN_RX_DATA_BUF_SIZE); |
| } |
| } |
| |
| for (i = 0; i < MVUSB_RX_DATA_URB; i++) { |
| /* Submit Rx data URB */ |
| if (!cardp->rx_data_list[i].pmbuf) { |
| if (woal_usb_submit_rx_urb(&cardp->rx_data_list[i], |
| buffer_len)) |
| continue; |
| } |
| ret = MLAN_STATUS_SUCCESS; |
| } |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief This function initialize the rx URBs and submit them |
| * |
| * @param handle Pointer to moal_handle structure |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| mlan_status |
| woal_usb_rx_init(moal_handle *handle) |
| { |
| struct usb_card_rec *cardp = (struct usb_card_rec *)handle->card; |
| int i; |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| cardp->rx_cmd.handle = handle; |
| cardp->rx_cmd.ep = cardp->rx_cmd_ep; |
| /* Allocate URB for command/event */ |
| cardp->rx_cmd.urb = usb_alloc_urb(0, GFP_KERNEL); |
| if (!cardp->rx_cmd.urb) { |
| PRINTM(MERROR, "Rx command URB allocation failed\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto init_exit; |
| } |
| |
| cardp->rx_cmd.pmbuf = |
| woal_alloc_mlan_buffer(handle, MLAN_RX_CMD_BUF_SIZE); |
| if (cardp->rx_cmd.pmbuf) { |
| /* Submit Rx command URB */ |
| if (woal_usb_submit_rx_urb(&cardp->rx_cmd, |
| MLAN_RX_CMD_BUF_SIZE)) { |
| ret = MLAN_STATUS_FAILURE; |
| goto init_exit; |
| } |
| } |
| for (i = 0; i < MVUSB_RX_DATA_URB; i++) { |
| cardp->rx_data_list[i].handle = handle; |
| cardp->rx_data_list[i].ep = cardp->rx_data_ep; |
| /* Allocate URB for data */ |
| cardp->rx_data_list[i].urb = usb_alloc_urb(0, GFP_KERNEL); |
| if (!cardp->rx_data_list[i].urb) { |
| PRINTM(MERROR, "Rx data URB allocation failed\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto init_exit; |
| } |
| } |
| ret = woal_usb_submit_rx_data_urbs(handle); |
| if (ret) { |
| PRINTM(MERROR, "Rx data URB submission failed\n"); |
| goto init_exit; |
| } |
| |
| init_exit: |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief This function downloads data blocks to device |
| * |
| * @param handle Pointer to moal_handle structure |
| * @param pmbuf Pointer to mlan_buffer structure |
| * @param ep Endpoint to send |
| * @param timeout Timeout value in milliseconds (if 0 the wait is forever) |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| static mlan_status |
| woal_usb_write_data_sync(moal_handle *handle, |
| mlan_buffer *pmbuf, t_u32 endpoint, t_u32 timeout) |
| { |
| struct usb_card_rec *cardp = handle->card; |
| t_u8 *data = (t_u8 *)(pmbuf->pbuf + pmbuf->data_offset); |
| t_u8 ep = endpoint; |
| t_u32 length = pmbuf->data_len; |
| int actual_length; |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| int bulk_out_maxpktsize = 512; |
| |
| if (ep == cardp->tx_cmd_ep) |
| bulk_out_maxpktsize = cardp->tx_cmd_maxpktsize; |
| else if (ep == cardp->tx_data_ep) |
| bulk_out_maxpktsize = cardp->tx_data_maxpktsize; |
| |
| if (length % bulk_out_maxpktsize == 0) |
| length++; |
| |
| /* Send the data block */ |
| ret = usb_bulk_msg(cardp->udev, usb_sndbulkpipe(cardp->udev, ep), |
| (t_u8 *)data, length, &actual_length, timeout); |
| if (ret) { |
| PRINTM(MERROR, "usb_blk_msg for send failed, ret %d\n", ret); |
| ret = MLAN_STATUS_FAILURE; |
| } |
| |
| pmbuf->data_len = actual_length; |
| DBG_HEXDUMP(MIF_D, "write sync", data, actual_length); |
| |
| return ret; |
| } |
| |
| /** |
| * @brief This function read data blocks to device |
| * |
| * @param handle Pointer to moal_handle structure |
| * @param pmbuf Pointer to mlan_buffer structure |
| * @param ep Endpoint to receive |
| * @param timeout Timeout value in milliseconds (if 0 the wait is forever) |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| static mlan_status |
| woal_usb_read_data_sync(moal_handle *handle, |
| mlan_buffer *pmbuf, t_u32 endpoint, t_u32 timeout) |
| { |
| struct usb_card_rec *cardp = handle->card; |
| t_u8 *data = (t_u8 *)(pmbuf->pbuf + pmbuf->data_offset); |
| t_u8 ep = endpoint; |
| t_u32 buf_len = pmbuf->data_len; |
| int actual_length; |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| ENTER(); |
| /* Receive the data response */ |
| ret = usb_bulk_msg(cardp->udev, usb_rcvbulkpipe(cardp->udev, ep), data, |
| buf_len, &actual_length, timeout); |
| if (ret) { |
| PRINTM(MERROR, "usb_bulk_msg failed: %d\n", ret); |
| ret = MLAN_STATUS_FAILURE; |
| } |
| pmbuf->data_len = actual_length; |
| DBG_HEXDUMP(MIF_D, "read sync", data, actual_length); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief This function downloads data/command packet to device |
| * |
| * @param handle Pointer to moal_handle structure |
| * @param pmbuf Pointer to mlan_buffer structure |
| * @param ep Endpoint to send |
| * |
| * @return MLAN_STATUS_PENDING or MLAN_STATUS_FAILURE or |
| * MLAN_STATUS_RESOURCE |
| */ |
| mlan_status |
| woal_write_data_async(moal_handle *handle, mlan_buffer *pmbuf, t_u8 ep) |
| { |
| struct usb_card_rec *cardp = handle->card; |
| urb_context *context = NULL; |
| t_u8 *data = (t_u8 *)(pmbuf->pbuf + pmbuf->data_offset); |
| struct urb *tx_urb = NULL; |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| t_u32 data_len = pmbuf->data_len; |
| int bulk_out_maxpktsize = 512; |
| |
| ENTER(); |
| |
| /* Check if device is removed */ |
| if (handle->surprise_removed) { |
| PRINTM(MERROR, "Device removed\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto tx_ret; |
| } |
| |
| if ((ep == cardp->tx_data_ep) && |
| (atomic_read(&cardp->tx_data_urb_pending) >= MVUSB_TX_HIGH_WMARK)) { |
| ret = MLAN_STATUS_RESOURCE; |
| goto tx_ret; |
| } |
| PRINTM(MINFO, "woal_write_data_async: ep=%d\n", ep); |
| |
| if (ep == cardp->tx_cmd_ep) { |
| context = &cardp->tx_cmd; |
| bulk_out_maxpktsize = cardp->tx_cmd_maxpktsize; |
| } else { |
| if (ep == cardp->tx_data_ep) { |
| bulk_out_maxpktsize = cardp->tx_data_maxpktsize; |
| if (cardp->tx_data_ix >= MVUSB_TX_HIGH_WMARK) |
| cardp->tx_data_ix = 0; |
| context = &cardp->tx_data_list[cardp->tx_data_ix++]; |
| } |
| } |
| |
| if (!context) { |
| PRINTM(MERROR, "Cannot allocate Tx urb_context\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto tx_ret; |
| } |
| context->handle = handle; |
| context->ep = ep; |
| context->pmbuf = pmbuf; |
| |
| tx_urb = context->urb; |
| |
| if (data_len % bulk_out_maxpktsize == 0) |
| data_len++; |
| |
| /* |
| * Use USB API usb_fill_bulk_urb() to set the |
| * configuration information of the Tx bulk URB |
| * and initialize the Tx callback |
| */ |
| |
| if (ep == cardp->tx_cmd_ep && |
| cardp->tx_cmd_ep_type == USB_ENDPOINT_XFER_INT) { |
| usb_fill_int_urb(tx_urb, cardp->udev, |
| usb_sndintpipe(cardp->udev, ep), data, |
| data_len, woal_usb_tx_complete, |
| (void *)context, cardp->tx_cmd_interval); |
| } else |
| usb_fill_bulk_urb(tx_urb, cardp->udev, |
| usb_sndbulkpipe(cardp->udev, ep), data, |
| data_len, woal_usb_tx_complete, |
| (void *)context); |
| /* We find on Ubuntu 12.10 this flag does not work */ |
| // tx_urb->transfer_flags |= URB_ZERO_PACKET; |
| |
| if (ep == cardp->tx_cmd_ep) |
| atomic_inc(&cardp->tx_cmd_urb_pending); |
| else if (ep == cardp->tx_data_ep) |
| atomic_inc(&cardp->tx_data_urb_pending); |
| if (usb_submit_urb(tx_urb, GFP_ATOMIC)) { |
| /* Submit URB failure */ |
| PRINTM(MERROR, "Submit EP %d Tx URB failed: %d\n", ep, ret); |
| if (ep == cardp->tx_cmd_ep) |
| atomic_dec(&cardp->tx_cmd_urb_pending); |
| else { |
| if (ep == cardp->tx_data_ep) { |
| atomic_dec(&cardp->tx_data_urb_pending); |
| if (cardp->tx_data_ix) |
| cardp->tx_data_ix--; |
| else |
| cardp->tx_data_ix = MVUSB_TX_HIGH_WMARK; |
| } |
| } |
| ret = MLAN_STATUS_FAILURE; |
| } else { |
| if (ep == cardp->tx_data_ep && |
| (atomic_read(&cardp->tx_data_urb_pending) == |
| MVUSB_TX_HIGH_WMARK)) |
| ret = MLAN_STATUS_PRESOURCE; |
| else |
| ret = MLAN_STATUS_SUCCESS; |
| } |
| |
| tx_ret: |
| |
| if (!ret) |
| ret = MLAN_STATUS_PENDING; |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief This function register usb device and initialize parameter |
| * |
| * @param handle Pointer to moal_handle structure |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| static mlan_status |
| woal_usb_register_dev(moal_handle *handle) |
| { |
| struct usb_card_rec *cardp = (struct usb_card_rec *)handle->card; |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| cardp->phandle = handle; |
| LEAVE(); |
| return ret; |
| } |
| |
| static void |
| woal_usb_unregister_dev(moal_handle *handle) |
| { |
| struct usb_card_rec *cardp = (struct usb_card_rec *)handle->card; |
| PRINTM(MMSG, "USB: unregister device\n"); |
| woal_usb_free(cardp); |
| cardp->phandle = NULL; |
| return; |
| } |
| |
| /** |
| * @brief This function registers driver. |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| mlan_status |
| woal_usb_bus_register(void) |
| { |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| ENTER(); |
| |
| if (skip_fwdnld) { |
| woal_usb_driver.id_table = woal_usb_table_skip_fwdnld; |
| } |
| /* |
| * API registers the NXP USB driver |
| * to the USB system |
| */ |
| if (usb_register(&woal_usb_driver)) { |
| PRINTM(MFATAL, "USB Driver Registration Failed \n"); |
| ret = MLAN_STATUS_FAILURE; |
| } |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief This function removes usb driver. |
| * |
| * @return N/A |
| */ |
| void |
| woal_usb_bus_unregister(void) |
| { |
| ENTER(); |
| /* API unregisters the driver from USB subsystem */ |
| usb_deregister(&woal_usb_driver); |
| LEAVE(); |
| return; |
| } |
| |
| /** |
| * @brief This function check if this is second mac |
| * |
| * @param handle A pointer to moal_handle structure |
| * @return MTRUE/MFALSE |
| * |
| */ |
| static t_u8 |
| woal_usb_is_second_mac(moal_handle *handle) |
| { |
| return ((struct usb_card_rec *)(handle->card))->second_mac; |
| } |
| |
| #ifdef CONFIG_USB_SUSPEND |
| /** |
| * @brief This function makes USB device to suspend. |
| * |
| * @param handle A pointer to moal_handle structure |
| * |
| * @return 0 --success, otherwise fail |
| */ |
| int |
| woal_enter_usb_suspend(moal_handle *handle) |
| { |
| struct usb_device *udev = ((struct usb_card_rec *)(handle->card))->udev; |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) |
| struct usb_interface *intf = |
| ((struct usb_card_rec *)(handle->card))->intf; |
| #endif /* < 2.6.34 */ |
| #endif /* >= 2.6.24 */ |
| |
| ENTER(); |
| |
| PRINTM(MIOCTL, "USB suspend ioctl (state %d)\n", udev->state); |
| |
| if (handle->is_suspended == MTRUE) { |
| PRINTM(MERROR, "Device already suspended\n"); |
| LEAVE(); |
| return -EFAULT; |
| } |
| |
| handle->suspend_wait_q_woken = MFALSE; |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) |
| /* Enter into USB suspend */ |
| usb_lock_device(udev); |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38) |
| udev->autosuspend_delay = 0; /* Autosuspend delay in jiffies */ |
| #else |
| pm_runtime_set_autosuspend_delay(&udev->dev, 0); /* Autosuspend delay in |
| jiffies */ |
| #endif /* < 2.6.38 */ |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) |
| udev->autosuspend_disabled = 0; /* /sys/bus/usb/devices/.../power/level |
| < auto */ |
| #endif /* < 2.6.34 */ |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) |
| udev->autoresume_disabled = 0; |
| #endif /* < 2.6.33 */ |
| usb_unlock_device(udev); |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) |
| intf->pm_usage_cnt = 1; |
| #else |
| atomic_set(&intf->pm_usage_cnt, 1); |
| #endif /* < 2.6.32 */ |
| usb_autopm_put_interface(intf); |
| #else |
| usb_lock_device(udev); |
| atomic_set(&udev->dev.power.usage_count, 1); |
| usb_enable_autosuspend(udev); |
| usb_unlock_device(udev); |
| #endif /* < 2.6.34 */ |
| #endif /* >= 2.6.24 */ |
| |
| /* Wait for suspend to complete */ |
| wait_event_interruptible(handle->suspend_wait_q, |
| handle->suspend_wait_q_woken); |
| |
| LEAVE(); |
| return 0; |
| } |
| |
| /** |
| * @brief This function makes USB device to resume. |
| * |
| * @param handle A pointer to moal_handle structure |
| * |
| * @return 0 --success, otherwise fail |
| */ |
| int |
| woal_exit_usb_suspend(moal_handle *handle) |
| { |
| struct usb_device *udev = ((struct usb_card_rec *)(handle->card))->udev; |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) |
| struct usb_interface *intf = |
| ((struct usb_card_rec *)(handle->card))->intf; |
| #endif /* < 2.6.34 */ |
| #endif /* >= 2.6.24 */ |
| |
| ENTER(); |
| |
| PRINTM(MIOCTL, "USB resume ioctl (state %d)\n", udev->state); |
| |
| if (handle->is_suspended == MFALSE || |
| udev->state != USB_STATE_SUSPENDED) { |
| PRINTM(MERROR, "Device already resumed\n"); |
| LEAVE(); |
| return -EFAULT; |
| } |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) |
| /* Exit from USB suspend */ |
| usb_lock_device(udev); |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) |
| udev->autosuspend_disabled = 1; /* /sys/bus/usb/devices/.../power/level |
| < on */ |
| #endif /* < 2.6.34 */ |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) |
| udev->autoresume_disabled = 0; |
| #endif /* < 2.6.33 */ |
| usb_unlock_device(udev); |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) |
| intf->pm_usage_cnt = 0; |
| #else |
| atomic_set(&intf->pm_usage_cnt, 0); |
| #endif /* < 2.6.32 */ |
| usb_autopm_get_interface(intf); |
| #else |
| usb_lock_device(udev); |
| atomic_set(&udev->dev.power.usage_count, 0); |
| usb_disable_autosuspend(udev); |
| usb_unlock_device(udev); |
| #endif /* < 2.6.34 */ |
| #endif /* >= 2.6.24 */ |
| |
| LEAVE(); |
| return 0; |
| } |
| #endif /* CONFIG_USB_SUSPEND */ |
| |
| /** |
| * @brief This function will submit rx urb. |
| * |
| * @param handle Pointer to moal_handle |
| * @param ep Endpoint to re-submit urb |
| * |
| * @return N/A |
| */ |
| void |
| woal_submit_rx_urb(moal_handle *handle, t_u8 ep) |
| { |
| struct usb_card_rec *cardp = (struct usb_card_rec *)handle->card; |
| |
| ENTER(); |
| |
| if ((ep == cardp->rx_cmd_ep) && |
| (!atomic_read(&cardp->rx_cmd_urb_pending))) { |
| woal_usb_submit_rx_urb(&cardp->rx_cmd, MLAN_RX_CMD_BUF_SIZE); |
| } |
| |
| LEAVE(); |
| return; |
| } |
| |
| /** |
| * @brief This function dump firmware memory to file |
| * |
| * @param phandle A pointer to moal_handle |
| * |
| * @return N/A |
| */ |
| static void |
| woal_usb_dump_fw_info(moal_handle *phandle) |
| { |
| moal_private *priv = NULL; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_misc_cfg *pcfg_misc = NULL; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| priv = woal_get_priv(phandle, MLAN_BSS_ROLE_ANY); |
| if (!priv) { |
| PRINTM(MERROR, "woal_usb_dump_fw_info: priv is NULL!\n"); |
| goto done; |
| } |
| /* Allocate an IOCTL request buffer */ |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); |
| if (req == NULL) { |
| PRINTM(MERROR, "woal_usb_dump_fw_info: alloc req fail!\n"); |
| goto done; |
| } |
| |
| /* Fill request buffer */ |
| pcfg_misc = (mlan_ds_misc_cfg *)req->pbuf; |
| pcfg_misc->sub_command = MLAN_OID_MISC_FW_DUMP_EVENT; |
| req->req_id = MLAN_IOCTL_MISC_CFG; |
| req->action = MLAN_ACT_GET; |
| |
| /* Send IOCTL request to MLAN */ |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| phandle->is_fw_dump_timer_set = MTRUE; |
| woal_mod_timer(&phandle->fw_dump_timer, MOAL_TIMER_5S); |
| |
| done: |
| LEAVE(); |
| return; |
| } |
| |
| static mlan_status |
| woal_usb_get_fw_name(moal_handle *handle) |
| { |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| #if defined(USB8997) || defined(USB9098) || defined(USB9097) || defined(USB8978) |
| t_u32 revision_id = 0; |
| t_u32 strap = 0; |
| #endif |
| struct usb_card_rec *cardp = (struct usb_card_rec *)handle->card; |
| #if defined(USB9098) |
| moal_handle *ref_handle = NULL; |
| #endif |
| |
| ENTER(); |
| if (handle->params.fw_name) |
| goto done; |
| if (cardp->boot_state == USB_FW_READY) |
| goto done; |
| #ifdef USB8801 |
| if (IS_USB8801(handle->card_type)) |
| goto done; |
| #endif |
| |
| #if defined(USB8997) || defined(USB9098) || defined(USB9097) || defined(USB8978) |
| ret = woal_check_chip_revision(handle, &revision_id, &strap); |
| if (ret != MLAN_STATUS_SUCCESS) { |
| PRINTM(MFATAL, "Chip revision check failure!\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| PRINTM(MCMND, "revision=0x%x, strap=0x%x\n", revision_id, strap); |
| #endif |
| |
| #ifdef USB8997 |
| if (IS_USB8997(handle->card_type)) { |
| if (strap == CARD_TYPE_USB_UART) |
| strcpy(handle->card_info->fw_name, |
| USBUART8997_DEFAULT_COMBO_FW_NAME); |
| else if (strap != 0) |
| strcpy(handle->card_info->fw_name, |
| USBUSB8997_DEFAULT_COMBO_FW_NAME); |
| } |
| #endif |
| |
| #ifdef USB8978 |
| if (IS_USB8978(handle->card_type)) { |
| if (strap == CARD_TYPE_USB_UART) |
| strcpy(handle->card_info->fw_name, |
| USBUART8978_DEFAULT_COMBO_FW_NAME); |
| else if (strap != 0) |
| strcpy(handle->card_info->fw_name, |
| USBUSB8978_DEFAULT_COMBO_FW_NAME); |
| } |
| #endif |
| |
| #ifdef USB9098 |
| if (IS_USB9098(handle->card_type)) { |
| if (cardp->second_mac) { |
| ref_handle = (moal_handle *)handle->pref_mac; |
| if (ref_handle) { |
| strcpy(handle->card_info->fw_name, |
| ref_handle->card_info->fw_name); |
| strcpy(handle->card_info->fw_name_wlan, |
| ref_handle->card_info->fw_name_wlan); |
| } |
| goto done; |
| } |
| switch (revision_id) { |
| case USB9098_Z1Z2: |
| if (strap != 0) { |
| if (strap == CARD_TYPE_USB_UART) |
| strcpy(handle->card_info->fw_name, |
| USBUART9098_DEFAULT_COMBO_FW_NAME); |
| else |
| strcpy(handle->card_info->fw_name, |
| USBUSB9098_DEFAULT_COMBO_FW_NAME); |
| } |
| strcpy(handle->card_info->fw_name_wlan, |
| USB9098_DEFAULT_WLAN_FW_NAME); |
| break; |
| case USB9098_A0: |
| case USB9098_A1: |
| case USB9098_A2: |
| if (strap != 0) { |
| if (strap == CARD_TYPE_USB_UART) |
| strcpy(handle->card_info->fw_name, |
| USBUART9098_COMBO_V1_FW_NAME); |
| else |
| strcpy(handle->card_info->fw_name, |
| USBUSB9098_COMBO_V1_FW_NAME); |
| } |
| strcpy(handle->card_info->fw_name_wlan, |
| USB9098_WLAN_V1_FW_NAME); |
| break; |
| } |
| } |
| #endif |
| #ifdef USB9097 |
| if (IS_USB9097(handle->card_type)) { |
| switch (revision_id) { |
| case USB9097_B0: |
| case USB9097_B1: |
| if (strap != 0) { |
| if (strap == CARD_TYPE_USB_UART) |
| strcpy(handle->card_info->fw_name, |
| USBUART9097_COMBO_V1_FW_NAME); |
| else |
| strcpy(handle->card_info->fw_name, |
| USBUSB9097_COMBO_V1_FW_NAME); |
| } |
| strcpy(handle->card_info->fw_name_wlan, |
| USB9097_WLAN_V1_FW_NAME); |
| break; |
| } |
| } |
| #endif |
| done: |
| PRINTM(MCMND, "combo fw:%s wlan fw:%s \n", handle->card_info->fw_name, |
| handle->card_info->fw_name_wlan); |
| LEAVE(); |
| return ret; |
| } |
| |
| static moal_if_ops usb_ops = { |
| .register_dev = woal_usb_register_dev, |
| .unregister_dev = woal_usb_unregister_dev, |
| .read_data_sync = woal_usb_read_data_sync, |
| .write_data_sync = woal_usb_write_data_sync, |
| .get_fw_name = woal_usb_get_fw_name, |
| .dump_fw_info = woal_usb_dump_fw_info, |
| .is_second_mac = woal_usb_is_second_mac, |
| }; |