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

Commit 1cdc5abf authored by David S. Miller's avatar David S. Miller
Browse files
parents e0f43752 0fb0a4f0
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -83,6 +83,21 @@
#define D_SUBMODULE control
#include "debug-levels.h"

static int i2400m_idle_mode_disabled;/* 0 (idle mode enabled) by default */
module_param_named(idle_mode_disabled, i2400m_idle_mode_disabled, int, 0644);
MODULE_PARM_DESC(idle_mode_disabled,
		 "If true, the device will not enable idle mode negotiation "
		 "with the base station (when connected) to save power.");

/* 0 (power saving enabled) by default */
static int i2400m_power_save_disabled;
module_param_named(power_save_disabled, i2400m_power_save_disabled, int, 0644);
MODULE_PARM_DESC(power_save_disabled,
		 "If true, the driver will not tell the device to enter "
		 "power saving mode when it reports it is ready for it. "
		 "False by default (so the device is told to do power "
		 "saving).");

int i2400m_passive_mode;	/* 0 (passive mode disabled) by default */
module_param_named(passive_mode, i2400m_passive_mode, int, 0644);
MODULE_PARM_DESC(passive_mode,
+128 −37
Original line number Diff line number Diff line
@@ -75,25 +75,6 @@
#include "debug-levels.h"


int i2400m_idle_mode_disabled;	/* 0 (idle mode enabled) by default */
module_param_named(idle_mode_disabled, i2400m_idle_mode_disabled, int, 0644);
MODULE_PARM_DESC(idle_mode_disabled,
		 "If true, the device will not enable idle mode negotiation "
		 "with the base station (when connected) to save power.");

int i2400m_rx_reorder_disabled;	/* 0 (rx reorder enabled) by default */
module_param_named(rx_reorder_disabled, i2400m_rx_reorder_disabled, int, 0644);
MODULE_PARM_DESC(rx_reorder_disabled,
		 "If true, RX reordering will be disabled.");

int i2400m_power_save_disabled;	/* 0 (power saving enabled) by default */
module_param_named(power_save_disabled, i2400m_power_save_disabled, int, 0644);
MODULE_PARM_DESC(power_save_disabled,
		 "If true, the driver will not tell the device to enter "
		 "power saving mode when it reports it is ready for it. "
		 "False by default (so the device is told to do power "
		 "saving).");

static char i2400m_debug_params[128];
module_param_string(debug, i2400m_debug_params, sizeof(i2400m_debug_params),
		    0644);
@@ -395,6 +376,16 @@ int __i2400m_dev_start(struct i2400m *i2400m, enum i2400m_bri flags)
	result = i2400m_dev_initialize(i2400m);
	if (result < 0)
		goto error_dev_initialize;

	/* We don't want any additional unwanted error recovery triggered
	 * from any other context so if anything went wrong before we come
	 * here, let's keep i2400m->error_recovery untouched and leave it to
	 * dev_reset_handle(). See dev_reset_handle(). */

	atomic_dec(&i2400m->error_recovery);
	/* Every thing works so far, ok, now we are ready to
	 * take error recovery if it's required. */

	/* At this point, reports will come for the device and set it
	 * to the right state if it is different than UNINITIALIZED */
	d_fnend(3, dev, "(net_dev %p [i2400m %p]) = %d\n",
@@ -403,10 +394,10 @@ int __i2400m_dev_start(struct i2400m *i2400m, enum i2400m_bri flags)

error_dev_initialize:
error_check_mac_addr:
error_fw_check:
	i2400m->ready = 0;
	wmb();		/* see i2400m->ready's documentation  */
	flush_workqueue(i2400m->work_queue);
error_fw_check:
	if (i2400m->bus_dev_stop)
		i2400m->bus_dev_stop(i2400m);
error_bus_dev_start:
@@ -436,7 +427,8 @@ int i2400m_dev_start(struct i2400m *i2400m, enum i2400m_bri bm_flags)
		result = __i2400m_dev_start(i2400m, bm_flags);
		if (result >= 0) {
			i2400m->updown = 1;
			wmb();	/* see i2400m->updown's documentation */
			i2400m->alive = 1;
			wmb();/* see i2400m->updown and i2400m->alive's doc */
		}
	}
	mutex_unlock(&i2400m->init_mutex);
@@ -497,7 +489,8 @@ void i2400m_dev_stop(struct i2400m *i2400m)
	if (i2400m->updown) {
		__i2400m_dev_stop(i2400m);
		i2400m->updown = 0;
		wmb();	/* see i2400m->updown's documentation  */
		i2400m->alive = 0;
		wmb();	/* see i2400m->updown and i2400m->alive's doc */
	}
	mutex_unlock(&i2400m->init_mutex);
}
@@ -617,12 +610,12 @@ int i2400m_post_reset(struct i2400m *i2400m)
error_dev_start:
	if (i2400m->bus_release)
		i2400m->bus_release(i2400m);
error_bus_setup:
	/* even if the device was up, it could not be recovered, so we
	 * mark it as down. */
	i2400m->updown = 0;
	wmb();		/* see i2400m->updown's documentation  */
	mutex_unlock(&i2400m->init_mutex);
error_bus_setup:
	d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result);
	return result;
}
@@ -669,6 +662,9 @@ void __i2400m_dev_reset_handle(struct work_struct *ws)

	d_fnstart(3, dev, "(ws %p i2400m %p reason %s)\n", ws, i2400m, reason);

	i2400m->boot_mode = 1;
	wmb();		/* Make sure i2400m_msg_to_dev() sees boot_mode */

	result = 0;
	if (mutex_trylock(&i2400m->init_mutex) == 0) {
		/* We are still in i2400m_dev_start() [let it fail] or
@@ -679,32 +675,62 @@ void __i2400m_dev_reset_handle(struct work_struct *ws)
		complete(&i2400m->msg_completion);
		goto out;
	}
	if (i2400m->updown == 0)  {
		dev_info(dev, "%s: device is down, doing nothing\n", reason);
		goto out_unlock;
	}

	dev_err(dev, "%s: reinitializing driver\n", reason);
	rmb();
	if (i2400m->updown) {
		__i2400m_dev_stop(i2400m);
		i2400m->updown = 0;
		wmb();		/* see i2400m->updown's documentation  */
	}

	if (i2400m->alive) {
		result = __i2400m_dev_start(i2400m,
				    I2400M_BRI_SOFT | I2400M_BRI_MAC_REINIT);
		if (result < 0) {
		i2400m->updown = 0;
		wmb();		/* see i2400m->updown's documentation  */
			dev_err(dev, "%s: cannot start the device: %d\n",
				reason, result);
			result = -EUCLEAN;
			if (atomic_read(&i2400m->bus_reset_retries)
					>= I2400M_BUS_RESET_RETRIES) {
				result = -ENODEV;
				dev_err(dev, "tried too many times to "
					"reset the device, giving up\n");
			}
out_unlock:
		}
	}

	if (i2400m->reset_ctx) {
		ctx->result = result;
		complete(&ctx->completion);
	}
	mutex_unlock(&i2400m->init_mutex);
	if (result == -EUCLEAN) {
		/*
		 * We come here because the reset during operational mode
		 * wasn't successully done and need to proceed to a bus
		 * reset. For the dev_reset_handle() to be able to handle
		 * the reset event later properly, we restore boot_mode back
		 * to the state before previous reset. ie: just like we are
		 * issuing the bus reset for the first time
		 */
		i2400m->boot_mode = 0;
		wmb();

		atomic_inc(&i2400m->bus_reset_retries);
		/* ops, need to clean up [w/ init_mutex not held] */
		result = i2400m_reset(i2400m, I2400M_RT_BUS);
		if (result >= 0)
			result = -ENODEV;
	} else {
		rmb();
		if (i2400m->alive) {
			/* great, we expect the device state up and
			 * dev_start() actually brings the device state up */
			i2400m->updown = 1;
			wmb();
			atomic_set(&i2400m->bus_reset_retries, 0);
		}
	}
out:
	i2400m_put(i2400m);
@@ -728,14 +754,72 @@ void __i2400m_dev_reset_handle(struct work_struct *ws)
 */
int i2400m_dev_reset_handle(struct i2400m *i2400m, const char *reason)
{
	i2400m->boot_mode = 1;
	wmb();		/* Make sure i2400m_msg_to_dev() sees boot_mode */
	return i2400m_schedule_work(i2400m, __i2400m_dev_reset_handle,
				    GFP_ATOMIC, &reason, sizeof(reason));
}
EXPORT_SYMBOL_GPL(i2400m_dev_reset_handle);


 /*
 * The actual work of error recovery.
 *
 * The current implementation of error recovery is to trigger a bus reset.
 */
static
void __i2400m_error_recovery(struct work_struct *ws)
{
	struct i2400m_work *iw = container_of(ws, struct i2400m_work, ws);
	struct i2400m *i2400m = iw->i2400m;

	i2400m_reset(i2400m, I2400M_RT_BUS);

	i2400m_put(i2400m);
	kfree(iw);
	return;
}

/*
 * Schedule a work struct for error recovery.
 *
 * The intention of error recovery is to bring back the device to some
 * known state whenever TX sees -110 (-ETIMEOUT) on copying the data to
 * the device. The TX failure could mean a device bus stuck, so the current
 * error recovery implementation is to trigger a bus reset to the device
 * and hopefully it can bring back the device.
 *
 * The actual work of error recovery has to be in a thread context because
 * it is kicked off in the TX thread (i2400ms->tx_workqueue) which is to be
 * destroyed by the error recovery mechanism (currently a bus reset).
 *
 * Also, there may be already a queue of TX works that all hit
 * the -ETIMEOUT error condition because the device is stuck already.
 * Since bus reset is used as the error recovery mechanism and we don't
 * want consecutive bus resets simply because the multiple TX works
 * in the queue all hit the same device erratum, the flag "error_recovery"
 * is introduced for preventing unwanted consecutive bus resets.
 *
 * Error recovery shall only be invoked again if previous one was completed.
 * The flag error_recovery is set when error recovery mechanism is scheduled,
 * and is checked when we need to schedule another error recovery. If it is
 * in place already, then we shouldn't schedule another one.
 */
void i2400m_error_recovery(struct i2400m *i2400m)
{
	struct device *dev = i2400m_dev(i2400m);

	if (atomic_add_return(1, &i2400m->error_recovery) == 1) {
		if (i2400m_schedule_work(i2400m, __i2400m_error_recovery,
			GFP_ATOMIC, NULL, 0) < 0) {
			dev_err(dev, "run out of memory for "
				"scheduling an error recovery ?\n");
			atomic_dec(&i2400m->error_recovery);
		}
	} else
		atomic_dec(&i2400m->error_recovery);
	return;
}
EXPORT_SYMBOL_GPL(i2400m_error_recovery);

/*
 * Alloc the command and ack buffers for boot mode
 *
@@ -802,6 +886,13 @@ void i2400m_init(struct i2400m *i2400m)

	mutex_init(&i2400m->init_mutex);
	/* wake_tx_ws is initialized in i2400m_tx_setup() */
	atomic_set(&i2400m->bus_reset_retries, 0);

	i2400m->alive = 0;

	/* initialize error_recovery to 1 for denoting we
	 * are not yet ready to take any error recovery */
	atomic_set(&i2400m->error_recovery, 1);
}
EXPORT_SYMBOL_GPL(i2400m_init);

+4 −1
Original line number Diff line number Diff line
@@ -99,7 +99,10 @@ enum {
 *
 * @tx_workqueue: workqeueue used for data TX; we don't use the
 *     system's workqueue as that might cause deadlocks with code in
 *     the bus-generic driver.
 *     the bus-generic driver. The read/write operation to the queue
 *     is protected with spinlock (tx_lock in struct i2400m) to avoid
 *     the queue being destroyed in the middle of a the queue read/write
 *     operation.
 *
 * @debugfs_dentry: dentry for the SDIO specific debugfs files
 *
+73 −9
Original line number Diff line number Diff line
@@ -160,6 +160,16 @@
#include <linux/wimax/i2400m.h>
#include <asm/byteorder.h>

enum {
/* netdev interface */
	/*
	 * Out of NWG spec (R1_v1.2.2), 3.3.3 ASN Bearer Plane MTU Size
	 *
	 * The MTU is 1400 or less
	 */
	I2400M_MAX_MTU = 1400,
};

/* Misc constants */
enum {
	/* Size of the Boot Mode Command buffer */
@@ -167,6 +177,11 @@ enum {
	I2400M_BM_ACK_BUF_SIZE = 256,
};

enum {
	/* Maximum number of bus reset can be retried */
	I2400M_BUS_RESET_RETRIES = 3,
};

/**
 * struct i2400m_poke_table - Hardware poke table for the Intel 2400m
 *
@@ -227,6 +242,11 @@ struct i2400m_barker_db;
 *     so we have a tx_blk_size variable that the bus layer sets to
 *     tell the engine how much of that we need.
 *
 * @bus_tx_room_min: [fill] Minimum room required while allocating
 *     TX queue's buffer space for message header. SDIO requires
 *     224 bytes and USB 16 bytes. Refer bus specific driver code
 *     for details.
 *
 * @bus_pl_size_max: [fill] Maximum payload size.
 *
 * @bus_setup: [optional fill] Function called by the bus-generic code
@@ -397,7 +417,7 @@ struct i2400m_barker_db;
 *
 * @tx_size_max: biggest TX message sent.
 *
 * @rx_lock: spinlock to protect RX members
 * @rx_lock: spinlock to protect RX members and rx_roq_refcount.
 *
 * @rx_pl_num: total number of payloads received
 *
@@ -421,6 +441,10 @@ struct i2400m_barker_db;
 *     delivered. Then the driver can release them to the host. See
 *     drivers/net/i2400m/rx.c for details.
 *
 * @rx_roq_refcount: refcount rx_roq. This refcounts any access to
 *     rx_roq thus preventing rx_roq being destroyed when rx_roq
 *     is being accessed. rx_roq_refcount is protected by rx_lock.
 *
 * @rx_reports: reports received from the device that couldn't be
 *     processed because the driver wasn't still ready; when ready,
 *     they are pulled from here and chewed.
@@ -507,6 +531,38 @@ struct i2400m_barker_db;
 *     same.
 *
 * @pm_notifier: used to register for PM events
 *
 * @bus_reset_retries: counter for the number of bus resets attempted for
 *	this boot. It's not for tracking the number of bus resets during
 *	the whole driver life cycle (from insmod to rmmod) but for the
 *	number of dev_start() executed until dev_start() returns a success
 *	(ie: a good boot means a dev_stop() followed by a successful
 *	dev_start()). dev_reset_handler() increments this counter whenever
 *	it is triggering a bus reset. It checks this counter to decide if a
 *	subsequent bus reset should be retried. dev_reset_handler() retries
 *	the bus reset until dev_start() succeeds or the counter reaches
 *	I2400M_BUS_RESET_RETRIES. The counter is cleared to 0 in
 *	dev_reset_handle() when dev_start() returns a success,
 *	ie: a successul boot is completed.
 *
 * @alive: flag to denote if the device *should* be alive. This flag is
 *	everything like @updown (see doc for @updown) except reflecting
 *	the device state *we expect* rather than the actual state as denoted
 *	by @updown. It is set 1 whenever @updown is set 1 in dev_start().
 *	Then the device is expected to be alive all the time
 *	(i2400m->alive remains 1) until the driver is removed. Therefore
 *	all the device reboot events detected can be still handled properly
 *	by either dev_reset_handle() or .pre_reset/.post_reset as long as
 *	the driver presents. It is set 0 along with @updown in dev_stop().
 *
 * @error_recovery: flag to denote if we are ready to take an error recovery.
 *	0 for ready to take an error recovery; 1 for not ready. It is
 *	initialized to 1 while probe() since we don't tend to take any error
 *	recovery during probe(). It is decremented by 1 whenever dev_start()
 *	succeeds to indicate we are ready to take error recovery from now on.
 *	It is checked every time we wanna schedule an error recovery. If an
 *	error recovery is already in place (error_recovery was set 1), we
 *	should not schedule another one until the last one is done.
 */
struct i2400m {
	struct wimax_dev wimax_dev;	/* FIRST! See doc */
@@ -522,6 +578,7 @@ struct i2400m {
	wait_queue_head_t state_wq;	/* Woken up when on state updates */

	size_t bus_tx_block_size;
	size_t bus_tx_room_min;
	size_t bus_pl_size_max;
	unsigned bus_bm_retries;

@@ -550,10 +607,12 @@ struct i2400m {
		tx_num, tx_size_acc, tx_size_min, tx_size_max;

	/* RX stuff */
	spinlock_t rx_lock;		/* protect RX state */
	/* protect RX state and rx_roq_refcount */
	spinlock_t rx_lock;
	unsigned rx_pl_num, rx_pl_max, rx_pl_min,
		rx_num, rx_size_acc, rx_size_min, rx_size_max;
	struct i2400m_roq *rx_roq;	/* not under rx_lock! */
	struct i2400m_roq *rx_roq;	/* access is refcounted */
	struct kref rx_roq_refcount;	/* refcount access to rx_roq */
	u8 src_mac_addr[ETH_HLEN];
	struct list_head rx_reports;	/* under rx_lock! */
	struct work_struct rx_report_ws;
@@ -581,6 +640,16 @@ struct i2400m {
	struct i2400m_barker_db *barker;

	struct notifier_block pm_notifier;

	/* counting bus reset retries in this boot */
	atomic_t bus_reset_retries;

	/* if the device is expected to be alive */
	unsigned alive;

	/* 0 if we are ready for error recovery; 1 if not ready  */
	atomic_t error_recovery;

};


@@ -803,6 +872,7 @@ void i2400m_put(struct i2400m *i2400m)
extern int i2400m_dev_reset_handle(struct i2400m *, const char *);
extern int i2400m_pre_reset(struct i2400m *);
extern int i2400m_post_reset(struct i2400m *);
extern void i2400m_error_recovery(struct i2400m *);

/*
 * _setup()/_release() are called by the probe/disconnect functions of
@@ -815,7 +885,6 @@ extern int i2400m_rx(struct i2400m *, struct sk_buff *);
extern struct i2400m_msg_hdr *i2400m_tx_msg_get(struct i2400m *, size_t *);
extern void i2400m_tx_msg_sent(struct i2400m *);

extern int i2400m_power_save_disabled;

/*
 * Utility functions
@@ -922,10 +991,5 @@ extern int i2400m_barker_db_init(const char *);
extern void i2400m_barker_db_exit(void);


/* Module parameters */

extern int i2400m_idle_mode_disabled;
extern int i2400m_rx_reorder_disabled;


#endif /* #ifndef __I2400M_H__ */
+5 −7
Original line number Diff line number Diff line
@@ -84,17 +84,15 @@

enum {
/* netdev interface */
	/*
	 * Out of NWG spec (R1_v1.2.2), 3.3.3 ASN Bearer Plane MTU Size
	 *
	 * The MTU is 1400 or less
	 */
	I2400M_MAX_MTU = 1400,
	/* 20 secs? yep, this is the maximum timeout that the device
	 * might take to get out of IDLE / negotiate it with the base
	 * station. We add 1sec for good measure. */
	I2400M_TX_TIMEOUT = 21 * HZ,
	I2400M_TX_QLEN = 5,
	/*
	 * Experimentation has determined that, 20 to be a good value
	 * for minimizing the jitter in the throughput.
	 */
	I2400M_TX_QLEN = 20,
};


Loading