static int mwifiex_usb_construct_send_urb(struct mwifiex_adapter *adapter, struct usb_tx_data_port *port, u8 ep, struct urb_context *context, struct sk_buff *skb_send) { struct usb_card_rec *card = adapter->card; int ret = -EINPROGRESS; struct urb *tx_urb; context->adapter = adapter; context->ep = ep; context->skb = skb_send; tx_urb = context->urb; if (ep == card->tx_cmd_ep && card->tx_cmd_ep_type == USB_ENDPOINT_XFER_INT) usb_fill_int_urb(tx_urb, card->udev, usb_sndintpipe(card->udev, ep), skb_send->data, skb_send->len, mwifiex_usb_tx_complete, (void *)context, card->tx_cmd_interval); else usb_fill_bulk_urb(tx_urb, card->udev, usb_sndbulkpipe(card->udev, ep), skb_send->data, skb_send->len, mwifiex_usb_tx_complete, (void *)context); tx_urb->transfer_flags |= URB_ZERO_PACKET; if (ep == card->tx_cmd_ep) atomic_inc(&card->tx_cmd_urb_pending); else atomic_inc(&port->tx_data_urb_pending); if (ep != card->tx_cmd_ep && atomic_read(&port->tx_data_urb_pending) == MWIFIEX_TX_DATA_URB) { port->block_status = true; adapter->data_sent = mwifiex_usb_data_sent(adapter); ret = -ENOSR; } if (usb_submit_urb(tx_urb, GFP_ATOMIC)) { mwifiex_dbg(adapter, ERROR, "%s: usb_submit_urb failed\n", __func__); if (ep == card->tx_cmd_ep) { atomic_dec(&card->tx_cmd_urb_pending); } else { atomic_dec(&port->tx_data_urb_pending); port->block_status = false; adapter->data_sent = false; if (port->tx_data_ix) port->tx_data_ix--; else port->tx_data_ix = MWIFIEX_TX_DATA_URB; } ret = -1; } return ret; }
static void mwifiex_usb_tx_aggr_tmo(struct timer_list *t) { struct urb_context *urb_cnxt = NULL; struct sk_buff *skb_send = NULL; struct tx_aggr_tmr_cnxt *timer_context = from_timer(timer_context, t, hold_timer); struct mwifiex_adapter *adapter = timer_context->adapter; struct usb_tx_data_port *port = timer_context->port; unsigned long flags; int err = 0; spin_lock_irqsave(&port->tx_aggr_lock, flags); err = mwifiex_usb_prepare_tx_aggr_skb(adapter, port, &skb_send); if (err) { mwifiex_dbg(adapter, ERROR, "prepare tx aggr skb failed, err=%d\n", err); goto unlock; } if (atomic_read(&port->tx_data_urb_pending) >= MWIFIEX_TX_DATA_URB) { port->block_status = true; adapter->data_sent = mwifiex_usb_data_sent(adapter); err = -1; goto done; } if (port->tx_data_ix >= MWIFIEX_TX_DATA_URB) port->tx_data_ix = 0; urb_cnxt = &port->tx_data_list[port->tx_data_ix++]; err = mwifiex_usb_construct_send_urb(adapter, port, port->tx_data_ep, urb_cnxt, skb_send); done: if (err == -1) mwifiex_write_data_complete(adapter, skb_send, 0, -1); unlock: spin_unlock_irqrestore(&port->tx_aggr_lock, flags); }
/* This function prepare data packet to be send under usb tx aggregation * protocol, check current usb aggregation status, link packet to aggrgation * list if possible, work flow as below: * (1) if only 1 packet available, add usb tx aggregation header and send. * (2) if packet is able to aggregated, link it to current aggregation list. * (3) if packet is not able to aggregated, aggregate and send exist packets * in aggrgation list. Then, link packet in the list if there is more * packet in transmit queue, otherwise try to transmit single packet. */ static int mwifiex_usb_aggr_tx_data(struct mwifiex_adapter *adapter, u8 ep, struct sk_buff *skb, struct mwifiex_tx_param *tx_param, struct usb_tx_data_port *port) { u8 *payload, pad; u16 align = adapter->bus_aggr.tx_aggr_align; struct sk_buff *skb_send = NULL; struct urb_context *context = NULL; struct txpd *local_tx_pd = (struct txpd *)((u8 *)skb->data + adapter->intf_hdr_len); u8 f_send_aggr_buf = 0; u8 f_send_cur_buf = 0; u8 f_precopy_cur_buf = 0; u8 f_postcopy_cur_buf = 0; u32 timeout; int ret; /* padding to ensure each packet alginment */ pad = (align - (skb->len & (align - 1))) % align; if (tx_param && tx_param->next_pkt_len) { /* next packet available in tx queue*/ if (port->tx_aggr.aggr_len + skb->len + pad > adapter->bus_aggr.tx_aggr_max_size) { f_send_aggr_buf = 1; f_postcopy_cur_buf = 1; } else { /* current packet could be aggregated*/ f_precopy_cur_buf = 1; if (port->tx_aggr.aggr_len + skb->len + pad + tx_param->next_pkt_len > adapter->bus_aggr.tx_aggr_max_size || port->tx_aggr.aggr_num + 2 > adapter->bus_aggr.tx_aggr_max_num) { /* next packet could not be aggregated * send current aggregation buffer */ f_send_aggr_buf = 1; } } } else { /* last packet in tx queue */ if (port->tx_aggr.aggr_num > 0) { /* pending packets in aggregation buffer*/ if (port->tx_aggr.aggr_len + skb->len + pad > adapter->bus_aggr.tx_aggr_max_size) { /* current packet not be able to aggregated, * send aggr buffer first, then send packet. */ f_send_cur_buf = 1; } else { /* last packet, Aggregation and send */ f_precopy_cur_buf = 1; } f_send_aggr_buf = 1; } else { /* no pending packets in aggregation buffer, * send current packet immediately */ f_send_cur_buf = 1; } } if (local_tx_pd->flags & MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET) { /* Send NULL packet immediately*/ if (f_precopy_cur_buf) { if (skb_queue_empty(&port->tx_aggr.aggr_list)) { f_precopy_cur_buf = 0; f_send_aggr_buf = 0; f_send_cur_buf = 1; } else { f_send_aggr_buf = 1; } } else if (f_postcopy_cur_buf) { f_send_cur_buf = 1; f_postcopy_cur_buf = 0; } } if (f_precopy_cur_buf) { skb_queue_tail(&port->tx_aggr.aggr_list, skb); port->tx_aggr.aggr_len += (skb->len + pad); port->tx_aggr.aggr_num++; if (f_send_aggr_buf) goto send_aggr_buf; /* packet will not been send immediately, * set a timer to make sure it will be sent under * strict time limit. Dynamically fit the timeout * value, according to packets number in aggr_list */ if (!port->tx_aggr.timer_cnxt.is_hold_timer_set) { port->tx_aggr.timer_cnxt.hold_tmo_msecs = MWIFIEX_USB_TX_AGGR_TMO_MIN; timeout = port->tx_aggr.timer_cnxt.hold_tmo_msecs; mod_timer(&port->tx_aggr.timer_cnxt.hold_timer, jiffies + msecs_to_jiffies(timeout)); port->tx_aggr.timer_cnxt.is_hold_timer_set = true; } else { if (port->tx_aggr.timer_cnxt.hold_tmo_msecs < MWIFIEX_USB_TX_AGGR_TMO_MAX) { /* Dyanmic fit timeout */ timeout = ++port->tx_aggr.timer_cnxt.hold_tmo_msecs; mod_timer(&port->tx_aggr.timer_cnxt.hold_timer, jiffies + msecs_to_jiffies(timeout)); } } } send_aggr_buf: if (f_send_aggr_buf) { ret = mwifiex_usb_prepare_tx_aggr_skb(adapter, port, &skb_send); if (!ret) { context = &port->tx_data_list[port->tx_data_ix++]; ret = mwifiex_usb_construct_send_urb(adapter, port, ep, context, skb_send); if (ret == -1) mwifiex_write_data_complete(adapter, skb_send, 0, -1); } } if (f_send_cur_buf) { if (f_send_aggr_buf) { if (atomic_read(&port->tx_data_urb_pending) >= MWIFIEX_TX_DATA_URB) { port->block_status = true; adapter->data_sent = mwifiex_usb_data_sent(adapter); /* no available urb, postcopy packet*/ f_postcopy_cur_buf = 1; goto postcopy_cur_buf; } if (port->tx_data_ix >= MWIFIEX_TX_DATA_URB) port->tx_data_ix = 0; } payload = skb->data; *(u16 *)&payload[2] = cpu_to_le16(MWIFIEX_TYPE_AGGR_DATA_V2 | 0x80); *(u16 *)payload = cpu_to_le16(skb->len); skb_send = skb; context = &port->tx_data_list[port->tx_data_ix++]; return mwifiex_usb_construct_send_urb(adapter, port, ep, context, skb_send); } postcopy_cur_buf: if (f_postcopy_cur_buf) { skb_queue_tail(&port->tx_aggr.aggr_list, skb); port->tx_aggr.aggr_len += (skb->len + pad); port->tx_aggr.aggr_num++; /* New aggregation begin, start timer */ if (!port->tx_aggr.timer_cnxt.is_hold_timer_set) { port->tx_aggr.timer_cnxt.hold_tmo_msecs = MWIFIEX_USB_TX_AGGR_TMO_MIN; timeout = port->tx_aggr.timer_cnxt.hold_tmo_msecs; mod_timer(&port->tx_aggr.timer_cnxt.hold_timer, jiffies + msecs_to_jiffies(timeout)); port->tx_aggr.timer_cnxt.is_hold_timer_set = true; } } return -EINPROGRESS; }
/* This function write a command/data packet to card. */ static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep, struct sk_buff *skb, struct mwifiex_tx_param *tx_param) { struct usb_card_rec *card = adapter->card; struct urb_context *context = NULL; struct usb_tx_data_port *port = NULL; unsigned long flags; int idx, ret; if (adapter->is_suspended) { mwifiex_dbg(adapter, ERROR, "%s: not allowed while suspended\n", __func__); return -1; } if (adapter->surprise_removed) { mwifiex_dbg(adapter, ERROR, "%s: device removed\n", __func__); return -1; } mwifiex_dbg(adapter, INFO, "%s: ep=%d\n", __func__, ep); if (ep == card->tx_cmd_ep) { context = &card->tx_cmd; } else { /* get the data port structure for endpoint */ for (idx = 0; idx < MWIFIEX_TX_DATA_PORT; idx++) { if (ep == card->port[idx].tx_data_ep) { port = &card->port[idx]; if (atomic_read(&port->tx_data_urb_pending) >= MWIFIEX_TX_DATA_URB) { port->block_status = true; adapter->data_sent = mwifiex_usb_data_sent(adapter); return -EBUSY; } if (port->tx_data_ix >= MWIFIEX_TX_DATA_URB) port->tx_data_ix = 0; break; } } if (!port) { mwifiex_dbg(adapter, ERROR, "Wrong usb tx data port\n"); return -1; } if (adapter->bus_aggr.enable) { spin_lock_irqsave(&port->tx_aggr_lock, flags); ret = mwifiex_usb_aggr_tx_data(adapter, ep, skb, tx_param, port); spin_unlock_irqrestore(&port->tx_aggr_lock, flags); return ret; } context = &port->tx_data_list[port->tx_data_ix++]; } return mwifiex_usb_construct_send_urb(adapter, port, ep, context, skb); }
/* This function write a command/data packet to card. */ static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep, struct sk_buff *skb, struct mwifiex_tx_param *tx_param) { struct usb_card_rec *card = adapter->card; struct urb_context *context = NULL; struct usb_tx_data_port *port = NULL; u8 *data = (u8 *)skb->data; struct urb *tx_urb; int idx, ret; if (adapter->is_suspended) { mwifiex_dbg(adapter, ERROR, "%s: not allowed while suspended\n", __func__); return -1; } if (adapter->surprise_removed) { mwifiex_dbg(adapter, ERROR, "%s: device removed\n", __func__); return -1; } mwifiex_dbg(adapter, INFO, "%s: ep=%d\n", __func__, ep); if (ep == card->tx_cmd_ep) { context = &card->tx_cmd; } else { for (idx = 0; idx < MWIFIEX_TX_DATA_PORT; idx++) { if (ep == card->port[idx].tx_data_ep) { port = &card->port[idx]; if (atomic_read(&port->tx_data_urb_pending) >= MWIFIEX_TX_DATA_URB) { port->block_status = true; ret = -EBUSY; goto done; } if (port->tx_data_ix >= MWIFIEX_TX_DATA_URB) port->tx_data_ix = 0; context = &port->tx_data_list[port->tx_data_ix++]; break; } } if (!port) { mwifiex_dbg(adapter, ERROR, "Wrong usb tx data port\n"); return -1; } } context->adapter = adapter; context->ep = ep; context->skb = skb; tx_urb = context->urb; usb_fill_bulk_urb(tx_urb, card->udev, usb_sndbulkpipe(card->udev, ep), data, skb->len, mwifiex_usb_tx_complete, (void *)context); tx_urb->transfer_flags |= URB_ZERO_PACKET; if (ep == card->tx_cmd_ep) atomic_inc(&card->tx_cmd_urb_pending); else atomic_inc(&port->tx_data_urb_pending); if (usb_submit_urb(tx_urb, GFP_ATOMIC)) { mwifiex_dbg(adapter, ERROR, "%s: usb_submit_urb failed\n", __func__); if (ep == card->tx_cmd_ep) { atomic_dec(&card->tx_cmd_urb_pending); } else { atomic_dec(&port->tx_data_urb_pending); port->block_status = false; if (port->tx_data_ix) port->tx_data_ix--; else port->tx_data_ix = MWIFIEX_TX_DATA_URB; } return -1; } else { if (ep != card->tx_cmd_ep && atomic_read(&port->tx_data_urb_pending) == MWIFIEX_TX_DATA_URB) { port->block_status = true; ret = -ENOSR; goto done; } } return -EINPROGRESS; done: if (ep != card->tx_cmd_ep) adapter->data_sent = mwifiex_usb_data_sent(adapter); return ret; }