/* SPDX-License-Identifier: GPL-2.0 */ #include "wcn_usb.h" #define INTF_LOCK_TIMEOUT 10000 static const struct usb_device_id wcn_usb_io_id_table[] = { { USB_DEVICE(0x1782, 0x4d00) }, { USB_DEVICE(0x0403, 0x6011) }, { USB_DEVICE(0x04b4, 0x1004) }, { USB_DEVICE(0x1782, 0x5d35) }, { } /* Terminating entry */ }; static inline int intf_hold(struct wcn_usb_intf *intf) { unsigned long irq; spin_lock_irqsave(&intf->lock, irq); if (intf->change_flag == 1) { spin_unlock_irqrestore(&intf->lock, irq); return -EBUSY; } intf->num_users++; spin_unlock_irqrestore(&intf->lock, irq); return 0; } static inline void intf_release(struct wcn_usb_intf *intf) { unsigned long irq; spin_lock_irqsave(&intf->lock, irq); intf->num_users--; wake_up(&intf->wait_user); spin_unlock_irqrestore(&intf->lock, irq); } /* if flags == 1, this function will block! */ static int intf_set_unavailable(struct wcn_usb_intf *intf, int flags) { unsigned long irq; spin_lock_irqsave(&intf->lock, irq); if (flags) { mutex_lock(&intf->flag_lock); } else { if (!mutex_trylock(&intf->flag_lock)) { spin_unlock_irqrestore(&intf->lock, irq); return -EBUSY; } } intf->change_flag = 1; spin_unlock_irqrestore(&intf->lock, irq); if (intf->num_users != 0) if (!wait_event_timeout(intf->wait_user, !(intf->num_users), INTF_LOCK_TIMEOUT)) return -ETIMEDOUT; return 0; } static void intf_set_available(struct wcn_usb_intf *intf) { unsigned long irq; spin_lock_irqsave(&intf->lock, irq); intf->change_flag = 0; spin_unlock_irqrestore(&intf->lock, irq); mutex_unlock(&intf->flag_lock); } static int ep_hold_intf(struct wcn_usb_ep *ep) { unsigned long irq; int ret = -ENODEV; spin_lock_irqsave(&ep->intf_lock, irq); if (ep->intf) ret = intf_hold(ep->intf); spin_unlock_irqrestore(&ep->intf_lock, irq); return ret; } static void ep_release_intf(struct wcn_usb_ep *ep) { intf_release(ep->intf); } /* please guarantee intf is not NULL! */ struct usb_endpoint_descriptor *wcn_usb_intf2endpoint(struct wcn_usb_intf *intf, __u8 numEp) { struct usb_endpoint_descriptor *endpoint; struct usb_host_interface *iface_desc; if (numEp > wcn_usb_intf_epNum(intf)) return NULL; iface_desc = intf->interface->cur_altsetting; endpoint = &iface_desc->endpoint[numEp].desc; return endpoint; } /* we can make it faster, If we have to */ static int __intf_fill_store_callback(struct wcn_usb_ep *ep, void *data) { struct wcn_usb_intf *intf; __u8 epAddress; int epNum; struct usb_endpoint_descriptor *endpoint; int i; unsigned long irq; intf = (struct wcn_usb_intf *)data; epAddress = wcn_usb_store_chn2addr(ep->channel); epNum = wcn_usb_intf_epNum(intf); for (i = 0; i < epNum; i++) { endpoint = wcn_usb_intf2endpoint(intf, i); if (endpoint->bEndpointAddress == epAddress) { wcn_usb_info("%s ep will be register %x\n", __func__, epAddress); spin_lock_irqsave(&ep->intf_lock, irq); ep->intf = intf; spin_unlock_irqrestore(&ep->intf_lock, irq); ep->numEp = i; break; } } return 0; } void wcn_usb_ep_stop(struct wcn_usb_ep *ep) { int ret; unsigned long flags; ret = ep_hold_intf(ep); if (ret) { wcn_usb_err("%s get lock error\n", __func__); return; } /* the lock of kernel is Unreliable!! so we do it */ spin_lock_irqsave(&ep->submit_lock, flags); usb_kill_anchored_urbs(&ep->submitted); spin_unlock_irqrestore(&ep->submit_lock, flags); ep_release_intf(ep); } static int __intf_erase_store_callback(struct wcn_usb_ep *ep, void *data) { struct wcn_usb_intf *intf; __u8 epAddress; int epNum; struct usb_endpoint_descriptor *endpoint; int i; unsigned long irq; intf = (struct wcn_usb_intf *)data; epAddress = wcn_usb_store_chn2addr(ep->channel); epNum = wcn_usb_intf_epNum(intf); for (i = 0; i < epNum; i++) { endpoint = wcn_usb_intf2endpoint(intf, i); if (endpoint->bEndpointAddress == epAddress) { wcn_usb_info("%s ep will be unregister %x\n", __func__, epAddress); spin_lock_irqsave(&ep->intf_lock, irq); ep->intf = NULL; spin_unlock_irqrestore(&ep->intf_lock, irq); ep->numEp = -1; wcn_usb_ep_stop(ep); break; } } return 0; } static int wcn_usb_intf_fill_store(struct wcn_usb_intf *intf) { return wcn_usb_store_travel_ep(__intf_fill_store_callback, intf); } static int wcn_usb_intf_erase_store(struct wcn_usb_intf *intf) { return wcn_usb_store_travel_ep(__intf_erase_store_callback, intf); } int wcn_usb_ep_can_dma(struct wcn_usb_ep *ep) { int ret; ret = ep_hold_intf(ep); if (ret) { wcn_usb_err("%s get lock error\n", __func__); return 0; } if (wcn_usb_ep_is_isoc(ep) || !wcn_usb_ep_no_sg_constraint(ep)) ret = 0; else ret = 1; ep_release_intf(ep); return 1; } unsigned int wcn_usb_packet_recv_len(struct wcn_usb_packet *packet) { int ret; unsigned int len = 0; int i; ret = ep_hold_intf(packet->ep); if (ret) { wcn_usb_err("%s ep lock set failed\n", __func__); return 0; } if (wcn_usb_ep_is_isoc(packet->ep)) { for (i = 0; i < packet->urb->number_of_packets; i++) len += packet->urb->iso_frame_desc[i].actual_length; } else { len = packet->urb->actual_length; } ep_release_intf(packet->ep); return len; } /* unsigned int *pip is ret value */ unsigned int wcn_usb_ep_pipe(struct wcn_usb_ep *ep, unsigned int *pip) { struct usb_endpoint_descriptor *endpoint; int ret; ret = ep_hold_intf(ep); if (ret) { wcn_usb_err("%s hold intf fail %d\n", __func__, ret); return ret; } endpoint = wcn_usb_intf2endpoint(ep->intf, ep->numEp); if (usb_endpoint_is_bulk_in(endpoint)) *pip = usb_rcvbulkpipe(ep->intf->udev, endpoint->bEndpointAddress); if (usb_endpoint_is_bulk_out(endpoint)) *pip = usb_sndbulkpipe(ep->intf->udev, endpoint->bEndpointAddress); if (usb_endpoint_is_int_in(endpoint)) *pip = usb_rcvintpipe(ep->intf->udev, endpoint->bEndpointAddress); if (usb_endpoint_is_int_out(endpoint)) *pip = usb_sndintpipe(ep->intf->udev, endpoint->bEndpointAddress); if (usb_endpoint_is_isoc_in(endpoint)) *pip = usb_rcvisocpipe(ep->intf->udev, endpoint->bEndpointAddress); if (usb_endpoint_is_isoc_out(endpoint)) *pip = usb_sndisocpipe(ep->intf->udev, endpoint->bEndpointAddress); if (usb_endpoint_xfer_control(endpoint) && usb_endpoint_dir_in(endpoint)) *pip = usb_rcvctrlpipe(ep->intf->udev, endpoint->bEndpointAddress); if (usb_endpoint_xfer_control(endpoint) && usb_endpoint_dir_out(endpoint)) *pip = usb_sndctrlpipe(ep->intf->udev, endpoint->bEndpointAddress); ep_release_intf(ep); return 0; } void wcn_usb_io_urb_callback(struct urb *urb) { struct wcn_usb_packet *packet; packet = urb->context; wcn_usb_print_packet(packet); packet->callback(packet); } static int wcn_usb_ep_set_packet(struct wcn_usb_ep *ep, struct wcn_usb_packet *packet, gfp_t mem_flags) { int ret; packet->ep = ep; packet->urb->dev = ep->intf->udev; ret = wcn_usb_ep_pipe(ep, &packet->urb->pipe); if (ret != 0) return ret; packet->urb->context = packet; packet->urb->complete = wcn_usb_io_urb_callback; if (wcn_usb_ep_is_isoc(ep) || wcn_usb_ep_is_int(ep)) wcn_usb_packet_interval(packet, wcn_usb_ep_interval(ep)); return 0; } struct wcn_usb_packet *wcn_usb_alloc_packet(gfp_t mem_flags) { struct wcn_usb_packet *packet; packet = kzalloc(sizeof(struct wcn_usb_packet), mem_flags); if (packet == NULL) return NULL; packet->urb = usb_alloc_urb(0, mem_flags); if (packet->urb == NULL) goto PACKET_FREE; return packet; PACKET_FREE: kfree(packet); return NULL; } int wcn_usb_packet_bind(struct wcn_usb_packet *packet, struct wcn_usb_ep *ep, gfp_t mem_flags) { int ret; ret = ep_hold_intf(ep); if (ret) { wcn_usb_err("%s hold intf fail %d\n", __func__, ret); return ret; } ret = wcn_usb_ep_set_packet(ep, packet, mem_flags); if (ret) wcn_usb_err("%s hold ep set packet failed %d\n", __func__, ret); ep_release_intf(ep); return ret; } /* return number_of_packets */ int wcn_usb_frame_fill(struct usb_iso_packet_descriptor *iso_frame_desc, int len, int mtu, int max_number_of_packets) { int i, offset = 0; for (i = 0; i < max_number_of_packets && len >= mtu; i++, offset += mtu, len -= mtu) { iso_frame_desc[i].offset = offset; iso_frame_desc[i].length = mtu; } if (len && i < max_number_of_packets) { iso_frame_desc[i].offset = offset; iso_frame_desc[i].length = len; i++; } return i; } int wcn_usb_packet_is_freed(struct wcn_usb_packet *packet) { if (atomic_read(&(&packet->urb->kref)->refcount) != 1) return 0; return 1; } void wcn_usb_packet_clean(struct wcn_usb_packet *packet) { /* keep urb */ /* clean other field */ memset((void *)packet + sizeof(void *), 0, sizeof(struct wcn_usb_packet) - sizeof(void *)); if (packet->urb) usb_init_urb(packet->urb); } int wcn_usb_packet_set_buf(struct wcn_usb_packet *packet, void *buf, ssize_t buf_size, gfp_t mem_flags) { int ret; int max_number_of_packets; ret = ep_hold_intf(packet->ep); if (ret) { wcn_usb_err("%s hold intf fail %d\n", __func__, ret); return ret; } if (wcn_usb_ep_is_isoc(packet->ep)) { if (!wcn_usb_ep_packet_max(packet->ep)) { ep_release_intf(packet->ep); if (wcn_usb_ep_set(packet->ep, 1)) { wcn_usb_err("%s maxSize is zero, can't set\n", __func__); return -EIO; } ret = ep_hold_intf(packet->ep); if (ret) { wcn_usb_err("%s hold intf fail %d\n", __func__, ret); return ret; } } usb_free_urb(packet->urb); max_number_of_packets = buf_size / wcn_usb_ep_packet_max(packet->ep) + 1; packet->urb = usb_alloc_urb(max_number_of_packets, mem_flags); if (!packet->urb) { wcn_usb_err("%s usb_alloc_urb failed\n", __func__); ep_release_intf(packet->ep); return -ENOMEM; } if (wcn_usb_ep_set_packet(packet->ep, packet, mem_flags)) { ep_release_intf(packet->ep); return -ENODEV; } packet->urb->transfer_flags = URB_ISO_ASAP; packet->urb->number_of_packets = wcn_usb_frame_fill( packet->urb->iso_frame_desc, buf_size, wcn_usb_ep_packet_max(packet->ep), max_number_of_packets ); } packet->urb->transfer_buffer = buf; packet->urb->transfer_buffer_length = buf_size; ep_release_intf(packet->ep); return 0; } void *wcn_usb_packet_pop_buf(struct wcn_usb_packet *packet) { void *ret; if (!packet && !packet->urb) return NULL; ret = packet->urb->transfer_buffer; packet->urb->transfer_buffer = NULL; return ret; } #if 0 /* reserved this function */ void *wcn_usb_packet_alloc_buf(struct wcn_usb_packet *packet, ssize_t buf_size, gfp_t mem_flags) { /* alloc a urb */ /* call a usb_alloc_coherent to get buf */ /* set packet->is_usb_dma */ /* fill urb */ return NULL; } /* only for wcn_usb_packet_alloc_buf */ void wcn_usb_packet_free_buf(struct wcn_usb_packet *packet) { /* free alloc_buf */ /* free urb */ } #endif int wcn_usb_packet_set_setup_packet(struct wcn_usb_packet *packet, struct usb_ctrlrequest *setup_packet) { if (!packet && !packet->urb) return -EINVAL; packet->urb->setup_packet = (char *)setup_packet; return 0; } struct usb_ctrlrequest *wcn_usb_packet_pop_setup_packet( struct wcn_usb_packet *packet) { struct usb_ctrlrequest *ret; if (!packet && !packet->urb) return NULL; ret = (struct usb_ctrlrequest *)packet->urb->setup_packet; packet->urb->setup_packet = NULL; return ret; } int wcn_usb_packet_set_sg(struct wcn_usb_packet *packet, struct scatterlist *sg, int num_sgs, unsigned int buf_len) { int ret; if (!packet && !packet->urb) return -EINVAL; /* that mean clean sg arg */ if (!sg) { packet->urb->sg = NULL; packet->urb->num_sgs = 0; packet->urb->transfer_buffer_length = 0; return 0; } ret = ep_hold_intf(packet->ep); if (ret) { wcn_usb_err("%s hold intf fail %d\n", __func__, ret); return ret; } if (wcn_usb_ep_is_isoc(packet->ep)) { wcn_usb_err("%s sg can be used in isoc\n", __func__); ep_release_intf(packet->ep); return -EINVAL; } ep_release_intf(packet->ep); packet->urb->sg = sg; packet->urb->num_sgs = num_sgs; packet->urb->transfer_buffer_length = buf_len; return 0; } struct scatterlist *wcn_usb_packet_pop_sg(struct wcn_usb_packet *packet, int *num_sgs) { struct scatterlist *sg; if (num_sgs) *num_sgs = packet->urb->num_sgs; packet->urb->num_sgs = 0; sg = packet->urb->sg; packet->urb->sg = NULL; return sg; } void wcn_usb_packet_free(struct wcn_usb_packet *packet) { usb_unanchor_urb(packet->urb); usb_free_urb(packet->urb); kfree(packet); } /* wcn_usb_packet_set_buf and wcn_usb_packet_alloc_buf will fill * interval and iso_frame_desc as default. (dependent on ep descriptive char) * and they didn't fill setup_packet for contrl transfer. * so, If you need change setup_packet or iso_frame_desc or interval, you need * function as follow. */ int wcn_usb_packet_frame_desc(struct wcn_usb_packet *packet, const struct usb_iso_packet_descriptor *frame, int framNum) { if (framNum > packet->urb->number_of_packets) return -EINVAL; memcpy(packet->urb->iso_frame_desc, frame, sizeof(struct usb_iso_packet_descriptor) * framNum); packet->urb->number_of_packets = framNum; return 0; } void wcn_usb_packet_interval(struct wcn_usb_packet *packet, int interval) { if (packet->urb->dev->speed == USB_SPEED_HIGH || packet->urb->dev->speed == USB_SPEED_SUPER) { interval = clamp(interval, 1, 16); packet->urb->interval = 1 << (interval - 1); } else { packet->urb->interval = interval; } packet->urb->start_frame = -1; } static int wcn_usb_need_zlp(struct wcn_usb_ep *ep, ssize_t buf_size) { if ((wcn_usb_ep_is_int(ep) || wcn_usb_ep_is_bulk(ep)) && !wcn_usb_ep_dir(ep) && !(buf_size % wcn_usb_ep_packet_max(ep))) return 1; return 0; } static void zlp_callback(struct urb *urb) { kfree(urb->transfer_buffer); usb_free_urb(urb); } /* * because musb_sprd cant sent zero length packet! * we sent a 1 byte length packet instead of zlp. */ int wcn_usb_send_zlp(struct wcn_usb_ep *ep, gfp_t mem_flags) { struct urb *urb; struct usb_endpoint_descriptor *endpoint; void *buf; buf = kzalloc(1, GFP_ATOMIC); if (buf == NULL) return -ENOMEM; endpoint = wcn_usb_intf2endpoint(ep->intf, ep->numEp); urb = usb_alloc_urb(0, mem_flags); if (urb == NULL) { kfree(buf); return -ENOMEM; } usb_fill_bulk_urb(urb, ep->intf->udev, usb_sndbulkpipe(ep->intf->udev, endpoint->bEndpointAddress), buf, 1, zlp_callback, NULL); usb_anchor_urb(urb, &ep->submitted); return usb_submit_urb(urb, mem_flags); } /* We have not touch packet after we submit it!!! */ int wcn_usb_packet_submit(struct wcn_usb_packet *packet, void (*callback)(struct wcn_usb_packet *packet), void *pdata, gfp_t mem_flags) { int ret; struct wcn_usb_ep *ep; ssize_t buf_size; unsigned long flags; if (packet == NULL || callback == NULL || packet->ep == NULL) return -EINVAL; wcn_usb_debug("%s be called\n", __func__); packet->pdata = pdata; packet->callback = callback; ret = ep_hold_intf(packet->ep); if (ret) { wcn_usb_err("%s lock error\n", __func__); return ret; } /*debug*/ if (wcn_usb_state_get(error_happen)) { ep_release_intf(packet->ep); return -EIO; } spin_lock_irqsave(&packet->ep->submit_lock, flags); ep = packet->ep; buf_size = packet->urb->transfer_buffer_length; usb_anchor_urb(packet->urb, &packet->ep->submitted); wcn_usb_print_packet(packet); ret = usb_submit_urb(packet->urb, mem_flags); if (ret) wcn_usb_err("%s send urb error %d\n", __func__, ret); if (wcn_usb_need_zlp(ep, buf_size)) { ret = wcn_usb_send_zlp(ep, mem_flags); if (ret) wcn_usb_err("%s send zlp error %d\n", __func__, ret); } spin_unlock_irqrestore(&packet->ep->submit_lock, flags); ep_release_intf(ep); return ret; } int wcn_usb_msg(struct wcn_usb_ep *ep, void *data, int len, int *actual_length, int timeout) { int ret; unsigned int pip; ret = ep_hold_intf(ep); if (ret) return ret; ret = wcn_usb_ep_pipe(ep, &pip); if (ret) goto OUT; if (wcn_usb_ep_is_int(ep)) { ret = usb_interrupt_msg(ep->intf->udev, pip, data, len, actual_length, timeout); } else if (wcn_usb_ep_is_bulk(ep)) { ret = usb_bulk_msg(ep->intf->udev, pip, data, len, actual_length, timeout); } else { wcn_usb_err("%s only int bulk can be sent with msg", __func__); ret = -EIO; } OUT: ep_release_intf(ep); if (ret) wcn_usb_err("%s error!\n", __func__); return ret; } int wcn_usb_packet_get_status(struct wcn_usb_packet *packet) { int i; int ret = 0; ret = ep_hold_intf(packet->ep); if (ret) { wcn_usb_err("%s hold intf fail %d\n", __func__, ret); return ret; } if (!wcn_usb_ep_is_isoc(packet->ep) || packet->urb->status < 0) { ret = packet->urb->status; goto RETURN_STATUS; } for (i = 0; i < packet->urb->number_of_packets; i++) { ret = packet->urb->iso_frame_desc[i].status; if (ret < 0) { wcn_usb_err("%s packet %d status %d\n", __func__, i, ret); goto RETURN_STATUS; } } RETURN_STATUS: ep_release_intf(packet->ep); return ret; } /* This function couldn't be called in context that blow intf_hold !*/ int wcn_usb_ep_set(struct wcn_usb_ep *ep, int setting_id) { int ret; struct wcn_usb_intf *intf; __u8 intrNumber; struct usb_device *udev; ret = ep_hold_intf(ep); if (ret) { wcn_usb_err("%s intf get failed\n", __func__); return -EBUSY; } intf = ep->intf; if (!intf) { ep_release_intf(ep); wcn_usb_err("%s intf is null\n", __func__); return -ENODEV; } intrNumber = intf->interface->cur_altsetting->desc.bInterfaceNumber; udev = usb_get_dev(ep->intf->udev); ep_release_intf(ep); ret = intf_set_unavailable(intf, 0); if (ret) { wcn_usb_err("%s intf_set_unavailable failed\n", __func__); usb_put_dev(udev); return ret; } wcn_usb_err("%s %d\n", __func__, __LINE__); wcn_usb_intf_erase_store(intf); /* TODO check this */ ret = usb_set_interface(udev, intrNumber, setting_id); wcn_usb_intf_fill_store(intf); intf_set_available(intf); usb_put_dev(udev); return ret; } /* we don't need usb usb major number to find interface */ #if 0 #define USB_SWCN_MINOR_BASE 123 /* TODO this fops need fill! Plan is that: fill it with io_dbg_fop */ static struct usb_class_driver wcn_usb_io_class = { .name = "sprd_wcn_io_%d", .fops = NULL, .minor_base = USB_SWCN_MINOR_BASE, }; #endif static atomic_t wcn_usb_suspend_flag; static atomic_t wcn_usb_resume_flag; static int wcn_usb_io_probe(struct usb_interface *interface, const struct usb_device_id *id) { /* init a struct wcn_usb_intf and fill it! */ struct wcn_usb_intf *intf; int interface_num; intf = kzalloc(sizeof(struct wcn_usb_intf), GFP_KERNEL); if (!intf) return -ENOMEM; usb_set_intfdata(interface, intf); spin_lock_init(&intf->lock); mutex_init(&intf->flag_lock); init_waitqueue_head(&intf->wait_user); intf->interface = usb_get_intf(interface); intf->udev = usb_get_dev(interface_to_usbdev(interface)); /* register struct wcn_usb_intf */ interface_num = interface->cur_altsetting->desc.bInterfaceNumber; wcn_usb_intf_fill_store(intf); wcn_usb_info("interface[%x] is register\n", interface_num); atomic_set(&wcn_usb_suspend_flag, -1); atomic_set(&wcn_usb_resume_flag, 0); wcn_usb_state_sent_event(interface_plug_base + interface_num); if ((interface_plug_base + interface_num) == interface_2_plug) { if (marlin_probe_status()) { sprdwcn_bus_set_carddump_status(false); marlin_schedule_usb_hotplug(); } } return 0; } static void wcn_usb_io_disconnect(struct usb_interface *interface) { /* we must clean all thing! * this driver may be probe and disconnect so much times! */ struct wcn_usb_intf *intf; int interface_num; interface_num = interface->cur_altsetting->desc.bInterfaceNumber; if ((interface_unplug_base + interface_num) == interface_0_unplug) { if (marlin_probe_status()) { marlin_reset_notify_call(MARLIN_CP2_STS_ASSERTED); wcn_usb_state_clear(cp_ready); wcn_usb_state_sent_event(pwr_off); marlin_set_usb_hotplug_status(1); wcn_usb_info("%s usb hotplug disconnect\n", __func__); } } wcn_usb_state_sent_event(interface_unplug_base + interface_num); wcn_usb_info("interface[%x] will be unregister\n", interface_num); intf = usb_get_intfdata(interface); /* this lock must give me! */ intf_set_unavailable(intf, 1); usb_set_intfdata(interface, NULL); wcn_usb_intf_erase_store(intf); usb_put_dev(intf->udev); usb_put_intf(interface); kfree(intf); } static int wcn_usb_io_pre_reset(struct usb_interface *interface) { /* there is a lock to prevent we reset a interface when * urb submit */ struct wcn_usb_intf *intf; intf = usb_get_intfdata(interface); intf_set_unavailable(intf, 1); return 0; } static int wcn_usb_io_post_reset(struct usb_interface *interface) { /* free the lock that we take in wcn_usb_io_pre_reset */ struct wcn_usb_intf *intf; intf = usb_get_intfdata(interface); intf_set_available(intf); return 0; } #include "bus_common.h" extern int wcn_usb_channel_is_apostle(int chn); static int wcn_usb_io_suspend(struct usb_interface *intf, pm_message_t message) { int chn, ret = 0; struct mchn_ops_t *wcn_usb_ops; struct wcn_usb_ep *ep_to_suspend; /* different with PM_EVENT_AUTO_SUSPEND */ if (message.event == PM_EVENT_AUTO_SUSPEND) { wcn_usb_err("%s pm_event[%d] to be confirm...\n", __func__, message.event); return 0; } wcn_usb_info("%s flag=%d\n", __func__, atomic_read(&wcn_usb_suspend_flag)); if (!atomic_inc_return(&wcn_usb_suspend_flag)) { for (chn = 0; chn < 32; chn++) { wcn_usb_ops = chn_ops(chn); if (wcn_usb_ops && wcn_usb_ops->power_notify) { wcn_usb_info("%s chn[%d] power notify...\n", __func__, chn); ret = wcn_usb_ops->power_notify(chn, false); wcn_usb_info("%s chn[%d] suspend[%d]\n", __func__, chn, ret); if (ret) { wcn_usb_err("%s chn[%d] suspend failed[%d]\n", __func__, chn, ret); goto not_allowed; } } } for (chn = 1; chn < 16; chn++) { ep_to_suspend = wcn_usb_store_get_epFRchn(chn); if (ep_to_suspend) { wcn_usb_info("%s check tx chn[%d]\n", __func__, chn); if (!usb_anchor_empty(&ep_to_suspend->submitted)) { wcn_usb_err("%s tx chn[%d] remaining to be sent\n", __func__, chn); ret = -1; goto not_allowed; } } } ep_to_suspend = wcn_usb_store_get_epFRchn(25); if (likely(ep_to_suspend)) { wcn_usb_info("%s kill urb of chn[%d]\n", __func__, 25); usb_kill_anchored_urbs(&ep_to_suspend->submitted); } else { wcn_usb_err("%s urb not exit of chn[%d]\n", __func__, 25); } for (chn = 16; chn < 32; chn++) { ep_to_suspend = wcn_usb_store_get_epFRchn(chn); if (ep_to_suspend) { wcn_usb_info("%s kill urb of chn[%d]\n", __func__, chn); usb_kill_anchored_urbs(&ep_to_suspend->submitted); } } atomic_set(&wcn_usb_resume_flag, -1); } return 0; not_allowed: atomic_set(&wcn_usb_suspend_flag, -1); return ret; } static int wcn_usb_io_resume(struct usb_interface *intf) { int chn, ret = 0; struct mchn_ops_t *wcn_usb_ops; struct wcn_usb_ep *ep_to_resume; wcn_usb_info("%s flag=%d\n", __func__, atomic_read(&wcn_usb_resume_flag)); if (!atomic_inc_return(&wcn_usb_resume_flag)) { wcn_usb_info("%s fire apostle\n", __func__); ret = wcn_usb_apostle_begin(25); if (ret) { wcn_usb_err("%s apostle begin failed[%d]\n", __func__, ret); goto out; } for (chn = 0; chn < 32; chn++) { wcn_usb_ops = chn_ops(chn); if (wcn_usb_ops && wcn_usb_ops->power_notify) { wcn_usb_info("%s chn[%d] power notify...\n", __func__, chn); ret = wcn_usb_ops->power_notify(chn, true); wcn_usb_info("%s chn[%d] resume[%d]\n", __func__, chn, ret); if (ret) wcn_usb_err("%s chn[%d] resume failed[%d]\n", __func__, chn, ret); } } for (chn = 16; chn < 32; chn++) { if (!wcn_usb_channel_is_apostle(chn)) { ep_to_resume = wcn_usb_store_get_epFRchn(chn); if (ep_to_resume) { wcn_usb_info("%s retrieve rx chn[%d]\n", __func__, chn); wcn_usb_begin_poll_rx(chn); } } } } out: atomic_set(&wcn_usb_suspend_flag, -1); return 0; } #ifdef CONFIG_AML_BOARD static void wcn_usb_io_shutdown(struct device *dev) { int chn, interface_num; struct wcn_usb_ep *ep_to_suspend; struct usb_interface *intf; intf = to_usb_interface(dev); interface_num = intf->cur_altsetting->desc.bInterfaceNumber; if ((interface_plug_base + interface_num) == interface_0_plug) { for (chn = 1; chn < 16; chn++) { ep_to_suspend = wcn_usb_store_get_epFRchn(chn); if (ep_to_suspend) { wcn_usb_info("%s check tx chn[%d]\n", __func__, chn); if (!usb_anchor_empty(&ep_to_suspend->submitted)) { wcn_usb_err("%s tx chn[%d] remaining to be sent\n", __func__, chn); } } } ep_to_suspend = wcn_usb_store_get_epFRchn(25); if (likely(ep_to_suspend)) { wcn_usb_info("%s kill urb of chn[%d]\n", __func__, 25); usb_kill_anchored_urbs(&ep_to_suspend->submitted); } else { wcn_usb_err("%s urb not exit of chn[%d]\n", __func__, 25); } for (chn = 16; chn < 32; chn++) { ep_to_suspend = wcn_usb_store_get_epFRchn(chn); if (ep_to_suspend) { wcn_usb_info("%s kill urb of chn[%d]\n", __func__, chn); usb_kill_anchored_urbs(&ep_to_suspend->submitted); } } } } #endif #if 0 #include static struct task_struct *wcn_usb_ctrlmsg_thread; static int wcn_usb_ctrlmsg_func(void *data) { int ret, i; struct wcn_usb_ep *wcn_ep; struct usb_device *udev; char *buffer; set_freezable(); do { buffer = kzalloc(0xff, GFP_KERNEL); if (!buffer) { wcn_usb_err("%s test end because of nomem\n", __func__); goto exit; } wcn_ep = wcn_usb_store_get_epFRchn(25); if (likely(wcn_ep)) { udev = wcn_ep->intf->udev; ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x06, 0x80, 0x300, 0x0, (void *)buffer, 0xff, 5000); if (ret > 0) { for (i = 0; i < ret; i++) { wcn_usb_info("%s data[%d]=%x\n", __func__, i, buffer[i]); } } else { wcn_usb_err("%s usb_control_msg failed[%d]\n", __func__, ret); } } kfree(data); try_to_freeze(); msleep(10); } while (!kthread_should_stop()); exit: wcn_usb_info("exit %s\n", __func__); return 0; } int start_wcn_usb_ctrlmsg_test(void) { if (wcn_usb_ctrlmsg_thread) { wcn_usb_err("%s test already started\n", __func__); return 0; } wcn_usb_ctrlmsg_thread = kthread_create(wcn_usb_ctrlmsg_func, NULL, "wcn_ctrlmsg"); if (!wcn_usb_ctrlmsg_thread) { wcn_usb_err("%s start ctrlmsg failed\n", __func__); return -ENOMEM; } wake_up_process(wcn_usb_ctrlmsg_thread); return 0; } int stop_wcn_usb_ctrlmsg_test(void) { if (wcn_usb_ctrlmsg_thread) { kthread_stop(wcn_usb_ctrlmsg_thread); } return 0; } #endif void wcn_usb_lock(void) { wcn_usb_info("%s\n", __func__); usb_lock_device(wcn_usb_store_get_epFRchn(25)->intf->udev); } void wcn_usb_unlock(void) { wcn_usb_info("%s\n", __func__); usb_unlock_device(wcn_usb_store_get_epFRchn(25)->intf->udev); } struct usb_driver wcn_usb_io_driver = { .name = "wcn_usb_io", .probe = wcn_usb_io_probe, .disconnect = wcn_usb_io_disconnect, .pre_reset = wcn_usb_io_pre_reset, .post_reset = wcn_usb_io_post_reset, .id_table = wcn_usb_io_id_table, .suspend = wcn_usb_io_suspend, .resume = wcn_usb_io_resume, .reset_resume = wcn_usb_io_resume, #ifdef CONFIG_AML_BOARD .drvwrap.driver.shutdown = wcn_usb_io_shutdown, #endif .supports_autosuspend = 1, }; /** * wcn_usb_io_init() - init wcn_usb_io's memory and register this driver. * @void: void. * * return: zero for success, or a error number return. * NOTE: we can't use module_usb_driver to register our driver. * Because this driver have a connection order with other */ int wcn_usb_io_init(void) { return usb_register(&wcn_usb_io_driver); } void wcn_usb_io_delet(void) { }