Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8fd07007 authored by Rupesh Gujare's avatar Rupesh Gujare Committed by Greg Kroah-Hartman
Browse files

staging: ozwpan: High resolution timers



Current implementation assumes HZ = 1000 for calculating
all internal timer intervals, which creates problem on
platforms where HZ != 1000.

As well we need resolution of less than 10 mSec for heartbeat
calculation, this creates problem on some platforms where HZ is
configured as HZ = 100, or around, which restricts us to timer interval
of 10 mSec. This is particularly found on embedded devices.

This patch moves on to use high resolution timers to calculate
all timer intervals as it allows us to have very small resolution
of timer interval, removing dependency on HZ.

Signed-off-by: default avatarRupesh Gujare <rupesh.gujare@atmel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 70bcbc06
Loading
Loading
Loading
Loading
+46 −45
Original line number Diff line number Diff line
@@ -26,7 +26,6 @@
 */
#include <linux/platform_device.h>
#include <linux/usb.h>
#include <linux/jiffies.h>
#include <linux/slab.h>
#include <linux/export.h>
#include "linux/usb/hcd.h"
@@ -49,6 +48,9 @@
/* Get endpoint object from the containing link.
 */
#define ep_from_link(__e) container_of((__e), struct oz_endpoint, link)
/*EP0 timeout before ep0 request is again added to TX queue. (13*8 = 98mSec)
 */
#define EP0_TIMEOUT_COUNTER 13
/*------------------------------------------------------------------------------
 * Used to link urbs together and also store some status information for each
 * urb.
@@ -60,7 +62,7 @@ struct oz_urb_link {
	struct oz_port *port;
	u8 req_id;
	u8 ep_num;
	unsigned long submit_jiffies;
	unsigned submit_counter;
};

/* Holds state information about a USB endpoint.
@@ -69,7 +71,7 @@ struct oz_endpoint {
	struct list_head urb_list;	/* List of oz_urb_link items. */
	struct list_head link;		/* For isoc ep, links in to isoc
					   lists of oz_port. */
	unsigned long last_jiffies;
	struct timespec timestamp;
	int credit;
	int credit_ceiling;
	u8 ep_num;
@@ -187,6 +189,7 @@ static DEFINE_SPINLOCK(g_tasklet_lock);
static struct tasklet_struct g_urb_process_tasklet;
static struct tasklet_struct g_urb_cancel_tasklet;
static atomic_t g_pending_urbs = ATOMIC_INIT(0);
static atomic_t g_usb_frame_number = ATOMIC_INIT(0);
static const struct hc_driver g_oz_hc_drv = {
	.description =		g_hcd_name,
	.product_desc =		"Ozmo Devices WPAN",
@@ -344,7 +347,7 @@ static struct oz_urb_link *oz_uncancel_urb(struct oz_hcd *ozhcd, struct urb *urb
 * Context: softirq or process
 */
static void oz_complete_urb(struct usb_hcd *hcd, struct urb *urb,
		int status, unsigned long submit_jiffies)
		int status)
{
	struct oz_hcd *ozhcd = oz_hcd_private(hcd);
	unsigned long irq_state;
@@ -372,12 +375,7 @@ static void oz_complete_urb(struct usb_hcd *hcd, struct urb *urb,
	if (oz_forget_urb(urb)) {
		oz_dbg(ON, "ERROR Unknown URB %p\n", urb);
	} else {
		static unsigned long last_time;
		atomic_dec(&g_pending_urbs);
		oz_dbg(URB, "%lu: giveback_urb(%p,%x) %lu %lu pending:%d\n",
		       jiffies, urb, status, jiffies-submit_jiffies,
		       jiffies-last_time, atomic_read(&g_pending_urbs));
		last_time = jiffies;
		usb_hcd_giveback_urb(hcd, urb, status);
	}
	spin_lock(&g_tasklet_lock);
@@ -445,7 +443,7 @@ static void oz_complete_buffered_urb(struct oz_port *port,
	ep->buffered_units--;
	oz_dbg(ON, "Trying to give back buffered frame of size=%d\n",
	       available_space);
	oz_complete_urb(port->ozhcd->hcd, urb, 0, 0);
	oz_complete_urb(port->ozhcd->hcd, urb, 0);
}

/*------------------------------------------------------------------------------
@@ -464,7 +462,7 @@ static int oz_enqueue_ep_urb(struct oz_port *port, u8 ep_addr, int in_dir,
	urbl = oz_alloc_urb_link();
	if (!urbl)
		return -ENOMEM;
	urbl->submit_jiffies = jiffies;
	urbl->submit_counter = 0;
	urbl->urb = urb;
	urbl->req_id = req_id;
	urbl->ep_num = ep_addr;
@@ -478,7 +476,7 @@ static int oz_enqueue_ep_urb(struct oz_port *port, u8 ep_addr, int in_dir,
	if (urb->unlinked) {
		spin_unlock_bh(&port->ozhcd->hcd_lock);
		oz_dbg(ON, "urb %p unlinked so complete immediately\n", urb);
		oz_complete_urb(port->ozhcd->hcd, urb, 0, 0);
		oz_complete_urb(port->ozhcd->hcd, urb, 0);
		oz_free_urb_link(urbl);
		return 0;
	}
@@ -501,7 +499,7 @@ static int oz_enqueue_ep_urb(struct oz_port *port, u8 ep_addr, int in_dir,
	if (ep && port->hpd) {
		list_add_tail(&urbl->link, &ep->urb_list);
		if (!in_dir && ep_addr && (ep->credit < 0)) {
			ep->last_jiffies = jiffies;
			getrawmonotonic(&ep->timestamp);
			ep->credit = 0;
		}
	} else {
@@ -790,7 +788,7 @@ void oz_hcd_get_desc_cnf(void *hport, u8 req_id, int status, const u8 *desc,
		}
	}
	urb->actual_length = total_size;
	oz_complete_urb(port->ozhcd->hcd, urb, 0, 0);
	oz_complete_urb(port->ozhcd->hcd, urb, 0);
}
/*------------------------------------------------------------------------------
 * Context: softirq
@@ -852,7 +850,7 @@ static void oz_hcd_complete_set_config(struct oz_port *port, struct urb *urb,
	} else {
		rc = -ENOMEM;
	}
	oz_complete_urb(hcd, urb, rc, 0);
	oz_complete_urb(hcd, urb, rc);
}
/*------------------------------------------------------------------------------
 * Context: softirq
@@ -877,7 +875,7 @@ static void oz_hcd_complete_set_interface(struct oz_port *port, struct urb *urb,
	} else {
		rc = -ENOMEM;
	}
	oz_complete_urb(hcd, urb, rc, 0);
	oz_complete_urb(hcd, urb, rc);
}
/*------------------------------------------------------------------------------
 * Context: softirq
@@ -914,7 +912,7 @@ void oz_hcd_control_cnf(void *hport, u8 req_id, u8 rcode, const u8 *data,
				(u8)windex, (u8)wvalue);
			break;
		default:
			oz_complete_urb(hcd, urb, 0, 0);
			oz_complete_urb(hcd, urb, 0);
		}

	} else {
@@ -928,7 +926,7 @@ void oz_hcd_control_cnf(void *hport, u8 req_id, u8 rcode, const u8 *data,
			memcpy(urb->transfer_buffer, data, copy_len);
			urb->actual_length = copy_len;
		}
		oz_complete_urb(hcd, urb, 0, 0);
		oz_complete_urb(hcd, urb, 0);
	}
}
/*------------------------------------------------------------------------------
@@ -998,7 +996,7 @@ void oz_hcd_data_ind(void *hport, u8 endpoint, const u8 *data, int data_len)
				copy_len = urb->transfer_buffer_length;
			memcpy(urb->transfer_buffer, data, copy_len);
			urb->actual_length = copy_len;
			oz_complete_urb(port->ozhcd->hcd, urb, 0, 0);
			oz_complete_urb(port->ozhcd->hcd, urb, 0);
			return;
		} else {
			oz_dbg(ON, "buffering frame as URB is not available\n");
@@ -1017,7 +1015,7 @@ void oz_hcd_data_ind(void *hport, u8 endpoint, const u8 *data, int data_len)
 */
static inline int oz_usb_get_frame_number(void)
{
	return jiffies_to_msecs(get_jiffies_64());
	return atomic_inc_return(&g_usb_frame_number);
}
/*------------------------------------------------------------------------------
 * Context: softirq
@@ -1033,7 +1031,8 @@ int oz_hcd_heartbeat(void *hport)
	struct list_head *n;
	struct urb *urb;
	struct oz_endpoint *ep;
	unsigned long now = jiffies;
	struct timespec ts, delta;
	getrawmonotonic(&ts);
	INIT_LIST_HEAD(&xfr_list);
	/* Check the OUT isoc endpoints to see if any URB data can be sent.
	 */
@@ -1042,10 +1041,11 @@ int oz_hcd_heartbeat(void *hport)
		ep = ep_from_link(e);
		if (ep->credit < 0)
			continue;
		ep->credit += jiffies_to_msecs(now - ep->last_jiffies);
		delta = timespec_sub(ts, ep->timestamp);
		ep->credit += timespec_to_ns(&delta) / NSEC_PER_MSEC;
		if (ep->credit > ep->credit_ceiling)
			ep->credit = ep->credit_ceiling;
		ep->last_jiffies = now;
		ep->timestamp = ts;
		while (ep->credit && !list_empty(&ep->urb_list)) {
			urbl = list_first_entry(&ep->urb_list,
				struct oz_urb_link, link);
@@ -1053,6 +1053,8 @@ int oz_hcd_heartbeat(void *hport)
			if ((ep->credit + 1) < urb->number_of_packets)
				break;
			ep->credit -= urb->number_of_packets;
			if (ep->credit < 0)
				ep->credit = 0;
			list_move_tail(&urbl->link, &xfr_list);
		}
	}
@@ -1060,16 +1062,14 @@ int oz_hcd_heartbeat(void *hport)
	/* Send to PD and complete URBs.
	 */
	list_for_each_safe(e, n, &xfr_list) {
		unsigned long t;
		urbl = container_of(e, struct oz_urb_link, link);
		urb = urbl->urb;
		t = urbl->submit_jiffies;
		list_del_init(e);
		urb->error_count = 0;
		urb->start_frame = oz_usb_get_frame_number();
		oz_usb_send_isoc(port->hpd, urbl->ep_num, urb);
		oz_free_urb_link(urbl);
		oz_complete_urb(port->ozhcd->hcd, urb, 0, t);
		oz_complete_urb(port->ozhcd->hcd, urb, 0);
	}
	/* Check the IN isoc endpoints to see if any URBs can be completed.
	 */
@@ -1080,13 +1080,14 @@ int oz_hcd_heartbeat(void *hport)
			if (ep->buffered_units >= OZ_IN_BUFFERING_UNITS) {
				ep->flags &= ~OZ_F_EP_BUFFERING;
				ep->credit = 0;
				ep->last_jiffies = now;
				ep->timestamp = ts;
				ep->start_frame = 0;
			}
			continue;
		}
		ep->credit += jiffies_to_msecs(now - ep->last_jiffies);
		ep->last_jiffies = now;
		delta = timespec_sub(ts, ep->timestamp);
		ep->credit += timespec_to_ns(&delta) / NSEC_PER_MSEC;
		ep->timestamp = ts;
		while (!list_empty(&ep->urb_list)) {
			struct oz_urb_link *urbl =
				list_first_entry(&ep->urb_list,
@@ -1095,7 +1096,7 @@ int oz_hcd_heartbeat(void *hport)
			int len = 0;
			int copy_len;
			int i;
			if ((ep->credit + 1) < urb->number_of_packets)
			if (ep->credit  < urb->number_of_packets)
				break;
			if (ep->buffered_units < urb->number_of_packets)
				break;
@@ -1141,7 +1142,7 @@ int oz_hcd_heartbeat(void *hport)
		urb = urbl->urb;
		list_del_init(e);
		oz_free_urb_link(urbl);
		oz_complete_urb(port->ozhcd->hcd, urb, 0, 0);
		oz_complete_urb(port->ozhcd->hcd, urb, 0);
	}
	/* Check if there are any ep0 requests that have timed out.
	 * If so resent to PD.
@@ -1153,11 +1154,12 @@ int oz_hcd_heartbeat(void *hport)
		spin_lock_bh(&ozhcd->hcd_lock);
		list_for_each_safe(e, n, &ep->urb_list) {
			urbl = container_of(e, struct oz_urb_link, link);
			if (time_after(now, urbl->submit_jiffies+HZ/2)) {
				oz_dbg(ON, "%ld: Request 0x%p timeout\n",
				       now, urbl->urb);
				urbl->submit_jiffies = now;
			if (urbl->submit_counter > EP0_TIMEOUT_COUNTER) {
				oz_dbg(ON, "Request 0x%p timeout\n", urbl->urb);
				list_move_tail(e, &xfr_list);
				urbl->submit_counter = 0;
			} else {
				urbl->submit_counter++;
			}
		}
		if (!list_empty(&ep->urb_list))
@@ -1382,7 +1384,7 @@ static void oz_process_ep0_urb(struct oz_hcd *ozhcd, struct urb *urb,
	int port_ix = -1;
	struct oz_port *port = NULL;

	oz_dbg(URB, "%lu: %s: (%p)\n", jiffies, __func__, urb);
	oz_dbg(URB, "[%s]:(%p)\n", __func__, urb);
	port_ix = oz_get_port_from_addr(ozhcd, urb->dev->devnum);
	if (port_ix < 0) {
		rc = -EPIPE;
@@ -1505,7 +1507,7 @@ static void oz_process_ep0_urb(struct oz_hcd *ozhcd, struct urb *urb,
out:
	if (rc || complete) {
		oz_dbg(ON, "Completing request locally\n");
		oz_complete_urb(ozhcd->hcd, urb, rc, 0);
		oz_complete_urb(ozhcd->hcd, urb, rc);
	} else {
		oz_usb_request_heartbeat(port->hpd);
	}
@@ -1569,7 +1571,7 @@ static void oz_urb_process_tasklet(unsigned long unused)
		oz_free_urb_link(urbl);
		rc = oz_urb_process(ozhcd, urb);
		if (rc)
			oz_complete_urb(ozhcd->hcd, urb, rc, 0);
			oz_complete_urb(ozhcd->hcd, urb, rc);
		spin_lock_irqsave(&g_tasklet_lock, irq_state);
	}
	spin_unlock_irqrestore(&g_tasklet_lock, irq_state);
@@ -1638,7 +1640,7 @@ static void oz_urb_cancel(struct oz_port *port, u8 ep_num, struct urb *urb)
	if (urbl) {
		urb->actual_length = 0;
		oz_free_urb_link(urbl);
		oz_complete_urb(ozhcd->hcd, urb, -EPIPE, 0);
		oz_complete_urb(ozhcd->hcd, urb, -EPIPE);
	}
}
/*------------------------------------------------------------------------------
@@ -1678,7 +1680,7 @@ static void oz_hcd_clear_orphanage(struct oz_hcd *ozhcd, int status)
			urbl = list_first_entry(&ozhcd->orphanage,
				struct oz_urb_link, link);
			list_del(&urbl->link);
			oz_complete_urb(ozhcd->hcd, urbl->urb, status, 0);
			oz_complete_urb(ozhcd->hcd, urbl->urb, status);
			oz_free_urb_link(urbl);
		}
	}
@@ -1720,14 +1722,13 @@ static int oz_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
	struct oz_port *port;
	unsigned long irq_state;
	struct oz_urb_link *urbl;
	oz_dbg(URB, "%lu: %s: (%p)\n", jiffies, __func__, urb);
	oz_dbg(URB, "%s: (%p)\n",  __func__, urb);
	if (unlikely(ozhcd == NULL)) {
		oz_dbg(URB, "%lu: Refused urb(%p) not ozhcd\n", jiffies, urb);
		oz_dbg(URB, "Refused urb(%p) not ozhcd\n", urb);
		return -EPIPE;
	}
	if (unlikely(hcd->state != HC_STATE_RUNNING)) {
		oz_dbg(URB, "%lu: Refused urb(%p) not running\n",
		       jiffies, urb);
		oz_dbg(URB, "Refused urb(%p) not running\n", urb);
		return -EPIPE;
	}
	port_ix = oz_get_port_from_addr(ozhcd, urb->dev->devnum);
@@ -1795,7 +1796,7 @@ static int oz_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
	struct oz_urb_link *urbl = NULL;
	int rc;
	unsigned long irq_state;
	oz_dbg(URB, "%lu: %s: (%p)\n", jiffies, __func__, urb);
	oz_dbg(URB, "%s: (%p)\n",  __func__, urb);
	urbl = oz_alloc_urb_link();
	if (unlikely(urbl == NULL))
		return -ENOMEM;
+21 −10
Original line number Diff line number Diff line
@@ -176,6 +176,14 @@ struct oz_pd *oz_pd_alloc(const u8 *mac_addr)
		pd->last_sent_frame = &pd->tx_queue;
		spin_lock_init(&pd->stream_lock);
		INIT_LIST_HEAD(&pd->stream_list);
		tasklet_init(&pd->heartbeat_tasklet, oz_pd_heartbeat_handler,
							(unsigned long)pd);
		tasklet_init(&pd->timeout_tasklet, oz_pd_timeout_handler,
							(unsigned long)pd);
		hrtimer_init(&pd->heartbeat, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
		hrtimer_init(&pd->timeout, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
		pd->heartbeat.function = oz_pd_heartbeat_event;
		pd->timeout.function = oz_pd_timeout_event;
	}
	return pd;
}
@@ -189,6 +197,13 @@ void oz_pd_destroy(struct oz_pd *pd)
	struct oz_isoc_stream *st;
	struct oz_farewell *fwell;
	oz_pd_dbg(pd, ON, "Destroying PD\n");
	if (hrtimer_active(&pd->timeout))
		hrtimer_cancel(&pd->timeout);
	if (hrtimer_active(&pd->heartbeat))
		hrtimer_cancel(&pd->heartbeat);
	/*Disable timer tasklets*/
	tasklet_kill(&pd->heartbeat_tasklet);
	tasklet_kill(&pd->timeout_tasklet);
	/* Delete any streams.
	 */
	e = pd->stream_list.next;
@@ -287,8 +302,8 @@ void oz_pd_heartbeat(struct oz_pd *pd, u16 apps)
				more = 1;
		}
	}
	if (more)
		oz_pd_request_heartbeat(pd);
	if ((!more) && (hrtimer_active(&pd->heartbeat)))
		hrtimer_cancel(&pd->heartbeat);
	if (pd->mode & OZ_F_ISOC_ANYTIME) {
		int count = 8;
		while (count-- && (oz_send_isoc_frame(pd) >= 0))
@@ -315,7 +330,6 @@ void oz_pd_stop(struct oz_pd *pd)
	list_del(&pd->link);
	oz_polling_unlock_bh();
	oz_dbg(ON, "pd ref count = %d\n", atomic_read(&pd->ref_count));
	oz_timer_delete(pd, 0);
	oz_pd_put(pd);
}
/*------------------------------------------------------------------------------
@@ -330,21 +344,18 @@ int oz_pd_sleep(struct oz_pd *pd)
		oz_polling_unlock_bh();
		return 0;
	}
	if (pd->keep_alive_j && pd->session_id) {
	if (pd->keep_alive && pd->session_id)
		oz_pd_set_state(pd, OZ_PD_S_SLEEP);
		pd->pulse_time_j = jiffies + pd->keep_alive_j;
		oz_dbg(ON, "Sleep Now %lu until %lu\n",
		       jiffies, pd->pulse_time_j);
	} else {
	else
		do_stop = 1;
	}

	stop_apps = pd->total_apps;
	oz_polling_unlock_bh();
	if (do_stop) {
		oz_pd_stop(pd);
	} else {
		oz_services_stop(pd, stop_apps, 1);
		oz_timer_add(pd, OZ_TIMER_STOP, jiffies + pd->keep_alive_j, 1);
		oz_timer_add(pd, OZ_TIMER_STOP, pd->keep_alive);
	}
	return do_stop;
}
+11 −7
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@
#ifndef _OZPD_H_
#define _OZPD_H_

#include <linux/interrupt.h>
#include "ozeltbuf.h"

/* PD state
@@ -68,18 +69,16 @@ struct oz_pd {
	u8		isoc_sent;
	u32		last_rx_pkt_num;
	u32		last_tx_pkt_num;
	struct timespec last_rx_timestamp;
	u32		trigger_pkt_num;
	unsigned long	pulse_time_j;
	unsigned long	timeout_time_j;
	unsigned long	pulse_period_j;
	unsigned long	presleep_j;
	unsigned long	keep_alive_j;
	unsigned long	last_rx_time_j;
	unsigned long	pulse_time;
	unsigned long	pulse_period;
	unsigned long	presleep;
	unsigned long	keep_alive;
	struct oz_elt_buf elt_buff;
	void		*app_ctx[OZ_APPID_MAX];
	spinlock_t	app_lock[OZ_APPID_MAX];
	int		max_tx_size;
	u8		heartbeat_requested;
	u8		mode;
	u8		ms_per_isoc;
	unsigned	isoc_latency;
@@ -95,6 +94,11 @@ struct oz_pd {
	spinlock_t	stream_lock;
	struct list_head stream_list;
	struct net_device *net_dev;
	struct hrtimer  heartbeat;
	struct hrtimer  timeout;
	u8      timeout_type;
	struct tasklet_struct   heartbeat_tasklet;
	struct tasklet_struct   timeout_tasklet;
};

#define OZ_MAX_QUEUED_FRAMES	4
+89 −240

File changed.

Preview size limit exceeded, changes collapsed.

+10 −14
Original line number Diff line number Diff line
@@ -12,20 +12,11 @@

#define OZ_ALLOCATED_SPACE(__x)	(LL_RESERVED_SPACE(__x)+(__x)->needed_tailroom)

/* Converts millisecs to jiffies.
 */
