[RFC PATCH 1/2] usb: typec: Simple bus for alternate modes

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

 



Bus for binding SVID specific drivers to the altnernate mode
devices.

Signed-off-by: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx>
---
 drivers/usb/typec/Makefile             |   2 +
 drivers/usb/typec/altmode.c            | 249 +++++++++++++++++++++++++++++++++
 drivers/usb/typec/altmode.h            |  46 ++++++
 drivers/usb/typec/{typec.c => class.c} | 128 ++++++++++++-----
 drivers/usb/typec/tcpm.c               |   2 +-
 include/linux/usb/typec.h              |   6 +-
 include/linux/usb/typec_altmode.h      |  65 +++++++++
 7 files changed, 461 insertions(+), 37 deletions(-)
 create mode 100644 drivers/usb/typec/altmode.c
 create mode 100644 drivers/usb/typec/altmode.h
 rename drivers/usb/typec/{typec.c => class.c} (93%)
 create mode 100644 include/linux/usb/typec_altmode.h

diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
index b77688ce1f16..e157cc4526f0 100644
--- a/drivers/usb/typec/Makefile
+++ b/drivers/usb/typec/Makefile
@@ -1,4 +1,6 @@
 obj-$(CONFIG_TYPEC)		+= typec.o
+typec-y				:= class.o
+typec-y				+= altmode.o
 obj-$(CONFIG_TYPEC_TCPM)	+= tcpm.o
 obj-y				+= fusb302/
 obj-$(CONFIG_TYPEC_WCOVE)	+= typec_wcove.o
diff --git a/drivers/usb/typec/altmode.c b/drivers/usb/typec/altmode.c
new file mode 100644
index 000000000000..81fe0af37da0
--- /dev/null
+++ b/drivers/usb/typec/altmode.c
@@ -0,0 +1,249 @@
+/**
+ * USB Type-C Alternate Mode bus
+ *
+ * Copyright (C) 2017 Intel Corporation
+ * Author: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/usb/typec_altmode.h>
+
+#include "altmode.h"
+
+/* -------------------------------------------------------------------------- */
+/* Common API */
+
+/**
+ * typec_altmode_notify - Communicate with the platform
+ * @altmode: Handle to the alternate mode
+ * @conf: Alternate mode specific configuration value
+ * @data: Alternate mode specific data to be passed to the partner
+ *
+ * The primary purpose for this function is to allow the alternate mode drivers
+ * to tell the platform which pin configuration has been negotiated with the
+ * partner, but communication to the other direction is also possible, so low
+ * level device drivers can also send notifications to the alternate mode
+ * drivers. The actual communication will be specific to every alternate mode.
+ */
+int typec_altmode_notify(struct typec_altmode *altmode,
+			 unsigned long conf, void *data)
+{
+	struct typec_altmode *partner;
+
+	if (!altmode)
+		return 0;
+
+	if (!altmode->partner)
+		return -ENODEV;
+
+	partner = altmode->partner;
+
+	/*
+	 * This is where we will later pass the data to the remote-endpoints,
+	 * but for now simply passing the data to the port.
+	 *
+	 * More information about the remote-endpoint concept:
+	 *   Documentation/acpi/dsd/graph.txt
+	 *   Documentation/devicetree/bindings/graph.txt
+	 *
+	 * Check drivers/base/property.c to see the API for the endpoint
+	 * handling (the fwnode_graph* functions).
+	 */
+
+	if (partner->ops && partner->ops->notify)
+		return partner->ops->notify(partner, conf, data);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(typec_altmode_notify);
+
+/**
+ * typec_altmode_send_vdm - Send Vendor Defined Messages to the partner
+ * @altmode: Alternate mode handle
+ * @header: VDM Header
+ * @vdo: Array of Vendor Defined Data Objects
+ * @count: Number of Data Objects
+ *
+ * The alternate mode drivers use this function for SVID specific communication
+ * with the partner. The port drivers use it to deliver the Structured VDMs
+ * received from the partners to the alternate mode drivers.
+ */
+int typec_altmode_send_vdm(struct typec_altmode *altmode,
+			   u32 header, u32 *vdo, int count)
+{
+	struct typec_altmode *partner;
+
+	if (!altmode)
+		return 0;
+
+	if (!altmode->partner)
+		return -ENODEV;
+
+	partner = altmode->partner;
+
+	if (partner->ops && partner->ops->vdm)
+		partner->ops->vdm(partner, header, vdo, count);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(typec_altmode_send_vdm);
+
+void typec_altmode_set_drvdata(struct typec_altmode *altmode, void *data)
+{
+	dev_set_drvdata(&altmode->dev, data);
+}
+EXPORT_SYMBOL_GPL(typec_altmode_set_drvdata);
+
+void *typec_altmode_get_drvdata(struct typec_altmode *altmode)
+{
+	return dev_get_drvdata(&altmode->dev);
+}
+EXPORT_SYMBOL_GPL(typec_altmode_get_drvdata);
+
+/* -------------------------------------------------------------------------- */
+/* API for the alternate mode drivers */
+
+/**
+ * typec_altmode_register_ops - Register alternate mode specific operations
+ * @altmode: Handle to the alternate mode
+ * @ops: Alternate mode specific operations vector
+ *
+ * Used by the alternate mode drivers for registering their operation vectors
+ * with the alternate mode device.
+ */
+void typec_altmode_register_ops(struct typec_altmode *altmode,
+				struct typec_altmode_ops *ops)
+{
+	altmode->ops = ops;
+}
+EXPORT_SYMBOL_GPL(typec_altmode_register_ops);
+
+/**
+ * typec_altmode_get_plug - Find cable plug alternate mode
+ * @altmode: Handle to partner alternate mode
+ * @index: Cable plug index
+ *
+ * Increment reference count for cable plug alternate mode device. Returns
+ * handle to the cable plug alternate mode, or NULL if none is found.
+ */
+struct typec_altmode *typec_altmode_get_plug(struct typec_altmode *altmode,
+					     int index)
+{
+	if (altmode->partner->plug[index]) {
+		get_device(&altmode->partner->plug[index]->dev);
+		return altmode->partner->plug[index];
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(typec_altmode_get_plug);
+
+/**
+ * typec_altmode_get_plug - Decrement cable plug alternate mode reference count
+ * @plug: Handle to the cable plug alternate mode
+ */
+void typec_altmode_put_plug(struct typec_altmode *plug)
+{
+	if (plug)
+		put_device(&plug->dev);
+}
+EXPORT_SYMBOL_GPL(typec_altmode_put_plug);
+
+/* -------------------------------------------------------------------------- */
+/* API for the port drivers */
+
+/**
+ * typec_find_altmode - Match SVID to an array of alternate modes
+ * @altmodes: Array of alternate modes
+ * @n: Number of elements in the array, or -1 for NULL termiated arrays
+ * @svid: Standard or Vendor ID to match with
+ *
+ * Return pointer to an alternate mode with SVID mathing @svid, or NULL when no
+ * match is found.
+ */
+struct typec_altmode *typec_find_altmode(struct typec_altmode **altmodes,
+					 size_t n, u16 svid)
+{
+	int i;
+
+	for (i = 0; i < n; i++) {
+		if (!altmodes[i] || !altmodes[i]->svid)
+			break;
+		if (altmodes[i]->svid == svid)
+			return altmodes[i];
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(typec_find_altmode);
+
+/* -------------------------------------------------------------------------- */
+
+static int typec_altmode_match(struct device *dev, struct device_driver *driver)
+{
+	struct typec_altmode_driver *drv = to_altmode_driver(driver);
+	struct typec_altmode *altmode = to_altmode(dev);
+
+	return drv->svid == altmode->svid;
+}
+
+static int typec_altmode_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	struct typec_altmode *altmode = to_altmode(dev);
+
+	return add_uevent_var(env, "MODALIAS=svid:%04x", altmode->svid);
+}
+
+static int typec_altmode_probe(struct device *dev)
+{
+	struct typec_altmode_driver *drv = to_altmode_driver(dev->driver);
+	struct typec_altmode *altmode = to_altmode(dev);
+
+	/* Fail if the port does not support the alternate mode */
+	if (!altmode->partner)
+		return -ENODEV;
+
+	return drv->probe(altmode);
+}
+
+static int typec_altmode_remove(struct device *dev)
+{
+	struct typec_altmode_driver *drv = to_altmode_driver(dev->driver);
+
+	if (drv->remove)
+		drv->remove(to_altmode(dev));
+
+	return 0;
+}
+
+struct bus_type typec_altmode_bus = {
+	.name = "typec_altmode",
+	.match = typec_altmode_match,
+	.uevent = typec_altmode_uevent,
+	.probe = typec_altmode_probe,
+	.remove = typec_altmode_remove,
+};
+
+/* -------------------------------------------------------------------------- */
+
+int __typec_altmode_register_driver(struct typec_altmode_driver *drv,
+				    struct module *module)
+{
+	if (!drv->probe)
+		return -EINVAL;
+
+	drv->driver.owner = module;
+	drv->driver.bus = &typec_altmode_bus;
+
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(__typec_altmode_register_driver);
+
+void typec_altmode_unregister_driver(struct typec_altmode_driver *drv)
+{
+	driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(typec_altmode_unregister_driver);
diff --git a/drivers/usb/typec/altmode.h b/drivers/usb/typec/altmode.h
new file mode 100644
index 000000000000..3ef0a46de68f
--- /dev/null
+++ b/drivers/usb/typec/altmode.h
@@ -0,0 +1,46 @@
+#ifndef __USB_TYPEC_ALTMODE_H__
+#define __USB_TYPEC_ALTMODE_H__
+
+#include <linux/device.h>
+#include <linux/usb/typec.h>
+
+struct typec_altmode_ops;
+
+struct typec_mode {
+	int				index;
+	u32				vdo;
+	char				*desc;
+	enum typec_port_type		roles;
+
+	unsigned int			active:1;
+
+	struct typec_altmode		*alt_mode;
+
+	char				group_name[6];
+	struct attribute_group		group;
+	struct attribute		*attrs[5];
+	struct device_attribute		vdo_attr;
+	struct device_attribute		desc_attr;
+	struct device_attribute		active_attr;
+	struct device_attribute		roles_attr;
+};
+
+struct typec_altmode {
+	struct device			dev;
+	u16				svid;
+	int				n_modes;
+
+	struct typec_mode		modes[ALTMODE_MAX_MODES];
+	const struct attribute_group	*mode_groups[ALTMODE_MAX_MODES];
+
+	struct typec_altmode			*partner;
+	struct typec_altmode			*plug[2];
+	const struct typec_altmode_ops		*ops;
+};
+
+#define to_altmode(d) container_of(d, struct typec_altmode, dev)
+
+extern struct bus_type typec_altmode_bus;
+extern const struct device_type typec_altmode_dev_type;
+
+#endif /* __USB_TYPEC_ALTMODE_H__ */
diff --git a/drivers/usb/typec/typec.c b/drivers/usb/typec/class.c
similarity index 93%
rename from drivers/usb/typec/typec.c
rename to drivers/usb/typec/class.c
index 24e355ba109d..019673df152d 100644
--- a/drivers/usb/typec/typec.c
+++ b/drivers/usb/typec/class.c
@@ -15,32 +15,7 @@
 #include <linux/slab.h>
 #include <linux/usb/typec.h>
 
-struct typec_mode {
-	int				index;
-	u32				vdo;
-	char				*desc;
-	enum typec_port_type		roles;
-
-	struct typec_altmode		*alt_mode;
-
-	unsigned int			active:1;
-
-	char				group_name[6];
-	struct attribute_group		group;
-	struct attribute		*attrs[5];
-	struct device_attribute		vdo_attr;
-	struct device_attribute		desc_attr;
-	struct device_attribute		active_attr;
-	struct device_attribute		roles_attr;
-};
-
-struct typec_altmode {
-	struct device			dev;
-	u16				svid;
-	int				n_modes;
-	struct typec_mode		modes[ALTMODE_MAX_MODES];
-	const struct attribute_group	*mode_groups[ALTMODE_MAX_MODES];
-};
+#include "altmode.h"
 
 struct typec_plug {
 	struct device			dev;
@@ -80,7 +55,6 @@ struct typec_port {
 #define to_typec_plug(_dev_) container_of(_dev_, struct typec_plug, dev)
 #define to_typec_cable(_dev_) container_of(_dev_, struct typec_cable, dev)
 #define to_typec_partner(_dev_) container_of(_dev_, struct typec_partner, dev)
-#define to_altmode(_dev_) container_of(_dev_, struct typec_altmode, dev)
 
 static const struct device_type typec_partner_dev_type;
 static const struct device_type typec_cable_dev_type;
@@ -171,6 +145,54 @@ static void typec_report_identity(struct device *dev)
 /* ------------------------------------------------------------------------- */
 /* Alternate Modes */
 
+static int altmode_match(struct device *dev, void *data)
+{
+	struct typec_altmode *partner = data;
+
+	if (dev->type != &typec_altmode_dev_type)
+		return 0;
+
+	return to_altmode(dev)->svid == partner->svid;
+}
+
+static void typec_altmode_get_partner(struct typec_altmode *altmode)
+{
+	struct typec_port *port = typec_altmode2port(altmode);
+	struct typec_altmode *partner;
+	struct device *dev;
+
+	dev = device_find_child(&port->dev, altmode, altmode_match);
+	if (!dev)
+		return;
+
+	partner = to_altmode(dev);
+	altmode->partner = partner;
+
+	if (is_typec_plug(altmode->dev.parent)) {
+		struct typec_plug *plug = to_typec_plug(altmode->dev.parent);
+
+		partner->plug[plug->index] = altmode;
+	} else {
+		partner->partner = altmode;
+	}
+}
+
+static void typec_altmode_put_partner(struct typec_altmode *altmode)
+{
+	if (!altmode->partner)
+		return;
+
+	if (is_typec_plug(altmode->dev.parent)) {
+		struct typec_plug *plug = to_typec_plug(altmode->dev.parent);
+
+		altmode->partner->plug[plug->index] = NULL;
+	} else {
+		altmode->partner->partner = NULL;
+	}
+
+	put_device(&altmode->partner->dev);
+}
+
 /**
  * typec_altmode_update_active - Report Enter/Exit mode
  * @alt: Handle to the alternate mode
@@ -369,12 +391,14 @@ static void typec_altmode_release(struct device *dev)
 	struct typec_altmode *alt = to_altmode(dev);
 	int i;
 
+	typec_altmode_put_partner(alt);
+
 	for (i = 0; i < alt->n_modes; i++)
 		kfree(alt->modes[i].desc);
 	kfree(alt);
 }
 
-static const struct device_type typec_altmode_dev_type = {
+const struct device_type typec_altmode_dev_type = {
 	.name = "typec_alternate_mode",
 	.groups = typec_altmode_groups,
 	.release = typec_altmode_release,
@@ -382,8 +406,9 @@ static const struct device_type typec_altmode_dev_type = {
 
 static struct typec_altmode *
 typec_register_altmode(struct device *parent,
-		       const struct typec_altmode_desc *desc)
+		       const struct typec_altmode_desc *desc, void *priv)
 {
+	bool is_port = is_typec_port(parent);
 	struct typec_altmode *alt;
 	int ret;
 
@@ -393,13 +418,22 @@ typec_register_altmode(struct device *parent,
 
 	alt->svid = desc->svid;
 	alt->n_modes = desc->n_modes;
-	typec_init_modes(alt, desc->modes, is_typec_port(parent));
+	typec_init_modes(alt, desc->modes, is_port);
 
 	alt->dev.parent = parent;
+	alt->dev.driver_data = priv;
 	alt->dev.groups = alt->mode_groups;
 	alt->dev.type = &typec_altmode_dev_type;
 	dev_set_name(&alt->dev, "svid-%04x", alt->svid);
 
+	/* Linking partners and plugs with the ports */
+	if (!is_port)
+		typec_altmode_get_partner(alt);
+
+	/* The partners are bind to drivers */
+	if (is_typec_partner(parent))
+		alt->dev.bus = &typec_altmode_bus;
+
 	ret = device_register(&alt->dev);
 	if (ret) {
 		dev_err(parent, "failed to register alternate mode (%d)\n",
@@ -501,7 +535,7 @@ struct typec_altmode *
 typec_partner_register_altmode(struct typec_partner *partner,
 			       const struct typec_altmode_desc *desc)
 {
-	return typec_register_altmode(&partner->dev, desc);
+	return typec_register_altmode(&partner->dev, desc, NULL);
 }
 EXPORT_SYMBOL_GPL(typec_partner_register_altmode);
 
@@ -596,7 +630,7 @@ struct typec_altmode *
 typec_plug_register_altmode(struct typec_plug *plug,
 			    const struct typec_altmode_desc *desc)
 {
-	return typec_register_altmode(&plug->dev, desc);
+	return typec_register_altmode(&plug->dev, desc, NULL);
 }
 EXPORT_SYMBOL_GPL(typec_plug_register_altmode);
 
@@ -1255,6 +1289,8 @@ EXPORT_SYMBOL_GPL(typec_set_pwr_opmode);
  * typec_port_register_altmode - Register USB Type-C Port Alternate Mode
  * @port: USB Type-C Port that supports the alternate mode
  * @desc: Description of the alternate mode
+ * @ops: Port specific operations for the alternate mode
+ * @drvdata: Private pointer to driver specific info
  *
  * This routine is used to register an alternate mode that @port is capable of
  * supporting.
@@ -1263,9 +1299,19 @@ EXPORT_SYMBOL_GPL(typec_set_pwr_opmode);
  */
 struct typec_altmode *
 typec_port_register_altmode(struct typec_port *port,
-			    const struct typec_altmode_desc *desc)
+			    const struct typec_altmode_desc *desc,
+			    const struct typec_altmode_ops *ops,
+			    void *driver_data)
 {
-	return typec_register_altmode(&port->dev, desc);
+	struct typec_altmode *altmode;
+
+	altmode =  typec_register_altmode(&port->dev, desc, driver_data);
+	if (!altmode)
+		return NULL;
+
+	altmode->ops = ops;
+
+	return altmode;
 }
 EXPORT_SYMBOL_GPL(typec_port_register_altmode);
 
@@ -1351,8 +1397,19 @@ EXPORT_SYMBOL_GPL(typec_unregister_port);
 
 static int __init typec_init(void)
 {
+	int ret;
+
+	ret = bus_register(&typec_altmode_bus);
+	if (ret)
+		return ret;
+
 	typec_class = class_create(THIS_MODULE, "typec");
-	return PTR_ERR_OR_ZERO(typec_class);
+	if (IS_ERR(typec_class)) {
+		bus_unregister(&typec_altmode_bus);
+		return PTR_ERR(typec_class);
+	}
+
+	return 0;
 }
 subsys_initcall(typec_init);
 
@@ -1360,6 +1417,7 @@ static void __exit typec_exit(void)
 {
 	class_destroy(typec_class);
 	ida_destroy(&typec_index_ida);
+	bus_unregister(&typec_altmode_bus);
 }
 module_exit(typec_exit);
 
diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c
index 8483d3e33853..ffc26a3294e6 100644
--- a/drivers/usb/typec/tcpm.c
+++ b/drivers/usb/typec/tcpm.c
@@ -3572,7 +3572,7 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
 		while (paltmode->svid && i < ARRAY_SIZE(port->port_altmode)) {
 			port->port_altmode[i] =
 			  typec_port_register_altmode(port->typec_port,
-						      paltmode);
+						      paltmode, NULL, NULL);
 			if (!port->port_altmode[i]) {
 				tcpm_log(port,
 					 "%s: failed to register port alternate mode 0x%x",
diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h
index ffe7487886ca..3bee4d60d874 100644
--- a/include/linux/usb/typec.h
+++ b/include/linux/usb/typec.h
@@ -12,6 +12,7 @@
 #define USB_TYPEC_REV_1_1	0x110 /* 1.1 */
 #define USB_TYPEC_REV_1_2	0x120 /* 1.2 */
 
+struct typec_altmode_ops;
 struct typec_altmode;
 struct typec_partner;
 struct typec_cable;
@@ -123,7 +124,10 @@ struct typec_altmode
 			     const struct typec_altmode_desc *desc);
 struct typec_altmode
 *typec_port_register_altmode(struct typec_port *port,
-			     const struct typec_altmode_desc *desc);
+			    const struct typec_altmode_desc *desc,
+			    const struct typec_altmode_ops *ops,
+			    void *driver_data);
+
 void typec_unregister_altmode(struct typec_altmode *altmode);
 
 struct typec_port *typec_altmode2port(struct typec_altmode *alt);
diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_altmode.h
new file mode 100644
index 000000000000..4c74cb19bdd3
--- /dev/null
+++ b/include/linux/usb/typec_altmode.h
@@ -0,0 +1,65 @@
+#ifndef __USB_TYPEC_ALTMODE_H
+#define __USB_TYPEC_ALTMODE_H
+
+#include <linux/device.h>
+
+struct typec_altmode;
+
+/**
+ * struct typec_altmode_ops - Alternate mode specific operations vector
+ * @vdm: Process VDM
+ * @notify: Communication channel for platform and the alternate mode
+ */
+struct typec_altmode_ops {
+	void (*vdm)(struct typec_altmode *altmode, u32 hdr, u32 *vdo, int cnt);
+	int (*notify)(struct typec_altmode *altmode,
+		      unsigned long conf, void *data);
+};
+
+int typec_altmode_notify(struct typec_altmode *altmode,
+			 unsigned long conf, void *data);
+int typec_altmode_send_vdm(struct typec_altmode *altmode,
+			   u32 header, u32 *vdo, int count);
+void typec_altmode_set_drvdata(struct typec_altmode *altmode, void *data);
+void *typec_altmode_get_drvdata(struct typec_altmode *altmode);
+
+void typec_altmode_register_ops(struct typec_altmode *altmode,
+				struct typec_altmode_ops *ops);
+struct typec_altmode *typec_altmode_get_plug(struct typec_altmode *altmode,
+					     int index);
+void typec_altmode_put_plug(struct typec_altmode *plug);
+
+struct typec_altmode *typec_find_altmode(struct typec_altmode **altmodes,
+					 size_t n, u16 svid);
+
+/**
+ * struct typec_altmode_driver - USB Type-C alternate mode device driver
+ * @svid: Standard or Vendor ID of the alternate mode
+ * @probe: Callback for device binding
+ * @remove: Callback for device unbinding
+ * @driver: Device driver model driver
+ *
+ * These drivers will be bind to the partner alternate mode devices. They will
+ * handle all SVID specific communication using VDMs (Vendor Defined Messages).
+ */
+struct typec_altmode_driver {
+	const u16 svid;
+	int (*probe)(struct typec_altmode *altmode);
+	void (*remove)(struct typec_altmode *altmode);
+	struct device_driver driver;
+};
+
+#define to_altmode_driver(d) container_of(d, struct typec_altmode_driver, \
+					  driver)
+
+#define typec_altmode_register_driver(drv) \
+		__typec_altmode_register_driver(drv, THIS_MODULE)
+int __typec_altmode_register_driver(struct typec_altmode_driver *drv,
+				    struct module *module);
+void typec_altmode_unregister_driver(struct typec_altmode_driver *drv);
+
+#define module_typec_altmode_driver(__typec_altmode_driver) \
+	module_driver(__typec_altmode_driver, typec_altmode_register_driver, \
+		      typec_altmode_unregister_driver)
+
+#endif /* __USB_TYPEC_ALTMODE_H */
-- 
2.14.1

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



[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux