Esempio n. 1
0
static void get_htc_packet(struct htc_target *target,
			   struct htc_endpoint *ep,
			   struct list_head *queue, int resources)
{
	struct htc_packet *packet;

	/* NOTE : the TX lock is held when this function is called */

	/* loop until we can grab as many packets out of the queue as we can */
	while (resources) {
		if (list_empty(&ep->txq))
			break;

		packet = list_first_entry(&ep->txq, struct htc_packet, list);
		list_del(&packet->list);

		ath6kl_dbg(ATH6KL_DBG_HTC,
			   "%s: got packet:0x%p , new queue depth: %d\n",
			   __func__, packet, get_queue_depth(&ep->txq));
		packet->info.tx.seqno = ep->seqno;
		packet->info.tx.flags = 0;
		packet->info.tx.cred_used = 0;
		ep->seqno++;

		/* queue this packet into the caller's queue */
		list_add_tail(&packet->list, queue);
		resources--;
	}
}
Esempio n. 2
0
static void dump_cred_dist(struct htc_endpoint_credit_dist *ep_dist)
{
	ath6kl_dbg(ATH6KL_DBG_ANY,
		   "--- endpoint: %d  svc_id: 0x%X ---\n",
		   ep_dist->endpoint, ep_dist->svc_id);
	ath6kl_dbg(ATH6KL_DBG_ANY, " dist_flags     : 0x%X\n",
		   ep_dist->dist_flags);
	ath6kl_dbg(ATH6KL_DBG_ANY, " cred_norm      : %d\n",
		   ep_dist->cred_norm);
	ath6kl_dbg(ATH6KL_DBG_ANY, " cred_min       : %d\n",
		   ep_dist->cred_min);
	ath6kl_dbg(ATH6KL_DBG_ANY, " credits        : %d\n",
		   ep_dist->credits);
	ath6kl_dbg(ATH6KL_DBG_ANY, " cred_assngd    : %d\n",
		   ep_dist->cred_assngd);
	ath6kl_dbg(ATH6KL_DBG_ANY, " seek_cred      : %d\n",
		   ep_dist->seek_cred);
	ath6kl_dbg(ATH6KL_DBG_ANY, " cred_sz        : %d\n",
		   ep_dist->cred_sz);
	ath6kl_dbg(ATH6KL_DBG_ANY, " cred_per_msg   : %d\n",
		   ep_dist->cred_per_msg);
	ath6kl_dbg(ATH6KL_DBG_ANY, " cred_to_dist   : %d\n",
		   ep_dist->cred_to_dist);
	ath6kl_dbg(ATH6KL_DBG_ANY, " txq_depth      : %d\n",
		   get_queue_depth(&((struct htc_endpoint *)
				     ep_dist->htc_rsvd)->txq));
	ath6kl_dbg(ATH6KL_DBG_ANY,
		   "----------------------------------\n");
}
Esempio n. 3
0
static void do_send_completion(struct htc_endpoint *ep,
			       struct list_head *queue_to_indicate)
{
	struct htc_packet *packet;

	if (list_empty(queue_to_indicate)) {
		/* nothing to indicate */
		return;
	}

	if (ep->ep_cb.tx_comp_multi != NULL) {
		ath6kl_dbg(ATH6KL_DBG_HTC,
			   "%s: calling ep %d, send complete multiple callback (%d pkts)\n",
			   __func__, ep->eid,
			   get_queue_depth(queue_to_indicate));
		/*
		 * a multiple send complete handler is being used,
		 * pass the queue to the handler
		 */
		ep->ep_cb.tx_comp_multi(ep->target, queue_to_indicate);
		/*
		 * all packets are now owned by the callback,
		 * reset queue to be safe
		 */
		INIT_LIST_HEAD(queue_to_indicate);
	} else {
		/* using legacy EpTxComplete */
		do {
			packet = list_first_entry(queue_to_indicate,
						  struct htc_packet, list);

			list_del(&packet->list);
			ath6kl_dbg(ATH6KL_DBG_HTC,
				   "%s: calling ep %d send complete callback on packet 0x%p\n",
				   __func__, ep->eid, packet);
			ep->ep_cb.tx_complete(ep->target, packet);
		} while (!list_empty(queue_to_indicate));
	}
}
Esempio n. 4
0
static void get_htc_packet_credit_based(struct htc_target *target,
					struct htc_endpoint *ep,
					struct list_head *queue)
{
	int credits_required;
	int remainder;
	u8 send_flags;
	struct htc_packet *packet;
	unsigned int transfer_len;

	/* NOTE : the TX lock is held when this function is called */

