#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 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; 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 */ wcn_usb_intf_fill_store(intf); wcn_usb_info("interface[%x] is register\n", interface->cur_altsetting->desc.bInterfaceNumber); wcn_usb_state_sent_event(interface_plug_base + interface->cur_altsetting->desc.bInterfaceNumber); 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; wcn_usb_state_sent_event(interface_unplug_base + interface->cur_altsetting->desc.bInterfaceNumber); wcn_usb_info("interface[%x] will be unregister\n", interface->cur_altsetting->desc.bInterfaceNumber); 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; } 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, .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) { }