[PATCH 39/43] rc-core: use struct rc_event to signal TX events from userspace

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Using struct rc_event as the data unit for writes from userspace provides
more flexibility and easier future compatibility with future developments
(e.g. if rc-core grew support for some very different transmission, such
as RF transmission).

Signed-off-by: David Härdeman <david@xxxxxxxxxxx>
---
 drivers/media/rc/ene_ir.c        |   24 ++++++------
 drivers/media/rc/ene_ir.h        |    2 -
 drivers/media/rc/ir-lirc-codec.c |   12 +++---
 drivers/media/rc/ite-cir.c       |   19 +++++----
 drivers/media/rc/mceusb.c        |   10 +++--
 drivers/media/rc/nuvoton-cir.c   |   10 +++--
 drivers/media/rc/rc-loopback.c   |   10 ++++-
 drivers/media/rc/rc-main.c       |   18 +++------
 drivers/media/rc/redrat3.c       |   20 ++++++++--
 drivers/media/rc/winbond-cir.c   |   22 +++++------
 include/media/rc-core.h          |   79 +++++++++++++++++++-------------------
 include/media/rc-ir-raw.h        |    6 +++
 12 files changed, 133 insertions(+), 99 deletions(-)

diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c
index 2c2cfd5..9669311 100644
--- a/drivers/media/rc/ene_ir.c
+++ b/drivers/media/rc/ene_ir.c
@@ -604,7 +604,7 @@ static void ene_tx_enable(struct ene_device *dev)
 static void ene_tx_disable(struct ene_device *dev)
 {
 	ene_write_reg(dev, ENE_CIRCFG, dev->saved_conf1);
-	init_ir_raw_event(&dev->tx_event);
+	dev->tx_event.val = 0;
 }
 
 
@@ -614,7 +614,7 @@ static void ene_tx_sample(struct ene_device *dev)
 	u8 raw_tx;
 
 	/* Grab next TX sample */
-	if (!dev->tx_event.duration) {
+	while (!dev->tx_event.val) {
 
 		if (!kfifo_get(&dev->rdev->txfifo, &dev->tx_event)) {
 			if (!dev->tx_done) {
@@ -629,19 +629,21 @@ static void ene_tx_sample(struct ene_device *dev)
 			}
 		}
 
-		dev->tx_event.duration = DIV_ROUND_CLOSEST(dev->tx_event.duration,
-							   US_TO_NS(sample_period));
-		if (!dev->tx_event.duration)
-			dev->tx_event.duration = 1;
+		if (!is_ir_raw_timing_event(dev->tx_event))
+			continue;
+
+		dev->tx_event.val = max_t(u64, 1,
+					  DIV_ROUND_CLOSEST(dev->tx_event.val,
+							    US_TO_NS(sample_period)));
 	}
 
-	raw_tx = min_t(unsigned, dev->tx_event.duration, ENE_CIRRLC_OUT_MASK);
-	dev->tx_event.duration -= raw_tx;
+	raw_tx = min_t(unsigned, dev->tx_event.val, ENE_CIRRLC_OUT_MASK);
+	dev->tx_event.val -= raw_tx;
 
 	dbg("TX: sample %8d (%s)", raw_tx * sample_period,
-	    dev->tx_event.pulse ? "pulse" : "space");
+	    dev->tx_event.code == RC_IR_RAW_PULSE ? "pulse" : "space");
 