	/* loop until we can grab as many packets out of the queue as we can */
	while (true) {
		send_flags = 0;
		if (list_empty(&ep->txq))
			break;

		/* get packet at head, but don't remove it */
		packet = list_first_entry(&ep->txq, struct htc_packet, list);
		if (packet == NULL)
			break;

		ath6kl_dbg(ATH6KL_DBG_HTC,
			   "%s: got head packet:0x%p , queue depth: %d\n",
			   __func__, packet, get_queue_depth(&ep->txq));

		transfer_len = packet->act_len + HTC_HDR_LENGTH;

		if (transfer_len <= target->tgt_cred_sz) {
			credits_required = 1;
		} else {
			/* figure out how many credits this message requires */
			credits_required = transfer_len / target->tgt_cred_sz;
			remainder = transfer_len % target->tgt_cred_sz;

			if (remainder)
				credits_required++;
		}

		ath6kl_dbg(ATH6KL_DBG_HTC, "%s: creds required:%d got:%d\n",
			   __func__, credits_required, ep->cred_dist.credits);

		if (ep->eid == ENDPOINT_0) {
			/*
			 * endpoint 0 is special, it always has a credit and
			 * does not require credit based flow control
			 */
			credits_required = 0;

		} else {

			if (ep->cred_dist.credits < credits_required)
				break;

			ep->cred_dist.credits -= credits_required;
			ep->ep_st.cred_cosumd += credits_required;

			/* check if we need credits back from the target */
			if (ep->cred_dist.credits <
					ep->cred_dist.cred_per_msg) {
				/* tell the target we need credits ASAP! */
				send_flags |= HTC_FLAGS_NEED_CREDIT_UPDATE;
				ep->ep_st.cred_low_indicate += 1;
				ath6kl_dbg(ATH6KL_DBG_HTC,
					   "%s: host needs credits\n",
					   __func__);
			}
		}

		/* now we can fully dequeue */
		packet = list_first_entry(&ep->txq, struct htc_packet, list);

		list_del(&packet->list);
		/* save the number of credits this packet consumed */
		packet->info.tx.cred_used = credits_required;
		/* save send flags */
		packet->info.tx.flags = send_flags;
		packet->info.tx.seqno = ep->seqno;
		ep->seqno++;
		/* queue this packet into the caller's queue */
		list_add_tail(&packet->list, queue);
	}

}
Esempio n. 5
0
static enum htc_send_queue_result htc_try_send(struct htc_target *target,
					       struct htc_endpoint *ep,
					       struct list_head *txq)
{
	struct list_head send_queue;	/* temp queue to hold packets */
	struct htc_packet *packet, *tmp_pkt;
	struct ath6kl *ar = target->dev->ar;
	enum htc_send_full_action action;
	int tx_resources, overflow, txqueue_depth, i, good_pkts;
	u8 pipeid;

	ath6kl_dbg(ATH6KL_DBG_HTC, "%s: (queue:0x%p depth:%d)\n",
		   __func__, txq,
		   (txq == NULL) ? 0 : get_queue_depth(txq));

	/* init the local send queue */
	INIT_LIST_HEAD(&send_queue);