#define oz_ms_to_jiffies(__x)	msecs_to_jiffies(__x)

/* Quantum milliseconds.
 */
#define OZ_QUANTUM_MS		8
/* Quantum jiffies
 */
#define OZ_QUANTUM_J		(oz_ms_to_jiffies(OZ_QUANTUM_MS))
/* Quantum in MS */
#define OZ_QUANTUM		8
/* Default timeouts.
 */
#define OZ_CONNECTION_TOUT_J	(2*HZ)
#define OZ_PRESLEEP_TOUT_J	(11*HZ)
#define OZ_PRESLEEP_TOUT	11

/* Maximun sizes of tx frames. */
#define OZ_MAX_TX_SIZE		1514
@@ -65,11 +56,16 @@ void oz_app_enable(int app_id, int enable);
struct oz_pd *oz_pd_find(const u8 *mac_addr);
void oz_binding_add(char *net_dev);
void oz_binding_remove(char *net_dev);
void oz_timer_add(struct oz_pd *pd, int type, unsigned long due_time,
		int remove);
void oz_timer_add(struct oz_pd *pd, int type, unsigned long due_time);
void oz_timer_delete(struct oz_pd *pd, int type);
void oz_pd_request_heartbeat(struct oz_pd *pd);
void oz_polling_lock_bh(void);
void oz_polling_unlock_bh(void);
void oz_pd_heartbeat_handler(unsigned long data);
void oz_pd_timeout_handler(unsigned long data);
enum hrtimer_restart oz_pd_heartbeat_event(struct hrtimer *timer);
enum hrtimer_restart oz_pd_timeout_event(struct hrtimer *timer);
int oz_get_pd_status_list(char *pd_list, int max_count);
int oz_get_binding_list(char *buf, int max_if);

#endif /* _OZPROTO_H */
Loading