예제 #1
0
파일: usb.c 프로젝트: ReneNyffenegger/linux
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;
}
예제 #2
0
파일: usb.c 프로젝트: ReneNyffenegger/linux
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);
}
예제 #3
0
파일: usb.c 프로젝트: ReneNyffenegger/linux
/* 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;
}
예제 #4
0
파일: usb.c 프로젝트: ReneNyffenegger/linux
/* 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);
}
예제 #5
0
파일: usb.c 프로젝트: 513855417/linux
/* 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;
}