	/*
	 * txq equals to NULL means
	 * caller didn't provide a queue, just wants us to
	 * check queues and send
	 */
	if (txq != NULL) {
		if (list_empty(txq)) {
			/* empty queue */
			return HTC_SEND_QUEUE_DROP;
		}

		spin_lock_bh(&target->tx_lock);
		txqueue_depth = get_queue_depth(&ep->txq);
		spin_unlock_bh(&target->tx_lock);

		if (txqueue_depth >= ep->max_txq_depth) {
			/* we've already overflowed */
			overflow = get_queue_depth(txq);
		} else {
			/* get how much we will overflow by */
			overflow = txqueue_depth;
			overflow += get_queue_depth(txq);
			/* get how much we will overflow the TX queue by */
			overflow -= ep->max_txq_depth;
		}

		/* if overflow is negative or zero, we are okay */
		if (overflow > 0) {
			ath6kl_dbg(ATH6KL_DBG_HTC,
				   "%s: Endpoint %d, TX queue will overflow :%d, Tx Depth:%d, Max:%d\n",
				   __func__, ep->eid, overflow, txqueue_depth,
				   ep->max_txq_depth);
		}
		if ((overflow <= 0) ||
		    (ep->ep_cb.tx_full == NULL)) {
			/*
			 * all packets will fit or caller did not provide send
			 * full indication handler -- just move all of them
			 * to the local send_queue object
			 */
			list_splice_tail_init(txq, &send_queue);
		} else {
			good_pkts = get_queue_depth(txq) - overflow;
			if (good_pkts < 0) {
				WARN_ON_ONCE(1);
				return HTC_SEND_QUEUE_DROP;
			}

			/* we have overflowed, and a callback is provided */
			/* dequeue all non-overflow packets to the sendqueue */
			for (i = 0; i < good_pkts; i++) {
				/* pop off caller's queue */
				packet = list_first_entry(txq,
							  struct htc_packet,
							  list);
				list_del(&packet->list);
				/* insert into local queue */
				list_add_tail(&packet->list, &send_queue);
			}

			/*
			 * the caller's queue has all the packets that won't fit
			 * walk through the caller's queue and indicate each to
			 * the send full handler
			 */
			list_for_each_entry_safe(packet, tmp_pkt,
						 txq, list) {

				ath6kl_dbg(ATH6KL_DBG_HTC,
					   "%s: Indicat overflowed TX pkts: %p\n",
					   __func__, packet);
				action = ep->ep_cb.tx_full(ep->target, packet);
				if (action == HTC_SEND_FULL_DROP) {
					/* callback wants the packet dropped */
					ep->ep_st.tx_dropped += 1;

					/* leave this one in the caller's queue
					 * for cleanup */
				} else {
					/* callback wants to keep this packet,
					 * remove from caller's queue */
					list_del(&packet->list);
					/* put it in the send queue */
					list_add_tail(&packet->list,
						      &send_queue);
				}

			}

			if (list_empty(&send_queue)) {
				/* no packets made it in, caller will cleanup */
				return HTC_SEND_QUEUE_DROP;
			}
		}
	}
Esempio n. 6
0
static int htc_issue_packets(struct htc_target *target,
			     struct htc_endpoint *ep,
			     struct list_head *pkt_queue)
{
	int status = 0;
	u16 payload_len;
	struct sk_buff *skb;
	struct htc_frame_hdr *htc_hdr;
	struct htc_packet *packet;

	ath6kl_dbg(ATH6KL_DBG_HTC,
		   "%s: queue: 0x%p, pkts %d\n", __func__,
		   pkt_queue, get_queue_depth(pkt_queue));

	while (!list_empty(pkt_queue)) {
		packet = list_first_entry(pkt_queue, struct htc_packet, list);
		list_del(&packet->list);

		skb = packet->skb;
		if (!skb) {
			WARN_ON_ONCE(1);
			status = -EINVAL;
			break;
		}

		payload_len = packet->act_len;

		/* setup HTC frame header */
		htc_hdr = (struct htc_frame_hdr *) skb_push(skb,
							    sizeof(*htc_hdr));
		if (!htc_hdr) {
			WARN_ON_ONCE(1);
			status = -EINVAL;
			break;
		}

		packet->info.tx.flags |= HTC_FLAGS_TX_FIXUP_NETBUF;

		/* Endianess? */
		put_unaligned((u16) payload_len, &htc_hdr->payld_len);
		htc_hdr->flags = packet->info.tx.flags;
		htc_hdr->eid = (u8) packet->endpoint;
		htc_hdr->ctrl[0] = 0;
		htc_hdr->ctrl[1] = (u8) packet->info.tx.seqno;

		spin_lock_bh(&target->tx_lock);

		/* store in look up queue to match completions */
		list_add_tail(&packet->list, &ep->pipe.tx_lookup_queue);
		ep->ep_st.tx_issued += 1;
		spin_unlock_bh(&target->tx_lock);

		status = ath6kl_hif_pipe_send(target->dev->ar,
					      ep->pipe.pipeid_ul, NULL, skb);

		if (status != 0) {
			if (status != -ENOMEM) {
				/* TODO: if more than 1 endpoint maps to the
				 * same PipeID, it is possible to run out of
				 * resources in the HIF layer.
				 * Don't emit the error
				 */
				ath6kl_dbg(ATH6KL_DBG_HTC,
					   "%s: failed status:%d\n",
					   __func__, status);
			}
			spin_lock_bh(&target->tx_lock);
			list_del(&packet->list);

			/* reclaim credits */
			ep->cred_dist.credits += packet->info.tx.cred_used;
			spin_unlock_bh(&target->tx_lock);

			/* put it back into the callers queue */
			list_add(&packet->list, pkt_queue);
			break;
		}

	}

	if (status != 0) {
		while (!list_empty(pkt_queue)) {
			if (status != -ENOMEM) {
				ath6kl_dbg(ATH6KL_DBG_HTC,
					   "%s: failed pkt:0x%p status:%d\n",
					   __func__, packet, status);
			}

			packet = list_first_entry(pkt_queue,
						  struct htc_packet, list);
			list_del(&packet->list);
			packet->status = status;
			send_packet_completion(target, packet);
		}
	}

	return status;
}