-	if (dev->tx_event.pulse)
+	if (dev->tx_event.code == RC_IR_RAW_PULSE)
 		raw_tx |= ENE_CIRRLC_OUT_PULSE;
 
 	ene_write_reg(dev,
@@ -953,7 +955,7 @@ static int ene_transmit(struct rc_dev *rdev)
 	dev->tx_reg = 0;
 	dev->tx_done = false;
 
-	init_ir_raw_event(&dev->tx_event);
+	dev->tx_event.val = 0;
 	dbg("TX: %d samples", kfifo_len(&rdev->txfifo));
 
 	spin_lock_irqsave(&dev->hw_lock, flags);
diff --git a/drivers/media/rc/ene_ir.h b/drivers/media/rc/ene_ir.h
index 975d67a..d0898e1 100644
--- a/drivers/media/rc/ene_ir.h
+++ b/drivers/media/rc/ene_ir.h
@@ -224,7 +224,7 @@ struct ene_device {
 	u8  saved_conf1;			/* saved FEC0 reg */
 
 	/* TX buffer */
-	struct ir_raw_event tx_event;		/* current sample for TX */
+	struct rc_event tx_event;		/* current sample for TX */
 	bool tx_done;				/* done transmitting */
 	struct completion tx_complete;		/* TX completion */
 	struct timer_list tx_sim_timer;
diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c
index 6811db9..a5b0368 100644
--- a/drivers/media/rc/ir-lirc-codec.c
+++ b/drivers/media/rc/ir-lirc-codec.c
@@ -109,8 +109,8 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf,
 	ssize_t ret;
 	s64 towait;
 	unsigned int duration = 0; /* signal duration in us */
-	DEFINE_IR_RAW_EVENT(event);
 	bool pulse = true;
+	struct rc_event ev;
 
 	if (!lirc || !lirc->dev)
 		return -EFAULT;
@@ -122,14 +122,16 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf,
 	if (n % sizeof(u32) || ((n / sizeof(u32)) % 2) == 0)
 		return -EINVAL;
 
+	ev.type = RC_IR_RAW;
+	ev.reserved = 0x0;
+
 	for (ret = 0; ret + sizeof(u32) <= n; ret += sizeof(u32)) {
 		if (copy_from_user(&value, buf + ret, sizeof(u32)))
 			return -EFAULT;
 
-		event.pulse = pulse;
-		event.duration = US_TO_NS(value);
-		if (kfifo_in_spinlocked(&dev->txfifo, &event, 1, &dev->txlock)
-		    != 1)
+		ev.code = pulse ? RC_IR_RAW_PULSE : RC_IR_RAW_SPACE;
+		ev.val = US_TO_NS(value);
+		if (!kfifo_in_spinlocked(&dev->txfifo, &ev, 1, &dev->txlock))
 			break;
 
 		pulse = !pulse;
diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c
index 44759db..51fb845 100644
--- a/drivers/media/rc/ite-cir.c
+++ b/drivers/media/rc/ite-cir.c
@@ -390,7 +390,7 @@ static int ite_tx_ir(struct rc_dev *rcdev)
 	int max_rle_us, next_rle_us;
 	u8 last_sent[ITE_TX_FIFO_LEN];
 	u8 val;
-	DEFINE_IR_RAW_EVENT(event);
+	struct rc_event ev;
 
 	ite_dbg("%s called", __func__);
 
@@ -420,14 +420,16 @@ static int ite_tx_ir(struct rc_dev *rcdev)
 	fifo_avail =
 	    ITE_TX_FIFO_LEN - dev->params.get_tx_used_slots(dev);
 
-	while (kfifo_get(&rcdev->txfifo, &event) && dev->in_use) {
+	while (kfifo_get(&rcdev->txfifo, &ev) && dev->in_use) {
+		if (!is_ir_raw_timing_event(ev))
+			continue;
+
 		/* transmit the next sample */
-		remaining_us = event.duration;
+		remaining_us = ev.val;
 
-		ite_dbg("%s: %ld",
-				      (event.pulse ? "pulse" : "space"),
-				      (long int)
-				      remaining_us);
+		ite_dbg("%s: %lld",
+			ev.code == RC_IR_RAW_PULSE ? "pulse" : "space",
+			(long long int)remaining_us);
 
 		/* repeat while the pulse is non-zero length */
 		while (remaining_us > 0 && dev->in_use) {
@@ -450,9 +452,8 @@ static int ite_tx_ir(struct rc_dev *rcdev)
 			val = (val - 1) & ITE_TX_RLE_MASK;
 
 			/* take into account pulse/space prefix */
-			if (event.pulse)
+			if (ev.code == RC_IR_RAW_PULSE)
 				val |= ITE_TX_PULSE;
-
 			else
 				val |= ITE_TX_SPACE;
 
diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
index e9dda87..952ea01 100644
--- a/drivers/media/rc/mceusb.c
+++ b/drivers/media/rc/mceusb.c
@@ -785,7 +785,7 @@ static int mceusb_tx_ir(struct rc_dev *dev)
 	struct mceusb_dev *ir = dev->priv;
 	unsigned tmp, i = 0;
 	unsigned char buf[MCE_CMDBUF_SIZE]; /* MCE command buffer */
-	DEFINE_IR_RAW_EVENT(event);
+	struct rc_event event;
 
 	/* MCE tx init header */
 	buf[i++] = MCE_CMD_PORT_IR;
@@ -793,7 +793,10 @@ static int mceusb_tx_ir(struct rc_dev *dev)
 	buf[i++] = ir->tx_mask;
 
 	while (kfifo_get(&dev->txfifo, &event)) {
-		tmp = event.duration / US_TO_NS(MCE_TIME_UNIT);
+		if (!is_ir_raw_timing_event(event))
+			continue;
+
+		tmp = event.val / US_TO_NS(MCE_TIME_UNIT);
 
 		/* Split event into pulses/spaces <= 127 * 50us = 6.35ms */
 		while (tmp > 0 && i < (MCE_CMDBUF_SIZE - 1)) {
@@ -804,7 +807,8 @@ static int mceusb_tx_ir(struct rc_dev *dev)
 
 			buf[i] = min_t(unsigned, tmp, MCE_MAX_PULSE_LENGTH);
 			tmp -= buf[i];
-			buf[i++] |= event.pulse ? MCE_PULSE_BIT : 0x00;
+			if (event.code == RC_IR_RAW_PULSE)
+				buf[i++] |= MCE_PULSE_BIT;
 		}
 
 		/* See if command buffer is full */
diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c
index 915074e..4c37ade 100644
--- a/drivers/media/rc/nuvoton-cir.c
+++ b/drivers/media/rc/nuvoton-cir.c
@@ -756,6 +756,7 @@ static irqreturn_t nvt_cir_isr(int irq, void *data)
 	struct nvt_dev *nvt = data;
 	u8 status, iren, cur_state;
 	unsigned long flags;
+	struct rc_event ev;
 
 	nvt_dbg_verbose("%s firing", __func__);
 
@@ -830,14 +831,15 @@ static irqreturn_t nvt_cir_isr(int irq, void *data)
 		spin_lock_irqsave(&nvt->tx.lock, flags);
 
 		/* Any data in tx buffer? */
-		if (nvt->tx.cur_buf_num >= nvt->tx.buf_count) {
-			DEFINE_IR_RAW_EVENT(event);
-			if (!kfifo_get(&nvt->rdev->txfifo, &event)) {
+		while (nvt->tx.cur_buf_num >= nvt->tx.buf_count) {
+			if (!kfifo_get(&nvt->rdev->txfifo, &ev)) {
 				/* No more data, disable TTR interrupt */
 				u8 tmp = nvt_cir_reg_read(nvt, CIR_IREN);
 				nvt_cir_reg_write(nvt, tmp & ~CIR_IREN_TTR, CIR_IREN);
+			} else if (!is_ir_raw_timing_event(ev)) {
+				continue;
 			} else {
-				unsigned sample = event.duration / 1000;
+				unsigned sample = ev.val / 1000;
 				nvt->tx.buf_count = sizeof(sample);
 				nvt->tx.cur_buf_num = 0;
 				memcpy(nvt->tx.buf, &sample, sizeof(sample));
diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c
index 7775b3e..45ef966 100644
--- a/drivers/media/rc/rc-loopback.c
+++ b/drivers/media/rc/rc-loopback.c
@@ -53,6 +53,7 @@ static int loop_tx_ir(struct rc_dev *dev)
 {
 	struct loopback_dev *lodev = dev->priv;
 	u32 rxmask;
+	struct rc_event event;
 	DEFINE_IR_RAW_EVENT(rawir);
 
 	if (lodev->txcarrier < lodev->rxcarriermin ||
@@ -73,8 +74,15 @@ static int loop_tx_ir(struct rc_dev *dev)
 		return 0;
 	}
 
-	while (kfifo_get(&dev->txfifo, &rawir))
+	while (kfifo_get(&dev->txfifo, &event)) {
+		if (is_ir_raw_timing_event(event))
+			continue;
+		init_ir_raw_event(&rawir);
+		rawir.duration = event.val;
+		if (event.code == RC_IR_RAW_PULSE)
+			rawir.pulse = true;
 		ir_raw_event_store_with_filter(dev, &rawir);
+	}
 
 	/* Fake a silence long enough to cause us to go idle */
 	init_ir_raw_event(&rawir);
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index 14728fc..6c8bc3a 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -801,14 +801,12 @@ static ssize_t rc_write(struct file *file, const char __user *buffer,
 	struct rc_client *client = file->private_data;
 	struct rc_dev *dev = client->dev;
 	ssize_t ret;
-	DEFINE_IR_RAW_EVENT(event);
-	bool pulse = true;
-	u32 value;
+	struct rc_event ev;
 
 	if (!dev->tx_ir)
 		return -ENOSYS;
 
-	if ((count < sizeof(u32)) || (count % sizeof(u32)))
+	if (count < sizeof(ev))
 		return -EINVAL;
 
 again:
@@ -825,16 +823,14 @@ again:
 	if (!dev->exist)
 		return -ENODEV;
 
-	for (ret = 0; ret + sizeof(value) <= count; ret += sizeof(value)) {
-		if (copy_from_user(&value, buffer + ret, sizeof(value)))
+	for (ret = 0; ret + sizeof(ev) <= count; ret += sizeof(ev)) {
+		if (copy_from_user(&ev, buffer + ret, sizeof(ev)))
 			return -EFAULT;
 
-		event.duration = US_TO_NS(value);
-		event.pulse = pulse;
-		pulse = !pulse;
+		if (ev.reserved)
+			return -EINVAL;
 
-		if (kfifo_in_spinlocked(&dev->txfifo, &event, 1, &dev->txlock)
-		    != 1)
+		if (!kfifo_in_spinlocked(&dev->txfifo, &ev, 1, &dev->txlock))
 			break;
 	}
 
diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c
index 394799d..760f13e 100644
--- a/drivers/media/rc/redrat3.c
+++ b/drivers/media/rc/redrat3.c
@@ -906,8 +906,8 @@ static int redrat3_transmit_ir(struct rc_dev *rcdev)
 {
 	struct redrat3_dev *rr3 = rcdev->priv;
 	struct device *dev = rr3->dev;
-	DEFINE_IR_RAW_EVENT(event);
 	struct redrat3_tx_data tx;
+	struct rc_event ev;
 	unsigned nlens = 0, nsamples = 0, i;
 	int txlen, actual_len;
 	int ret;
@@ -924,9 +924,23 @@ static int redrat3_transmit_ir(struct rc_dev *rcdev)
 	rr3->det_enabled = false;
 	rr3->transmitting = true;
 
-	while (kfifo_get(&rcdev->txfifo, &event)) {
+	while (kfifo_get(&rcdev->txfifo, &ev)) {
+		if (!is_ir_raw_timing_event(ev))
+			continue;
+
 		/* Convert duration to RR format */
-		sample = redrat3_us_to_len(event.duration / 1000);
+		sample = redrat3_us_to_len(ev.val / 1000);
+
+		if (sample < 1)
+			continue;
+
+		if (((ev.code == RC_IR_RAW_PULSE) && (nsamples % 2 == 1)) ||
+		    ((ev.code == RC_IR_RAW_SPACE) && (nsamples % 2 == 0))) {
+			/* Input is not in pulse/space/pulse/space/etc format */
+			dev_err(dev, "signal invalid\n");
+			ret = -EINVAL;
+			goto out;
+		}
 
 		/* See if we already have the same sample size stored */
 		for (i = 0; i < nlens; i++)
diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c
index 5a240ba..76a8104 100644
--- a/drivers/media/rc/winbond-cir.c
+++ b/drivers/media/rc/winbond-cir.c
@@ -213,7 +213,7 @@ struct wbcir_data {
 	/* TX state */
 	enum wbcir_txstate txstate;
 	struct led_trigger *txtrigger;
-	struct ir_raw_event txevent;
+	struct rc_event txevent;
 	wait_queue_head_t txwaitq;
 	u8 txmask;
 	u32 txcarrier;
@@ -403,19 +403,20 @@ wbcir_irq_tx(struct wbcir_data *data)
 	 * X = duration, encoded as (X + 1) * 10us (i.e 10 to 1280 us)
 	 */
 	for (used = 0; used < space; used++) {
-		while (data->txevent.duration == 0) {
+		while (data->txevent.val == 0) {
 			if (!kfifo_get(&data->dev->txfifo, &data->txevent))
 				break;
+			if (!is_ir_raw_timing_event(data->txevent))
+				continue;
 			/* Convert duration to multiples of 10us */
-			data->txevent.duration =
-				DIV_ROUND_CLOSEST(data->txevent.duration,
-						  10 * 1000);
+			data->txevent.val =
+				DIV_ROUND_CLOSEST(data->txevent.val, 10 * 1000);
 		}
 
-		byte = min_t(u32, 0x80, data->txevent.duration);
-		data->txevent.duration -= byte;
+		byte = min_t(u32, 0x80, data->txevent.val);
+		data->txevent.val -= byte;
 		byte--;
-		byte |= data->txevent.pulse ? 0x80 : 0x00;
+		byte |= data->txevent.code == RC_IR_RAW_PULSE ? 0x80 : 0x00;
 		bytes[used] = byte;
 	}
 
@@ -429,8 +430,7 @@ wbcir_irq_tx(struct wbcir_data *data)
 		wbcir_set_irqmask(data, WBCIR_IRQ_RX | WBCIR_IRQ_ERR);
 		led_trigger_event(data->txtrigger, LED_OFF);
 		wake_up(&data->txwaitq);
-	} else if (data->txevent.duration == 0 &&
-		   kfifo_is_empty(&data->dev->txfifo)) {
+	} else if (data->txevent.val == 0 && kfifo_is_empty(&data->dev->txfifo)) {
 		/* At the end of transmission, tell the hw before last byte */
 		outsb(data->sbase + WBCIR_REG_SP3_TXDATA, bytes, used - 1);
 		outb(WBCIR_TX_EOT, data->sbase + WBCIR_REG_SP3_ASCR);
@@ -594,7 +594,7 @@ wbcir_tx(struct rc_dev *dev)
 	}
 
 	/* Fill the TX fifo once, the irq handler will do the rest */
-	init_ir_raw_event(&data->txevent);
+	data->txevent.val = 0;
 	wbcir_irq_tx(data);
 
 	/* Wait for the TX to complete */
diff --git a/include/media/rc-core.h b/include/media/rc-core.h
index f2ff7f7..d8cad87 100644
--- a/include/media/rc-core.h
+++ b/include/media/rc-core.h
@@ -199,7 +199,6 @@ struct rc_keymap_entry {
 	};
 };
 
-#define RC_TX_KFIFO_SIZE	1024
 struct ir_raw_event {
 	union {
 		u32             duration;
@@ -216,6 +215,43 @@ struct ir_raw_event {
 	unsigned                carrier_report:1;
 };
 
+/**
+ * struct rc_event - used to communicate rc events to userspace
+ * @type:	the event type
+ * @code:	the event code (type specific)
+ * @reserved:	zero for now
+ * @val:	the event value (type and code specific)
+ */
+struct rc_event {
+	__u16 type;
+	__u16 code;
+	__u32 reserved;
+	__u64 val;
+} __packed;
+
+/* rc_event.type value */
+#define RC_DEBUG		0x0
+#define RC_CORE			0x1
+#define RC_KEY			0x2
+#define RC_IR_RAW		0x3
+
+/* RC_CORE codes */
+#define RC_CORE_DROPPED		0x0
+
+/* RC_KEY codes */
+#define RC_KEY_REPEAT		0x0
+#define RC_KEY_PROTOCOL		0x1
+#define RC_KEY_SCANCODE		0x2
+#define RC_KEY_TOGGLE		0x3
+
+/* RC_IR_RAW codes */
+#define RC_IR_RAW_SPACE		0x0
+#define RC_IR_RAW_PULSE		0x1
+#define RC_IR_RAW_START		0x2
+#define RC_IR_RAW_STOP		0x3
+#define RC_IR_RAW_RESET		0x4
+#define RC_IR_RAW_CARRIER	0x5
+#define RC_IR_RAW_DUTY_CYCLE	0x6
 
 /**
  * struct rc_dev - represents a remote control device
@@ -279,6 +315,7 @@ struct ir_raw_event {
  * @set_ir_tx: allow driver to change tx settings
  */
 #define RC_MAX_KEYTABLES		32
+#define RC_TX_KFIFO_SIZE		1024
 struct rc_dev {
 	struct device			dev;
 	const char			*input_name;
@@ -294,7 +331,7 @@ struct rc_dev {
 	struct list_head		client_list;
 	spinlock_t			client_lock;
 	unsigned			users;
-	DECLARE_KFIFO_PTR(txfifo, struct ir_raw_event);
+	DECLARE_KFIFO_PTR(txfifo, struct rc_event);
 	spinlock_t			txlock;
 	wait_queue_head_t		txwait;
 	wait_queue_head_t		rxwait;
@@ -374,44 +411,6 @@ struct rc_keytable {
 
 #define to_rc_dev(d) container_of(d, struct rc_dev, dev)
 
-/* rc_event.type value */
-#define RC_DEBUG		0x0
-#define RC_CORE			0x1
-#define RC_KEY			0x2
-#define RC_IR_RAW		0x3
-
-/* RC_CORE codes */
-#define RC_CORE_DROPPED		0x0
-
-/* RC_KEY codes */
-#define RC_KEY_REPEAT		0x0
-#define RC_KEY_PROTOCOL		0x1
-#define RC_KEY_SCANCODE		0x2
-#define RC_KEY_TOGGLE		0x3
-
-/* RC_IR_RAW codes */
-#define RC_IR_RAW_SPACE		0x0
-#define RC_IR_RAW_PULSE		0x1
-#define RC_IR_RAW_START		0x2
-#define RC_IR_RAW_STOP		0x3
-#define RC_IR_RAW_RESET		0x4
-#define RC_IR_RAW_CARRIER	0x5
-#define RC_IR_RAW_DUTY_CYCLE	0x6
-
-/**
- * struct rc_event - used to communicate rc events to userspace
- * @type:	the event type
- * @code:	the event code (type specific)
- * @reserved:	zero for now
- * @val:	the event value (type and code specific)
- */
-struct rc_event {
-	__u16 type;
-	__u16 code;
-	__u32 reserved;
-	__u64 val;
-} __packed;
-
 /*
  * From rc-main.c
  * Those functions can be used on any type of Remote Controller. They
diff --git a/include/media/rc-ir-raw.h b/include/media/rc-ir-raw.h
index 4c3fe78..7fd0693 100644
--- a/include/media/rc-ir-raw.h
+++ b/include/media/rc-ir-raw.h
@@ -65,4 +65,10 @@ static inline void ir_raw_event_reset(struct rc_dev *dev)
 	ir_raw_event_handle(dev);
 }
 
+static inline bool is_ir_raw_timing_event(struct rc_event ev)
+{
+	return (ev.type == RC_IR_RAW &&
+		(ev.code == RC_IR_RAW_PULSE || ev.code == RC_IR_RAW_SPACE));
+}
+
 #endif /* _RC_IR_RAW */

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux