/* SPDX-License-Identifier: GPL-2.0 */ #include "wcn_usb.h" #include "bus_common.h" #include #include #include #include #include #include #include #define TRANSF_UNITS 16 #define TRANSF_TOTAL 10 #define WCN_USB_MEMCOPY 128 #define WIFIDATA_OUT_ALIGNMENT 1600 #define WIFIDATA_IN_ALIGNMENT 1620 #define TRANSF_ALIGNMENT_MAX 1672 #define WCN_USB_BT_CTRL_TYPE 0x21 /* 10 HZ */ #define WCN_USB_TIMEOUT (10 * HZ) char dirty_buff[TRANSF_ALIGNMENT_MAX]; struct rx_tx_pool { spinlock_t lock; int pool_size; void *buf; struct list_head remain_head; struct list_head wait_free_head; } rx_tx_pool; static void wcn_usb_print_mbuf(int chn, struct mbuf_t *mbuf_head, int num, const char *func_name) { struct mbuf_t *mbuf; int i; if (!(get_wcn_usb_print_switch() & mbuf_info)) return; wcn_usb_print(mbuf_info, "%s chn %d\n", func_name, chn); mbuf_list_iter(mbuf_head, num, mbuf, i) { wcn_usb_print(mbuf_info, "mbuf %p ->buf %p ->len %d\n", mbuf, mbuf->buf, mbuf->len); if (!mbuf->buf) continue; print_hex_dump(KERN_INFO, "mbuf ", 0, 32, 1, mbuf->buf, 32, 1); } } /* we usb skbuff.c to manager rx buf */ static void *wcn_usb_alloc_buf(unsigned int size, gfp_t gfp_mask) { void *buf; size = SKB_DATA_ALIGN(size); buf = netdev_alloc_frag(size); if (buf) channel_debug_net_malloc(1); return buf; } static void wcn_usb_free_buf(void *buf) { if (buf == NULL) return; channel_debug_net_free(1); put_page(virt_to_head_page(buf)); } static void *wcn_usb_kzalloc(unsigned int size, gfp_t gfp_mask) { void *buf; buf = kzalloc(size, gfp_mask); if (buf) channel_debug_kzalloc(1); return buf; } static void wcn_usb_kfree(void *buf) { if (buf == NULL) return; kfree(buf); } int wcn_usb_rx_tx_pool_init(void) { int i = 0; struct wcn_usb_rx_tx *rx_tx; spin_lock_init(&(rx_tx_pool.lock)); rx_tx_pool.pool_size = 500; rx_tx_pool.buf = wcn_usb_kzalloc(rx_tx_pool.pool_size * sizeof(struct wcn_usb_rx_tx), GFP_KERNEL); if (!rx_tx_pool.buf) return -ENOMEM; INIT_LIST_HEAD(&rx_tx_pool.wait_free_head); INIT_LIST_HEAD(&rx_tx_pool.remain_head); for (i = 0; i < rx_tx_pool.pool_size; i++) { rx_tx = rx_tx_pool.buf + i * sizeof(struct wcn_usb_rx_tx); rx_tx->packet = wcn_usb_alloc_packet(GFP_KERNEL); list_add_tail(&rx_tx->list, &rx_tx_pool.remain_head); } return 0; } void wcn_usb_rx_tx_pool_deinit(void) { struct wcn_usb_rx_tx *rx_tx; spin_lock_bh(&(rx_tx_pool.lock)); list_for_each_entry(rx_tx, &rx_tx_pool.wait_free_head, list) wcn_usb_packet_free(rx_tx->packet); list_for_each_entry(rx_tx, &rx_tx_pool.remain_head, list) wcn_usb_packet_free(rx_tx->packet); INIT_LIST_HEAD(&rx_tx_pool.wait_free_head); INIT_LIST_HEAD(&rx_tx_pool.remain_head); wcn_usb_kfree(rx_tx_pool.buf); spin_unlock_bh(&(rx_tx_pool.lock)); } void wcn_usb_rx_tx_pool_checkout_freed(void) { struct wcn_usb_rx_tx *rx_tx, *n; list_for_each_entry_safe(rx_tx, n, &rx_tx_pool.wait_free_head, list) { if (wcn_usb_packet_is_freed(rx_tx->packet)) list_move_tail(&rx_tx->list, &rx_tx_pool.remain_head); } } struct wcn_usb_rx_tx *wcn_usb_rx_tx_pool_zalloc(void) { struct wcn_usb_rx_tx *rx_tx; spin_lock_bh(&rx_tx_pool.lock); if (list_empty(&rx_tx_pool.remain_head)) wcn_usb_rx_tx_pool_checkout_freed(); if (list_empty(&rx_tx_pool.remain_head)) { spin_lock_bh(&rx_tx_pool.lock); return NULL; } rx_tx = list_first_entry(&rx_tx_pool.remain_head, struct wcn_usb_rx_tx, list); list_del(&rx_tx->list); /* clean */ rx_tx->head = rx_tx->tail = NULL; rx_tx->num = rx_tx->channel = rx_tx->packet_status = 0; wcn_usb_packet_clean(rx_tx->packet); wcn_usb_print_rx_tx(rx_tx); spin_unlock_bh(&rx_tx_pool.lock); return rx_tx; } void wcn_usb_rx_tx_pool_free(struct wcn_usb_rx_tx *rx_tx) { if (!rx_tx) return; spin_lock_bh(&rx_tx_pool.lock); list_add_tail(&rx_tx->list, &rx_tx_pool.wait_free_head); wcn_usb_rx_tx_pool_checkout_freed(); spin_unlock_bh(&rx_tx_pool.lock); } static inline int wcn_usb_channel_is_rx(int channel) { return channel >= 16; } /* There is BUG in MUSB_SPRD's inturrpt */ #ifndef NO_EXCHANGE_CHANNEL_17 static const char report_num_map_chn[] = {18, 20, 21, 22, 23, 24, 17, 31}; #else static const char report_num_map_chn[] = {18, 20, 21, 22, 23, 24, 29, 31}; #endif /*static*/ inline int wcn_usb_channel_is_apostle(int chn) { int i; for (i = 0; i < ARRAY_SIZE(report_num_map_chn); i++) if (chn == report_num_map_chn[i]) return 1; return 0; } static inline int alignment_comm(int channel) { switch (channel) { case 6: return WIFIDATA_OUT_ALIGNMENT; case 22: return WIFIDATA_IN_ALIGNMENT; case 25: return 20; default: return TRANSF_ALIGNMENT_MAX; } } /* NOTE! wcn WIFI __ASK__ mbuf that they recive must more than 2048byte!!! */ static inline int alignment_protocol_stack(int channel) { switch (channel) { case 22: return 2048 - alignment_comm(channel); default: return 0; } } static inline int wcn_usb_channel_is_sg(int chn) { return 0; } static inline int wcn_usb_channel_is_copy(int chn) { return chn == 22 || chn == 6; } static inline int wcn_usb_rx_tx_need_resent(struct wcn_usb_rx_tx *rx_tx) { return !rx_tx->packet_status && wcn_usb_channel_is_rx(rx_tx->channel); } struct wcn_usb_copy_kthread { struct task_struct *wcn_usb_thread; spinlock_t lock; struct completion callback_complete; struct list_head rx_tx_head; struct mbuf_t *tx_mbuf_head; struct mbuf_t *tx_mbuf_tail; } copy_work[2]; static void wcn_usb_callback(struct wcn_usb_packet *packet) { struct wcn_usb_rx_tx *rx_tx; struct list_head *rx_tx_head; struct completion *callback_complete; spinlock_t *list_lock; rx_tx = wcn_usb_packet_get_pdata(packet); channel_debug_rx_tx_from_controller(rx_tx->channel, 1); rx_tx->packet_status = wcn_usb_packet_get_status(packet); if (wcn_usb_channel_is_copy(rx_tx->channel) && wcn_usb_channel_is_rx(rx_tx->channel)) { struct wcn_usb_copy_kthread *rx_copy; rx_copy = ©_work[1]; rx_tx_head = &rx_copy->rx_tx_head; callback_complete = &rx_copy->callback_complete; list_lock = &rx_copy->lock; } else { struct wcn_usb_work_data *work_data; work_data = wcn_usb_store_get_channel_info(rx_tx->channel); if (!work_data) { wcn_usb_err("%s channel[%d] work_data miss\n", __func__, rx_tx->channel); return; } rx_tx_head = &work_data->rx_tx_head; callback_complete = &work_data->callback_complete; list_lock = &work_data->lock; } spin_lock(list_lock); list_add_tail(&rx_tx->list, rx_tx_head); spin_unlock(list_lock); complete(callback_complete); #if 0 ret = schedule_work(&work_data->wcn_usb_work); #endif } static struct scatterlist *wcn_usb_mbuf2sgs(struct mbuf_t *head, struct mbuf_t *tail, int num, int align, unsigned int *total_len, int *sgs_num) { struct mbuf_t *mbuf; unsigned short mbuf_len; int i = 0, j = 0; int extra_sg = 0; struct scatterlist *sgs; mbuf_list_iter(head, num, mbuf, i) { if (mbuf->len < align) extra_sg++; } *sgs_num = extra_sg + num; sgs = wcn_usb_kzalloc((*sgs_num) * sizeof(struct scatterlist), GFP_KERNEL); if (!sgs) return NULL; sg_init_table(sgs, *sgs_num); mbuf_list_iter(head, num, mbuf, i) { mbuf_len = min_t(unsigned short, mbuf->len, align); sg_set_buf(sgs + j++, mbuf->buf, mbuf_len); *total_len += mbuf_len; if (mbuf_len < align) { sg_set_buf(sgs + j++, dirty_buff, align - mbuf_len); *total_len += align - mbuf_len; } } return sgs; } static int wcn_usb_sent_mbuf_sg_all(int chn, struct mbuf_t *head, struct mbuf_t *tail, int num) { struct wcn_usb_rx_tx *rx_tx; struct wcn_usb_ep *ep; int ret = 0; struct scatterlist *sgs; int sgs_num; unsigned int total_len = 0; int align; ep = wcn_usb_store_get_epFRchn(chn); if (!ep) return -ENODEV; rx_tx = wcn_usb_rx_tx_pool_zalloc(); channel_debug_rx_tx_alloc(chn, 1); if (!rx_tx) return -ENOMEM; ret = wcn_usb_packet_bind(rx_tx->packet, ep, GFP_KERNEL); if (ret) goto SG_FREE_RX_TX; align = alignment_comm(chn); sgs = wcn_usb_mbuf2sgs(head, tail, num, align, &total_len, &sgs_num); if (!sgs) { ret = -ENOMEM; goto SG_FREE_RX_TX; } rx_tx->channel = chn; rx_tx->head = head; rx_tx->tail = tail; rx_tx->num = num; ret = wcn_usb_packet_set_sg(rx_tx->packet, sgs, sgs_num, total_len); if (ret) goto SG_FREE_RX_TX; wcn_usb_print_rx_tx(rx_tx); channel_debug_rx_tx_to_controller(chn, 1); ret = wcn_usb_packet_submit(rx_tx->packet, wcn_usb_callback, rx_tx, GFP_KERNEL); if (!ret) return ret; wcn_usb_kfree(sgs); SG_FREE_RX_TX: wcn_usb_rx_tx_pool_free(rx_tx); channel_debug_rx_tx_free(chn, 1); return ret; } static int wcn_usb_mbuf_list_destroy(int chn, struct mbuf_t *head, struct mbuf_t *tail, int num); /** * This is a secondary function. * It first try to pop_link mbuf to user, * Or try to free mbuf list and buf of mbuf->buf */ static void wcn_usb_deal_partial_fail(int chn, struct mbuf_t *head, struct mbuf_t *tail, int num) { struct mchn_ops_t *mchn_ops; int ret; struct wcn_usb_work_data *work_data; work_data = wcn_usb_store_get_channel_info(chn); if (!work_data) return; mutex_lock(&work_data->channel_lock); mchn_ops = chn_ops(chn); if (!wcn_usb_channel_is_rx(chn) && mchn_ops && mchn_ops->pop_link) { channel_debug_mbuf_to_user(chn, num); ret = mchn_ops->pop_link(chn, head, tail, num); if (ret) wcn_usb_err("%s:%d channel[%d] pop_link error[%d]\n", __func__, __LINE__, chn, ret); } else { if (!wcn_usb_channel_is_rx(chn)) wcn_usb_err("%s %d pop_link mis\n", __func__, __LINE__); wcn_usb_mbuf_list_destroy(chn, head, tail, num); } mutex_unlock(&work_data->channel_lock); } /** * C is difficult to deal with "partial failure" * so this function and `wcn_usb_sent_mbuf` function * only return 0: successs or other_value: "total failure" * if we at partial failure, We will try to fix it by self, * AND RETURN 0 */ static int wcn_usb_sent_mbuf_sg_dispack(int chn, struct mbuf_t *head, struct mbuf_t *tail, int num, int unit_size) { struct mbuf_t *mbuf; struct mbuf_t *temp_head, *temp_tail; int ret; int i; int temp_num; temp_head = head; for (; num > 0; num -= unit_size) { temp_num = min(num, unit_size); /* find last */ mbuf_list_iter(temp_head, temp_num - 1, mbuf, i); if (mbuf == NULL) { wcn_usb_err("%s error tail\n", __func__); wcn_usb_deal_partial_fail(chn, temp_head, tail, num); } temp_tail = mbuf; mbuf = mbuf->next; /* keep next head */ temp_tail->next = NULL; /* cut list */ ret = wcn_usb_sent_mbuf_sg_all(chn, temp_head, temp_tail, temp_num); if (ret) { wcn_usb_err("%s submit is error %d\n", __func__, ret); wcn_usb_deal_partial_fail(chn, temp_head, tail, num); } temp_head = mbuf; } return 0; } static inline struct mbuf_t *wcn_usb_mbuf_stack_pop(struct mbuf_t **head) { struct mbuf_t *mbuf; if (*head == NULL) return NULL; mbuf = *head; *head = (*head)->next; return mbuf; } static inline void wcn_usb_mbuf_stack_push(struct mbuf_t **head, struct mbuf_t *mbuf) { mbuf->next = *head; *head = mbuf; } static int wcn_usb_sent_mbuf(int chn, struct mbuf_t *head, struct mbuf_t *tail, int num) { struct wcn_usb_rx_tx *rx_tx; struct wcn_usb_ep *ep; struct mbuf_t *mbuf; int ret; ep = wcn_usb_store_get_epFRchn(chn); if (!ep) return -ENODEV; /* we need carefull in this loop */ /* this mbuf and this rx_tx is not belong to us when we submit it */ /* so we cant do mbuf = mbuf->next after we submit it!!!*/ while ((mbuf = wcn_usb_mbuf_stack_pop(&head)) != NULL && num-- >= 0) { rx_tx = wcn_usb_rx_tx_pool_zalloc(); if (!rx_tx) goto FREE_RX_TX; channel_debug_rx_tx_alloc(chn, 1); ret = wcn_usb_packet_bind(rx_tx->packet, ep, GFP_KERNEL); if (ret) goto FREE_RX_TX; rx_tx->head = mbuf; rx_tx->tail = mbuf; rx_tx->num = 1; rx_tx->channel = chn; ret = wcn_usb_packet_set_buf(rx_tx->packet, mbuf->buf, mbuf->len, GFP_ATOMIC); if (ret) goto FREE_RX_TX; wcn_usb_print_rx_tx(rx_tx); channel_debug_rx_tx_to_controller(chn, 1); ret = wcn_usb_packet_submit(rx_tx->packet, wcn_usb_callback, rx_tx, GFP_ATOMIC); if (ret) { /* if submit is failed, rx_tx_temp is nobody belonged, * so we collect back it */ wcn_usb_mbuf_stack_push(&head, mbuf); wcn_usb_err("%s channel[%d] submit error[%d]\n", __func__, chn, ret); goto FREE_RX_TX; } } return 0; FREE_RX_TX: channel_debug_rx_tx_free(chn, 1); wcn_usb_rx_tx_pool_free(rx_tx); wcn_usb_err("%s channel[%d] not sent all list, remain mbuf[%d],ret:%d\n", __func__, chn, num + 1, ret); wcn_usb_deal_partial_fail(chn, mbuf, tail, num + 1); return 0; } struct wcn_ctrl { struct usb_ctrlrequest dr; struct mbuf_t *head; }; void ctrl_callback(struct urb *urb) { struct mchn_ops_t *mchn_ops; struct wcn_ctrl *wcn_c = (struct wcn_ctrl *)(urb->context); mchn_ops = chn_ops(0); if (urb->status || !mchn_ops || !mchn_ops->pop_link) { wcn_usb_err("%s ctrl channel is error\n", __func__); goto CTRL_OUT; } channel_debug_mbuf_to_user(0, 1); mchn_ops->pop_link(0, wcn_c->head, wcn_c->head, 1); CTRL_OUT: usb_free_urb(urb); wcn_usb_kfree(wcn_c); } struct { struct list_head big_men_head; spinlock_t list_lock; } big_men_head; struct wcn_usb_big_men { struct list_head list; char buf[0]; }; #define LOCK_FREE_BIG_BUF(at) \ __atomic_add_unless(at, 1, 1) static void *wcn_usb_get_big_men(int chn) { void *buf = NULL; struct wcn_usb_big_men *big_men; spin_lock_irq(&big_men_head.list_lock); big_men = list_first_entry_or_null(&big_men_head.big_men_head, struct wcn_usb_big_men, list); if (!big_men) { buf = NULL; wcn_usb_err("%s no big buf!!\n", __func__); } else { buf = big_men->buf; list_del(&big_men->list); channel_debug_alloc_big_men(chn); } spin_unlock_irq(&big_men_head.list_lock); return buf; } static void wcn_usb_put_big_men(void *buf, int chn) { struct wcn_usb_big_men *big_men = container_of(buf, struct wcn_usb_big_men, buf); spin_lock_irq(&big_men_head.list_lock); list_add(&big_men->list, &big_men_head.big_men_head); spin_unlock_irq(&big_men_head.list_lock); channel_debug_free_big_men(chn); } static int rx_copy_work_func(void *work) { struct wcn_usb_copy_kthread *copy_kthread; struct wcn_usb_rx_tx *rx_tx, *n; int recv_len, total_len; void *buf; struct mbuf_t *mbuf; int i; struct wcn_usb_work_data *work_data; struct list_head rx_tx_head; do { struct sched_param param; param.sched_priority = 1; sched_setscheduler(current, SCHED_RR, ¶m); } while (0); copy_kthread = (struct wcn_usb_copy_kthread *)work; GET_RX_TX_HEAD: reinit_completion(©_kthread->callback_complete); INIT_LIST_HEAD(&rx_tx_head); spin_lock_irq(©_kthread->lock); list_splice_init(©_kthread->rx_tx_head, &rx_tx_head); spin_unlock_irq(©_kthread->lock); list_for_each_entry_safe(rx_tx, n, &rx_tx_head, list) { total_len = recv_len = wcn_usb_packet_recv_len(rx_tx->packet); buf = wcn_usb_packet_pop_buf(rx_tx->packet); if (!rx_tx->packet_status) { mbuf_list_iter(rx_tx->head, rx_tx->num, mbuf, i) { if (recv_len <= 0) break; memcpy(mbuf->buf, buf + (total_len - recv_len), alignment_comm(rx_tx->channel)); recv_len -= alignment_comm(rx_tx->channel); } } wcn_usb_put_big_men(buf, rx_tx->channel); work_data = wcn_usb_store_get_channel_info(rx_tx->channel); if (work_data) { spin_lock_irq(&work_data->lock); list_move_tail(&rx_tx->list, &work_data->rx_tx_head); spin_unlock_irq(&work_data->lock); complete(&work_data->callback_complete); } else { wcn_usb_err("%s channel[%d] work_data miss\n", __func__, rx_tx->channel); WARN_ON(1); } } wait_for_completion_interruptible(©_kthread->callback_complete); goto GET_RX_TX_HEAD; return 0; } static int wcn_usb_sent_mbuf_copy(int chn, struct mbuf_t *head, struct mbuf_t *tail, int num) { struct wcn_usb_copy_kthread *copy_kthread; copy_kthread = ©_work[0]; spin_lock(©_kthread->lock); if (!copy_kthread->tx_mbuf_head) { copy_kthread->tx_mbuf_head = head; copy_kthread->tx_mbuf_tail = tail; } else { copy_kthread->tx_mbuf_tail->next = head; copy_kthread->tx_mbuf_tail = tail; } copy_kthread->tx_mbuf_tail->next = NULL; spin_unlock(©_kthread->lock); complete(©_kthread->callback_complete); return 0; } static void wcn_usb_callback_copy_tx(struct wcn_usb_packet *packet) { struct wcn_usb_rx_tx *rx_tx; rx_tx = wcn_usb_packet_get_pdata(packet); wcn_usb_put_big_men(wcn_usb_packet_pop_buf(packet), rx_tx->channel); return wcn_usb_callback(packet); } static int wcn_usb_copy_submit_rx_tx(struct wcn_usb_rx_tx *rx_tx, void *buf, int buf_size); static int tx_copy_work_func(void *work) { struct wcn_usb_copy_kthread *copy_kthread; struct wcn_usb_rx_tx *rx_tx = NULL; struct mbuf_t *mbuf; void *buf = NULL; int buf_size = 0; int ret; int mbuf_num; do { struct sched_param param; param.sched_priority = 1; sched_setscheduler(current, SCHED_RR, ¶m); } while (0); copy_kthread = (struct wcn_usb_copy_kthread *)work; GET_NEXT_MBUF: reinit_completion(©_kthread->callback_complete); spin_lock(©_kthread->lock); mbuf = wcn_usb_mbuf_stack_pop(©_kthread->tx_mbuf_head); spin_unlock(©_kthread->lock); if (!mbuf) { if (rx_tx) { ret = wcn_usb_copy_submit_rx_tx(rx_tx, buf, buf_size); if (ret) { wcn_usb_err("%s %d submit error %d\n", __func__, __LINE__, ret); wcn_usb_put_big_men(buf, rx_tx->channel); wcn_usb_rx_tx_pool_free(rx_tx); } rx_tx = NULL; buf = NULL; buf_size = 0; } wait_for_completion_interruptible(©_kthread->callback_complete); goto GET_NEXT_MBUF; } while (!rx_tx) { rx_tx = wcn_usb_rx_tx_pool_zalloc(); if (!rx_tx) { wcn_usb_err("%s %d no rx_tx memory\n", __func__, __LINE__); usleep_range(100, 300); } } rx_tx->channel = 6; while (!buf) { buf = wcn_usb_get_big_men(rx_tx->channel); if (!buf) msleep(200); } memcpy(buf + buf_size, mbuf->buf, mbuf->len); buf_size += alignment_comm(rx_tx->channel); if (!rx_tx->head) { rx_tx->head = rx_tx->tail = mbuf; } else { rx_tx->tail->next = mbuf; rx_tx->tail = mbuf; } rx_tx->tail->next = NULL; rx_tx->num += 1; /** * We need to avoid that send ZERO_LENGTH_PACKET(ZLP) * because sprd-musb can't send URB_ZERO_PACKET FLAG * and it will accidentally lose the packet that len == 0. * * Since mbuf->len == 1600 then it need send ZLP when * rx_tx->num == 8 or rx_tx->num == 16. (num * len % 512 == 0) * So we need to avoid rx_tx->num == 8 or rx_tx->num == 16. */ if (rx_tx->num == 7) { spin_lock(©_kthread->lock); mbuf_list_iter(copy_kthread->tx_mbuf_head, 2, mbuf, mbuf_num); spin_unlock(©_kthread->lock); } if (rx_tx->num >= 15 || (rx_tx->num == 7 && mbuf_num <= 1)) { ret = wcn_usb_copy_submit_rx_tx(rx_tx, buf, buf_size); if (ret) { wcn_usb_put_big_men(buf, rx_tx->channel); wcn_usb_err("%s %d submit error %d\n", __func__, __LINE__, ret); wcn_usb_rx_tx_pool_free(rx_tx); } rx_tx = NULL; buf = NULL; buf_size = 0; } goto GET_NEXT_MBUF; return 0; } static int wcn_usb_copy_submit_rx_tx(struct wcn_usb_rx_tx *rx_tx, void *buf, int buf_size) { struct wcn_usb_ep *ep; int ret; ep = wcn_usb_store_get_epFRchn(rx_tx->channel); if (!ep) return -ENODEV; ret = wcn_usb_packet_bind(rx_tx->packet, ep, GFP_KERNEL); if (ret) return ret; ret = wcn_usb_packet_set_buf(rx_tx->packet, buf, buf_size, GFP_ATOMIC); if (ret) return ret; ret = wcn_usb_packet_submit(rx_tx->packet, wcn_usb_callback_copy_tx, rx_tx, GFP_ATOMIC); if (ret) return ret; return 0; } void wcn_usb_init_copy_men(void) { int i; INIT_LIST_HEAD(&big_men_head.big_men_head); spin_lock_init(&big_men_head.list_lock); for (i = 0; i < WCN_USB_MEMCOPY; i++) { struct wcn_usb_big_men *big_men; big_men = wcn_usb_kzalloc(sizeof(struct wcn_usb_big_men) + TRANSF_ALIGNMENT_MAX * TRANSF_UNITS, GFP_KERNEL); list_add(&big_men->list, &big_men_head.big_men_head); } copy_work[0].wcn_usb_thread = kthread_create(tx_copy_work_func, ©_work[0], "wcn_usb_thread_tx"); copy_work[1].wcn_usb_thread = kthread_create(rx_copy_work_func, ©_work[1], "wcn_copy_rx"); for (i = 0; i < 2; i++) { spin_lock_init(©_work[i].lock); init_completion(©_work[i].callback_complete); INIT_LIST_HEAD(©_work[i].rx_tx_head); if (copy_work[i].wcn_usb_thread) wake_up_process(copy_work[i].wcn_usb_thread); } } static int wcn_usb_poll_copy_one(int chn) { struct wcn_usb_rx_tx *rx_tx; struct wcn_usb_ep *ep; int ret; void *buf; struct mbuf_t *mbuf; int i; ep = wcn_usb_store_get_epFRchn(chn); if (!ep) return -ENODEV; channel_debug_rx_tx_alloc(chn, 1); rx_tx = wcn_usb_rx_tx_pool_zalloc(); if (!rx_tx) return -ENOMEM; rx_tx->num = TRANSF_UNITS; ret = wcn_usb_list_alloc(chn, &rx_tx->head, &rx_tx->tail, &rx_tx->num); if (ret) goto FREE_RX_TX; rx_tx->channel = chn; if (rx_tx->num != TRANSF_UNITS) { ret = -ENOMEM; goto FREE_MBUF; } mbuf_list_iter(rx_tx->head, rx_tx->num, mbuf, i) { mbuf->len = alignment_comm(chn) + alignment_protocol_stack(chn); mbuf->buf = wcn_usb_alloc_buf(mbuf->len, GFP_KERNEL); if (!mbuf->buf) { ret = -ENOMEM; goto FREE_MBUF; } } ret = wcn_usb_packet_bind(rx_tx->packet, ep, GFP_KERNEL); if (ret) goto FREE_MBUF; buf = wcn_usb_get_big_men(chn); if (!buf) { ret = -ENOMEM; goto FREE_MBUF; } ret = wcn_usb_packet_set_buf(rx_tx->packet, buf, alignment_comm(chn) * rx_tx->num, GFP_ATOMIC); if (ret) goto FREE_BIG_MEN; channel_debug_rx_tx_to_controller(chn, 1); ret = wcn_usb_packet_submit(rx_tx->packet, wcn_usb_callback, rx_tx, GFP_ATOMIC); if (ret) goto FREE_BIG_MEN; return 0; FREE_BIG_MEN: wcn_usb_put_big_men(buf, chn); FREE_MBUF: wcn_usb_mbuf_list_destroy(chn, rx_tx->head, rx_tx->tail, rx_tx->num); FREE_RX_TX: channel_debug_rx_tx_free(chn, 1); wcn_usb_rx_tx_pool_free(rx_tx); return ret; } static int wcn_usb_poll_copy(int chn, int urbs) { int i; int ret = 0; for (i = 0; i < urbs; i++) { ret = wcn_usb_poll_copy_one(chn); if (ret) break; } if (i != 0) return urbs - i; else return ret; } inline int wcn_usb_push_list_tx(int chn, struct mbuf_t *head, struct mbuf_t *tail, int num) { struct wcn_usb_ep *ep; wcn_usb_print_mbuf(chn, head, num, __func__); if (chn == 0) ep = wcn_usb_store_get_epFRchn(23); else ep = wcn_usb_store_get_epFRchn(chn); if (!ep || !ep->intf) return -ENODEV; if (chn == 0) { struct urb *urb = usb_alloc_urb(0, GFP_KERNEL); struct wcn_ctrl *dr; unsigned int pipe; struct usb_device *udev; udev = ep->intf->udev; dr = wcn_usb_kzalloc(sizeof(struct wcn_ctrl), GFP_KERNEL); if (!dr) return -ENOMEM; dr->dr.bRequestType = 0x21; dr->dr.bRequest = 0; dr->dr.wIndex = 0; dr->dr.wValue = 0; dr->dr.wLength = __cpu_to_le16(head->len); dr->head = head; pipe = usb_sndctrlpipe(udev, 0x00); usb_fill_control_urb(urb, udev, pipe, (void *)(&(dr->dr)), head->buf, head->len, ctrl_callback, dr); return usb_submit_urb(urb, GFP_KERNEL); } if (wcn_usb_channel_is_sg(chn)) return wcn_usb_sent_mbuf_sg_dispack(chn, head, tail, num, TRANSF_UNITS); else if (wcn_usb_channel_is_copy(chn)) return wcn_usb_sent_mbuf_copy(chn, head, tail, num); else return wcn_usb_sent_mbuf(chn, head, tail, num); } static int wcn_usb_mbuf_list_destroy(int chn, struct mbuf_t *head, struct mbuf_t *tail, int num) { struct mbuf_t *mbuf; int i; mbuf_list_iter(head, num, mbuf, i) { wcn_usb_free_buf(mbuf->buf); mbuf->buf = NULL; mbuf->len = 0; } return wcn_usb_list_free(chn, head, tail, num); } inline int wcn_usb_push_list_rx(int chn, struct mbuf_t *head, struct mbuf_t *tail, int num) { wcn_usb_print_mbuf(chn, head, num, __func__); return wcn_usb_mbuf_list_destroy(chn, head, tail, num); } static int wcn_usb_poll_sg(int chn, int urbs) { struct mbuf_t *head, *tail; int num; int ret; struct mbuf_t *mbuf; int i, j; int err; /* TODO maybe we can call wcn_usb_sent_mbuf_sg_dispatch */ for (i = 0; i < urbs; i++) { num = TRANSF_UNITS; ret = wcn_usb_list_alloc(chn, &head, &tail, &num); if (ret || num != TRANSF_UNITS) { if (ret) ret = -ENOMEM; else ret = urbs - i; if (head != NULL) goto POLL_FREE_MBUF_SG; else return ret; } mbuf_list_iter(head, num, mbuf, j) { mbuf->len = alignment_comm(chn) + alignment_protocol_stack(chn); mbuf->buf = wcn_usb_alloc_buf(mbuf->len, GFP_ATOMIC); if (!mbuf->buf) goto POLL_FREE_MBUF_SG; } ret = wcn_usb_sent_mbuf_sg_all(chn, head, tail, num); if (ret) goto POLL_FREE_MBUF_SG; } return 0; POLL_FREE_MBUF_SG: err = wcn_usb_mbuf_list_destroy(chn, head, tail, num); if (err) wcn_usb_err("%s:%d channel[%d] list_destroy error[%d]\n", __func__, __LINE__, chn, err); wcn_usb_err("%s channel[%d] poll sg error, in urb[%d]\n", __func__, chn, i); if (i != 0) ret = urbs - i; return ret; } /* * if ret_value > 0 : ret_value = number not process yet (maybe no mbuf) * if ret_value < 0 : error * if ret_value = 0 : success all */ static int wcn_usb_poll(int chn, int urbs) { struct mbuf_t *head, *tail; int num; int ret; struct mbuf_t *mbuf; int i; int err; num = urbs; ret = wcn_usb_list_alloc(chn, &head, &tail, &num); if (ret) return -ENOMEM; mbuf_list_iter(head, num, mbuf, i) { mbuf->len = alignment_comm(chn) + alignment_protocol_stack(chn); mbuf->buf = wcn_usb_alloc_buf(mbuf->len, GFP_KERNEL); if (!mbuf->buf) { ret = -ENOMEM; goto POLL_FREE_MBUF_BUF; } } ret = wcn_usb_sent_mbuf(chn, head, tail, num); if (ret) goto POLL_FREE_MBUF_BUF; return urbs - num; POLL_FREE_MBUF_BUF: err = wcn_usb_mbuf_list_destroy(chn, head, tail, num); if (err) wcn_usb_err("%s:%d channel[%d] list_destroy error[%d]\n", __func__, __LINE__, chn, err); wcn_usb_err("%s channel[%d] poll error, in mbuf[%d]\n", __func__, chn, i); return ret; } static inline int wcn_usb_poll_rx(int chn, int urbs) { if (wcn_usb_channel_is_sg(chn)) return wcn_usb_poll_sg(chn, urbs); else if (wcn_usb_channel_is_copy(chn)) return wcn_usb_poll_copy(chn, urbs); else return wcn_usb_poll(chn, urbs); } void wcn_usb_begin_poll_rx(int chn) { struct wcn_usb_work_data *work_data; struct mchn_ops_t *mchn_ops; int ret; int urbs = 1; int total; if (wcn_usb_channel_is_apostle(chn)) return; work_data = wcn_usb_store_get_channel_info(chn); if (!work_data) { wcn_usb_err("%s channel[%d] work_data is miss\n", __func__, chn); return; } mutex_lock(&work_data->channel_lock); mchn_ops = chn_ops(chn); if (!mchn_ops) { mutex_unlock(&work_data->channel_lock); wcn_usb_err("%s channel[%d] chn_ops is miss\n", __func__, chn); return; } total = mchn_ops->pool_size; mutex_unlock(&work_data->channel_lock); if (wcn_usb_channel_is_sg(chn)) urbs = total / TRANSF_UNITS; else urbs = total; urbs = min(urbs, TRANSF_TOTAL); urbs = max(urbs, 1); ret = wcn_usb_poll_rx(chn, urbs); if (ret) wcn_usb_err("%s channel[%d] begin poll error[%d]\n", __func__, chn, ret); } void wcn_usb_mbuf_free_notif(int chn) { struct wcn_usb_work_data *work_data; work_data = wcn_usb_store_get_channel_info(chn); if (!work_data) { wcn_usb_err("%s channel[%d] work_data is miss\n", __func__, chn); return; } complete(&work_data->callback_complete); #if 0 wake_up(&work_data->wait_mbuf); #endif } void wcn_usb_wait_channel_stop(int chn) { struct wcn_usb_work_data *work_data; struct wcn_usb_ep *ep; long time; int i = 3; work_data = wcn_usb_store_get_channel_info(chn); if (!work_data) return; ep = wcn_usb_store_get_epFRchn(chn); if (!ep) return; STOP_AGAIN: work_data->goon = 0; wcn_usb_ep_stop(ep); #if 0 if (work_data->head) schedule_work(&work_data->wcn_usb_work); #endif time = wait_event_timeout(work_data->work_completion, buf_list_is_full(chn), WCN_USB_TIMEOUT); if (!time) { wcn_usb_err("%s chn:%d wait work_completion timeout!\n", __func__, chn); if (--i <= 0 && !wcn_usb_state_get(error_happen)) return; } if (!buf_list_is_full(chn) && !wcn_usb_state_get(error_happen)) goto STOP_AGAIN; } static int wcn_usb_rx_tx_parse(struct wcn_usb_rx_tx *rx_tx, struct mbuf_t **head, struct mbuf_t **tail, int *num) { /* struct wcn_usb_ep *ep; */ struct mbuf_t *mbuf; int i = 0; int recv_len, total_len; int ret; int recv_mbuf_num; *head = NULL; *tail = NULL; *num = 0; /* split list */ rx_tx->tail->next = NULL; if (wcn_usb_channel_is_rx(rx_tx->channel) && !rx_tx->packet_status) { total_len = recv_len = wcn_usb_packet_recv_len(rx_tx->packet); if (!recv_len) { ret = -ENOENT; goto OUT; } if (wcn_usb_channel_is_copy(rx_tx->channel) || wcn_usb_channel_is_sg(rx_tx->channel)) { recv_mbuf_num = DIV_ROUND_UP(recv_len, alignment_comm(rx_tx->channel)); mbuf_list_iter(rx_tx->head, recv_mbuf_num - 1, mbuf, i); } else { recv_mbuf_num = 1; mbuf = rx_tx->head; mbuf->len = recv_len; } if (!mbuf) { wcn_usb_err("%s chn[%d] recv_len longer than we give\n", __func__, rx_tx->channel); } else { *head = rx_tx->head; *tail = mbuf; *num = i + 1; rx_tx->head = mbuf->next; mbuf->next = NULL; rx_tx->num -= *num; if (!rx_tx->num) rx_tx->tail = NULL; } switch (*num) { case 16: case 15: case 14: case 13: case 12: case 11: case 10: channel_debug_mbuf_10(*num); break; case 9: case 8: channel_debug_mbuf_8(*num); break; case 7: case 6: case 5: case 4: channel_debug_mbuf_4(*num); break; case 3: case 2: case 1: channel_debug_mbuf_1(*num); break; default: break; } } else if (!wcn_usb_channel_is_rx(rx_tx->channel)) { *head = rx_tx->head; *tail = rx_tx->tail; *num = rx_tx->num; rx_tx->head = NULL; rx_tx->tail = NULL; rx_tx->num = 0; } ret = rx_tx->packet_status; OUT: if (rx_tx->num) { channel_debug_packet_no_full(rx_tx->num); if (wcn_usb_mbuf_list_destroy(rx_tx->channel, rx_tx->head, rx_tx->tail, rx_tx->num)) wcn_usb_err("%s chn[%d] list_destroy error%d\n", __func__, rx_tx->channel, ret); } return ret; } static void wcn_usb_rx_tx_list_parse(struct list_head *rx_tx_head, int *rx_tx_num, int *rx_tx_resent, struct mbuf_t **head, struct mbuf_t **tail, int *num) { struct wcn_usb_rx_tx *rx_tx, *n; struct mbuf_t *temp_head, *temp_tail; int temp_num; int ret; *head = NULL; *tail = NULL; *num = 0; *rx_tx_num = 0; *rx_tx_resent = 0; list_for_each_entry_safe(rx_tx, n, rx_tx_head, list) { wcn_usb_print_rx_tx(rx_tx); (*rx_tx_num)++; ret = wcn_usb_rx_tx_parse(rx_tx, &temp_head, &temp_tail, &temp_num); if (ret) { /* There fix rx urb only received a zero len packer, * usb can't continue read data. */ if (ret == -ENOENT && wcn_usb_rx_tx_need_resent(rx_tx) && wcn_usb_channel_is_apostle(rx_tx->channel)) (*rx_tx_resent)++; else wcn_usb_err("%s channel[%d] rx_tx_parse error[%d]\n", __func__, rx_tx->channel, ret); } else if (wcn_usb_rx_tx_need_resent(rx_tx) && !wcn_usb_channel_is_apostle(rx_tx->channel)) (*rx_tx_resent)++; if (!*head) { *head = temp_head; *tail = temp_tail; *num = temp_num; } else if (temp_head) { (*tail)->next = temp_head; *tail = temp_tail; *num += temp_num; } } } static void wcn_usb_work_rx_tx_free(struct list_head *rx_tx_head) { struct wcn_usb_rx_tx *rx_tx, *n; list_for_each_entry_safe(rx_tx, n, rx_tx_head, list) { channel_debug_rx_tx_free(rx_tx->channel, 1); kfree(wcn_usb_packet_pop_sg(rx_tx->packet, NULL)); kfree(wcn_usb_packet_pop_setup_packet(rx_tx->packet)); wcn_usb_rx_tx_pool_free(rx_tx); } } static unsigned long long wcn_usb_rx_tx_cnt; unsigned long long wcn_usb_get_rx_tx_cnt(void) { return wcn_usb_rx_tx_cnt; } static void wcn_usb_rx_trash(int chn, int num); int wcn_usb_work_func(void *work) { struct wcn_usb_work_data *work_data; struct list_head rx_tx_head; struct mbuf_t *head, *tail; struct mchn_ops_t *mchn_ops; int num = 0, rx_tx_num; int ret; #if 0 work_data = container_of(work, struct wcn_usb_work_data, wcn_usb_work); #endif work_data = (struct wcn_usb_work_data *)work; do { struct sched_param param; param.sched_priority = 1; sched_setscheduler(current, SCHED_RR, ¶m); } while (0); GET_RX_TX_HEAD: reinit_completion(&work_data->callback_complete); INIT_LIST_HEAD(&rx_tx_head); spin_lock_irq(&work_data->lock); list_splice_init(&work_data->rx_tx_head, &rx_tx_head); if (work_data->report_num >= work_data->report_num_last) work_data->transfer_remains += work_data->report_num - work_data->report_num_last; else work_data->transfer_remains += USHRT_MAX - work_data->report_num_last + work_data->report_num + 1; work_data->report_num_last = work_data->report_num; spin_unlock_irq(&work_data->lock); wcn_usb_rx_tx_list_parse(&rx_tx_head, &rx_tx_num, &ret, &head, &tail, &num); work_data->transfer_remains += ret; if (head == NULL) goto RX_TX_LIST_HANDLE; mutex_lock(&work_data->channel_lock); mchn_ops = chn_ops(work_data->channel); if (mchn_ops && mchn_ops->pop_link) { wcn_usb_print_mbuf(work_data->channel, head, num, __func__); channel_debug_mbuf_to_user(work_data->channel, num); ret = mchn_ops->pop_link(work_data->channel, head, tail, num); if (ret) wcn_usb_err("%s channel[%d] pop_link error[%d]\n", __func__, work_data->channel, ret); } else { wcn_usb_err("%s channel[%d] pop_link miss\n", __func__, work_data->channel); ret = wcn_usb_mbuf_list_destroy(work_data->channel, head, tail, num); if (ret) wcn_usb_err("%s channel[%d] list_destroy error[%d]\n", __func__, work_data->channel, ret); } mutex_unlock(&work_data->channel_lock); if (wcn_usb_channel_is_rx(work_data->channel)) wcn_usb_rx_tx_cnt += rx_tx_num; RX_TX_LIST_HANDLE: wcn_usb_work_rx_tx_free(&rx_tx_head); if (work_data->transfer_remains) { if (chn_ops(work_data->channel) && work_data->goon) { ret = wcn_usb_poll_rx(work_data->channel, work_data->transfer_remains); if (ret < 0) { //wcn_usb_err("%s chn[%d] poll rx error[%d]\n", // __func__, work_data->channel, ret); /* * if ret == -ENOMEN then we can wait it, * if ret != -ENOMEN that mean that is a * serous error, then we drop all info */ if (ret != -ENOMEM) work_data->transfer_remains = 0; } else { channel_debug_to_accept(work_data->channel, work_data->transfer_remains - ret); work_data->transfer_remains = ret; } } else { wcn_usb_rx_trash(work_data->channel, work_data->transfer_remains); work_data->transfer_remains = 0; } } wake_up(&work_data->work_completion); if (!work_data->transfer_remains) wait_for_completion_interruptible(&work_data->callback_complete); else msleep(100); goto GET_RX_TX_HEAD; return 0; } void wcn_usb_work_data_init(struct wcn_usb_work_data *work_data, int id) { work_data->channel = id; mutex_init(&work_data->channel_lock); spin_lock_init(&work_data->lock); init_waitqueue_head(&work_data->wait_mbuf); work_data->wcn_usb_thread = kthread_create(wcn_usb_work_func, work_data, "wcn_thread%d", id); init_waitqueue_head(&work_data->work_completion); init_completion(&work_data->callback_complete); INIT_LIST_HEAD(&work_data->rx_tx_head); if (work_data->wcn_usb_thread) wake_up_process(work_data->wcn_usb_thread); else wcn_usb_err("%s create a new thread failed\n", __func__); } struct wcn_usb_rx_apostle { struct wcn_usb_packet *packet; void *buf; int buf_size; int chn; }; int wcn_usb_apostle_fire(int chn, void (*fn)(struct wcn_usb_packet *packet)) { struct wcn_usb_rx_apostle *apostle; int ret = 0; struct wcn_usb_ep *ep; int i; wcn_usb_info("%s report num map is ", __func__); for (i = 0; i < ARRAY_SIZE(report_num_map_chn); i++) wcn_usb_info("%d ", report_num_map_chn[i]); wcn_usb_info("\n"); ep = wcn_usb_store_get_epFRchn(chn); if (!ep) return -ENODEV; apostle = wcn_usb_kzalloc(sizeof(struct wcn_usb_rx_apostle), GFP_ATOMIC); if (!apostle) return -ENOMEM; apostle->chn = chn; apostle->packet = wcn_usb_alloc_packet(GFP_ATOMIC); if (!apostle->packet) { ret = -ENOMEM; goto FREE_APOSTLE; } ret = wcn_usb_packet_bind(apostle->packet, ep, GFP_ATOMIC); if (ret) goto FREE_APOSTLE_PACKET; apostle->buf_size = alignment_comm(chn) + alignment_protocol_stack(chn); /* this code is unnecessary, if apostle chn is not sg mode */ if (wcn_usb_channel_is_sg(chn) || wcn_usb_channel_is_copy(chn)) apostle->buf_size = apostle->buf_size * TRANSF_UNITS + 1; apostle->buf = wcn_usb_kzalloc(apostle->buf_size, GFP_ATOMIC); if (!apostle->buf) { ret = -ENOMEM; goto FREE_APOSTLE_PACKET; } ret = wcn_usb_packet_set_buf(apostle->packet, apostle->buf, apostle->buf_size, GFP_ATOMIC); if (ret) goto FREE_APOSTLE_BUF; ret = wcn_usb_packet_submit(apostle->packet, fn, apostle, GFP_ATOMIC); if (ret) goto FREE_APOSTLE_BUF; return ret; FREE_APOSTLE_BUF: wcn_usb_kfree(apostle->buf); FREE_APOSTLE_PACKET: wcn_usb_packet_free(apostle->packet); FREE_APOSTLE: wcn_usb_kfree(apostle); return ret; } static void wcn_usb_rx_apostle_free(struct wcn_usb_rx_apostle *apostle) { wcn_usb_kfree(apostle->buf); wcn_usb_packet_free(apostle->packet); wcn_usb_kfree(apostle); } static void wcn_usb_rx_trash_callback(struct wcn_usb_packet *packet) { struct wcn_usb_rx_apostle *apostle; apostle = wcn_usb_packet_get_pdata(packet); wcn_usb_rx_apostle_free(apostle); } static void wcn_usb_rx_trash(int chn, int num) { int i; for (i = 0; i < num; i++) wcn_usb_apostle_fire(chn, wcn_usb_rx_trash_callback); } struct int_info { unsigned int count; unsigned short report_num[8]; }; #define loop_check_cmd "at+loopcheck\r" static void loop_check_callback(struct urb *urb) { kfree(urb->transfer_buffer); usb_free_urb(urb); } static void start_loop_check(void) { void *buf = kzalloc(strlen(loop_check_cmd) + 1, GFP_ATOMIC); struct urb *urb = usb_alloc_urb(0, GFP_ATOMIC); struct wcn_usb_ep *ep = wcn_usb_store_get_epFRchn(7); unsigned int pipe; struct usb_device *udev; struct usb_endpoint_descriptor *endpoint; if (!ep || !ep->intf || !ep->numEp) { wcn_usb_err("%s start loopcheck ep is error\n", __func__); return; } endpoint = wcn_usb_intf2endpoint(ep->intf, ep->numEp); if (!endpoint) { wcn_usb_err("%s start loopcheck endpoint is error\n", __func__); return; } udev = ep->intf->udev; pipe = usb_sndbulkpipe(udev, endpoint->bEndpointAddress); strncpy(buf, loop_check_cmd, strlen(loop_check_cmd)); usb_fill_bulk_urb(urb, udev, pipe, buf, strlen(loop_check_cmd) + 1, loop_check_callback, buf); if (usb_submit_urb(urb, GFP_KERNEL)) wcn_usb_err("%s start loopcheck error\n", __func__); } static void wcn_usb_rx_apostle_callback(struct wcn_usb_packet *packet) { struct wcn_usb_rx_apostle *apostle; int i; int total_len; int ret; struct wcn_usb_work_data *work_data; static unsigned int interrupt_count; char log_info[64]; struct int_info *apostle_info = NULL; channel_debug_interrupt_callback(1); apostle = wcn_usb_packet_get_pdata(packet); ret = wcn_usb_packet_get_status(packet); if (ret) { wcn_usb_info_ratelimited("%s get apostle error[%d]\n", __func__, ret); udelay(300); goto RESUBMIT_PACKET; } total_len = wcn_usb_packet_recv_len(packet); if (total_len != apostle->buf_size) { wcn_usb_err("%s apostle->buf_size[0x%x] total_len[0x%x]\n", __func__, apostle->buf_size, total_len); if (get_wcn_usb_print_switch() & packet_info) { snprintf(log_info, 32, "wcn usb_interrupt_msg %d ", interrupt_count); print_hex_dump(KERN_ERR, log_info, 0, apostle->buf_size, 1, apostle->buf, apostle->buf_size, 0); } start_loop_check(); goto RESUBMIT_PACKET; } interrupt_count++; if (get_wcn_usb_print_switch() & packet_info) { snprintf(log_info, 32, "wcn usb_interrupt_msg %d ", interrupt_count); print_hex_dump(KERN_ERR, log_info, 0, apostle->buf_size, 1, apostle->buf, apostle->buf_size, 0); } apostle_info = (struct int_info *)(apostle->buf); for (i = 0; i < ARRAY_SIZE(report_num_map_chn) - 1; i++) { int channel; channel = report_num_map_chn[i]; work_data = wcn_usb_store_get_channel_info(channel); if (!work_data) continue; spin_lock(&work_data->lock); work_data->report_num = apostle_info->report_num[i]; spin_unlock(&work_data->lock); channel_debug_report_num(channel, apostle_info->report_num[i]); complete(&work_data->callback_complete); } channel_debug_cp_num(apostle_info->count); RESUBMIT_PACKET: if (wcn_usb_state_get(error_happen)) { wcn_usb_rx_apostle_free(apostle); wcn_usb_err("%s assert is happen!!!\n", __func__); return; } if (apostle_info && apostle_info->report_num[ARRAY_SIZE( report_num_map_chn) - 1] > 0) { wcn_usb_info("%s recv sync 0x%x\n", __func__, apostle_info->report_num[ARRAY_SIZE( report_num_map_chn) - 1]); wcn_usb_state_sent_event(cp_ready); } else if (!wcn_usb_state_get(pwr_state) && wcn_usb_state_get(cp_ready)) { wcn_usb_rx_apostle_free(apostle); wcn_usb_err("%s power off!!!\n", __func__); return; } ret = wcn_usb_packet_submit(apostle->packet, wcn_usb_rx_apostle_callback, apostle, GFP_ATOMIC); if (ret) { wcn_usb_rx_apostle_free(apostle); wcn_usb_err("%s submit error %d\n", __func__, ret); } } int wcn_usb_apostle_begin(int chn) { return wcn_usb_apostle_fire(chn, wcn_usb_rx_apostle_callback); }