Re: [RFC/PATCH v4 03/11] media: Entities, pads and links

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

 



Em 20-08-2010 12:29, Laurent Pinchart escreveu:
> As video hardware pipelines become increasingly complex and
> configurable, the current hardware description through v4l2 subdevices
> reaches its limits. In addition to enumerating and configuring
> subdevices, video camera drivers need a way to discover and modify at
> runtime how those subdevices are connected. This is done through new
> elements called entities, pads and links.
> 
> An entity is a basic media hardware building block. It can correspond to
> a large variety of logical blocks such as physical hardware devices
> (CMOS sensor for instance), logical hardware devices (a building block
> in a System-on-Chip image processing pipeline), DMA channels or physical
> connectors.
> 
> A pad is a connection endpoint through which an entity can interact with
> other entities. Data (not restricted to video) produced by an entity
> flows from the entity's output to one or more entity inputs. Pads should
> not be confused with physical pins at chip boundaries.
> 
> A link is a point-to-point oriented connection between two pads, either
> on the same entity or on different entities. Data flows from a source
> pad to a sink pad.
> 
> Links are stored in the source entity. To make backwards graph walk
> faster, a copy of all links is also stored in the sink entity. The copy
> is known as a backlink and is only used to help graph traversal.
> 
> The entity API is made of three functions:
> 
> - media_entity_init() initializes an entity. The caller must provide an
> array of pads as well as an estimated number of links. The links array
> is allocated dynamically and will be reallocated if it grows beyond the
> initial estimate.
> 
> - media_entity_cleanup() frees resources allocated for an entity. It
> must be called during the cleanup phase after unregistering the entity
> and before freeing it.
> 
> - media_entity_create_link() creates a link between two entities. An
> entry in the link array of each entity is allocated and stores pointers
> to source and sink pads.
> 
> When a media device is unregistered, all its entities are unregistered
> automatically.
> 
> The code is based on Hans Verkuil <hverkuil@xxxxxxxxx> initial work.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@xxxxxxxxxxxxxxxx>
> Signed-off-by: Sakari Ailus <sakari.ailus@xxxxxxxxxxxxxxxxxxxxxxxxxx>
> ---
>  Documentation/media-framework.txt |  149 +++++++++++++++++++++++++++++++++++++
>  drivers/media/Makefile            |    2 +-
>  drivers/media/media-device.c      |   53 +++++++++++++
>  drivers/media/media-entity.c      |  145 ++++++++++++++++++++++++++++++++++++
>  include/media/media-device.h      |   19 +++++
>  include/media/media-entity.h      |   96 ++++++++++++++++++++++++
>  6 files changed, 463 insertions(+), 1 deletions(-)
>  create mode 100644 drivers/media/media-entity.c
>  create mode 100644 include/media/media-entity.h
> 
> diff --git a/Documentation/media-framework.txt b/Documentation/media-framework.txt
> index 89dc7ad..35d74e4 100644
> --- a/Documentation/media-framework.txt
> +++ b/Documentation/media-framework.txt
> @@ -35,6 +35,30 @@ belong to userspace.
>  The media kernel API aims at solving those problems.
>  
>  
> +Abstract media device model
> +---------------------------
> +
> +Discovering a device internal topology, and configuring it at runtime, is one
> +of the goals of the media framework. To achieve this, hardware devices are
> +modeled as an oriented graph of building blocks called entities connected
> +through pads.
> +
> +An entity is a basic media hardware building block. It can correspond to
> +a large variety of logical blocks such as physical hardware devices
> +(CMOS sensor for instance), logical hardware devices (a building block
> +in a System-on-Chip image processing pipeline), DMA channels or physical
> +connectors.
> +
> +A pad is a connection endpoint through which an entity can interact with
> +other entities. Data (not restricted to video) produced by an entity
> +flows from the entity's output to one or more entity inputs. Pads should
> +not be confused with physical pins at chip boundaries.
> +
> +A link is a point-to-point oriented connection between two pads, either
> +on the same entity or on different entities. Data flows from a source
> +pad to a sink pad.
> +
> +
>  Media device
>  ------------
>  
> @@ -89,3 +113,128 @@ Drivers unregister media device instances by calling
>  
>  Unregistering a media device that hasn't been registered is *NOT* safe.
>  
> +
> +Entities, pads and links
> +------------------------
> +
> +- Entities
> +
> +Entities are represented by a struct media_entity instance, defined in
> +include/media/media-entity.h. The structure is usually embedded into a
> +higher-level structure, such as a v4l2_subdev or video_device instance,
> +although drivers can allocate entities directly.
> +
> +Drivers initialize entities by calling
> +
> +	media_entity_init(struct media_entity *entity, u16 num_pads,
> +			  struct media_pad *pads, u16 extra_links);
> +
> +The media_entity name, type, flags, revision and group_id fields can be
> +initialized before or after calling media_entity_init. Entities embedded in
> +higher-level standard structures can have some of those fields set by the
> +higher-level framework.
> +
> +As the number of pads is known in advance, the pads array is not allocated
> +dynamically but is managed by the entity driver. Most drivers will embed the
> +pads array in a driver-specific structure, avoiding dynamic allocation.
> +
> +Drivers must set the direction of every pad in the pads array before calling
> +media_entity_init. The function will initialize the other pads fields.
> +
> +Unlike the number of pads, the total number of links isn't always known in
> +advance by the entity driver. As an initial estimate, media_entity_init
> +pre-allocates a number of links equal to the number of pads plus an optional
> +number of extra links. The links array will be reallocated if it grows beyond
> +the initial estimate.
> +
> +Drivers register entities with a media device by calling
> +
> +	media_device_register_entity(struct media_device *mdev,
> +				     struct media_entity *entity);
> +
> +When registered the entity is assigned an ID. Entity IDs are positive integers
> +and are guaranteed to be unique in the context of the media device. The
> +framework doesn't guarantee that IDs will always be continuous.
> +
> +Drivers unregister entities by calling
> +
> +	media_device_unregister_entity(struct media_entity *entity);
> +
> +Unregistering an entity will not change the IDs of the other entities, and the
> +ID will never be reused for a newly registered entity.
> +
> +When a media device is unregistered, all its entities are unregistered
> +automatically. No manual entities unregistration is then required.
> +
> +Drivers free resources associated with an entity by calling
> +
> +	media_entity_cleanup(struct media_entity *entity);
> +
> +This function must be called during the cleanup phase after unregistering the
> +entity. Note that the media_entity instance itself must be freed explicitly by
> +the driver if required.
> +
> +Entities have flags that describe the entity capabilities and state.
> +
> +	MEDIA_ENTITY_FLAG_DEFAULT indicates the default entity for a given
> +	type. This can be used to report the default audio and video devices
> +	or the default camera sensor.
> +
> +Logical entity groups can be defined by setting the group ID of all member
> +entities to the same non-zero value. An entity group serves no purpose in the
> +kernel, but is reported to userspace during entities enumeration. The group_id
> +field belongs to the media device driver and must not by touched by entity
> +drivers.
> +
> +Media device drivers should define groups if several entities are logically
> +bound together. Example usages include reporting
> +
> +	- ALSA, VBI and video nodes that carry the same media stream
> +	- lens and flash controllers associated with a sensor
> +
> +- Pads
> +
> +Pads are represented by a struct media_pad instance, defined in
> +include/media/media-entity.h. Each entity stores its pads in a pads array
> +managed by the entity driver. Drivers usually embed the array in a
> +driver-specific structure.
> +
> +Pads are identified by their entity and their 0-based index in the pads array.
> +Both information are stored in the media_pad structure, making the media_pad
> +pointer the canonical way to store and pass link references.
> +
> +Pads have flags that describe the pad capabilities and state.
> +
> +	MEDIA_PAD_FLAG_INPUT indicates that the pad supports sinking data.
> +	MEDIA_PAD_FLAG_OUTPUT indicates that the pad supports sourcing data.
> +
> +One and only one of MEDIA_PAD_FLAG_INPUT and MEDIA_PAD_FLAG_OUTPUT must be set
> +for each pad.
> +
> +- Links
> +
> +Links are represented by a struct media_link instance, defined in
> +include/media/media-entity.h. Each entity stores all links originating at or
> +targetting any of its pads in a links array. A given link is thus stored
> +twice, once in the source entity and once in the target entity. The array is
> +pre-allocated and grows dynamically as needed.
> +
> +Drivers create links by calling
> +
> +	media_entity_create_link(struct media_entity *source, u16 source_pad,
> +				 struct media_entity *sink,   u16 sink_pad,
> +				 u32 flags);
> +
> +An entry in the link array of each entity is allocated and stores pointers
> +to source and sink pads.
> +
> +Links have flags that describe the link capabilities and state.
> +
> +	MEDIA_LINK_FLAG_ACTIVE indicates that the link is active and can be
> +	used to transfer media data. When two or more links target a sink pad,
> +	only one of them can be active at a time.
> +	MEDIA_LINK_FLAG_IMMUTABLE indicates that the link active state can't
> +	be modified at runtime. If MEDIA_LINK_FLAG_IMMUTABLE is set, then
> +	MEDIA_LINK_FLAG_ACTIVE must also be set since an immutable link is
> +	always active.
> +
> diff --git a/drivers/media/Makefile b/drivers/media/Makefile
> index f8d8dcb..a425581 100644
> --- a/drivers/media/Makefile
> +++ b/drivers/media/Makefile
> @@ -2,7 +2,7 @@
>  # Makefile for the kernel multimedia device drivers.
>  #
>  
> -media-objs	:= media-device.o media-devnode.o
> +media-objs	:= media-device.o media-devnode.o media-entity.o
>  
>  obj-$(CONFIG_MEDIA_SUPPORT)	+= media.o
>  
> diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
> index 781c641..eeb002e 100644
> --- a/drivers/media/media-device.c
> +++ b/drivers/media/media-device.c
> @@ -23,6 +23,7 @@
>  
>  #include <media/media-device.h>
>  #include <media/media-devnode.h>
> +#include <media/media-entity.h>
>  
>  static const struct media_file_operations media_device_fops = {
>  	.owner = THIS_MODULE,
> @@ -67,6 +68,10 @@ int __must_check media_device_register(struct media_device *mdev)
>  	if (WARN_ON(mdev->dev == NULL || mdev->model[0] == 0))
>  		return 0;
>  
> +	mdev->entity_id = 1;
> +	INIT_LIST_HEAD(&mdev->entities);
> +	spin_lock_init(&mdev->lock);
> +
>  	/* Register the device node. */
>  	mdev->devnode.fops = &media_device_fops;
>  	mdev->devnode.parent = mdev->dev;
> @@ -92,7 +97,55 @@ EXPORT_SYMBOL_GPL(media_device_register);
>   */
>  void media_device_unregister(struct media_device *mdev)
>  {
> +	struct media_entity *entity;
> +	struct media_entity *next;
> +
> +	list_for_each_entry_safe(entity, next, &mdev->entities, list)
> +		media_device_unregister_entity(entity);
> +
>  	device_remove_file(&mdev->devnode.dev, &dev_attr_model);
>  	media_devnode_unregister(&mdev->devnode);
>  }
>  EXPORT_SYMBOL_GPL(media_device_unregister);
> +
> +/**
> + * media_device_register_entity - Register an entity with a media device
> + * @mdev:	The media device
> + * @entity:	The entity
> + */
> +int __must_check media_device_register_entity(struct media_device *mdev,
> +					      struct media_entity *entity)
> +{
> +	/* Warn if we apparently re-register an entity */
> +	WARN_ON(entity->parent != NULL);

Instead, it should just return -EINVAL and use __must_check. 

What's the sense of allowing registering it twice?

> +	entity->parent = mdev;
> +
> +	spin_lock(&mdev->lock);
> +	entity->id = mdev->entity_id++;
> +	list_add_tail(&entity->list, &mdev->entities);
> +	spin_unlock(&mdev->lock);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(media_device_register_entity);
> +
> +/**
> + * media_device_unregister_entity - Unregister an entity
> + * @entity:	The entity
> + *
> + * If the entity has never been registered this function will return
> + * immediately.
> + */
> +void media_device_unregister_entity(struct media_entity *entity)
> +{
> +	struct media_device *mdev = entity->parent;
> +
> +	if (mdev == NULL)
> +		return;
> +
> +	spin_lock(&mdev->lock);
> +	list_del(&entity->list);
> +	spin_unlock(&mdev->lock);
> +	entity->parent = NULL;
> +}
> +EXPORT_SYMBOL_GPL(media_device_unregister_entity);
> diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c
> new file mode 100644
> index 0000000..541063b
> --- /dev/null
> +++ b/drivers/media/media-entity.c
> @@ -0,0 +1,145 @@
> +/*
> + *  Media Entity support
> + *
> + *  Copyright (C) 2010 Laurent Pinchart <laurent.pinchart@xxxxxxxxxxxxxxxx>
> + *
> + *  This program is free software; you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation; either version 2 of the License, or
> + *  (at your option) any later version.
> + *
> + *  This program is distributed in the hope that it will be useful,
> + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *  GNU General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with this program; if not, write to the Free Software
> + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <media/media-entity.h>
> +
> +/**
> + * media_entity_init - Initialize a media entity
> + *
> + * @num_pads: Total number of input and output pads.
> + * @extra_links: Initial estimate of the number of extra links.
> + * @pads: Array of 'num_pads' pads.
> + *
> + * The total number of pads is an intrinsic property of entities known by the
> + * entity driver, while the total number of links depends on hardware design
> + * and is an extrinsic property unknown to the entity driver. However, in most
> + * use cases the entity driver can guess the number of links which can safely
> + * be assumed to be equal to or larger than the number of pads.
> + *
> + * For those reasons the links array can be preallocated based on the entity
> + * driver guess and will be reallocated later if extra links need to be
> + * created.
> + *
> + * This function allocates a links array with enough space to hold at least
> + * 'num_pads' + 'extra_links' elements. The media_entity::max_links field will
> + * be set to the number of allocated elements.
> + *
> + * The pads array is managed by the entity driver and passed to
> + * media_entity_init() where its pointer will be stored in the entity structure.
> + */
> +int
> +media_entity_init(struct media_entity *entity, u16 num_pads,
> +		  struct media_pad *pads, u16 extra_links)
> +{
> +	struct media_link *links;
> +	unsigned int max_links = num_pads + extra_links;
> +	unsigned int i;
> +
> +	links = kzalloc(max_links * sizeof(links[0]), GFP_KERNEL);
> +	if (links == NULL)
> +		return -ENOMEM;
> +
> +	entity->group_id = 0;
> +	entity->max_links = max_links;
> +	entity->num_links = 0;
> +	entity->num_backlinks = 0;
> +	entity->num_pads = num_pads;
> +	entity->pads = pads;
> +	entity->links = links;
> +
> +	for (i = 0; i < num_pads; i++) {
> +		pads[i].entity = entity;
> +		pads[i].index = i;
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(media_entity_init);

Just use EXPORT_SYMBOL_GPL() for all symbols that needs to be exported.

> +
> +void
> +media_entity_cleanup(struct media_entity *entity)
> +{
> +	kfree(entity->links);
> +}
> +EXPORT_SYMBOL(media_entity_cleanup);
> +
> +static struct media_link *media_entity_add_link(struct media_entity *entity)
> +{
> +	if (entity->num_links >= entity->max_links) {
> +		struct media_link *links = entity->links;
> +		unsigned int max_links = entity->max_links + 2;
> +		unsigned int i;
> +
> +		links = krealloc(links, max_links * sizeof(*links), GFP_KERNEL);
> +		if (links == NULL)
> +			return NULL;
> +
> +		for (i = 0; i < entity->num_links; i++)
> +			links[i].reverse->reverse = &links[i];
> +
> +		entity->max_links = max_links;
> +		entity->links = links;
> +	}
> +
> +	return &entity->links[entity->num_links++];
> +}
> +
> +int
> +media_entity_create_link(struct media_entity *source, u16 source_pad,
> +			 struct media_entity *sink, u16 sink_pad, u32 flags)
> +{
> +	struct media_link *link;
> +	struct media_link *backlink;
> +
> +	BUG_ON(source == NULL || sink == NULL);
> +	BUG_ON(source_pad >= source->num_pads);
> +	BUG_ON(sink_pad >= sink->num_pads);
> +
> +	link = media_entity_add_link(source);
> +	if (link == NULL)
> +		return -ENOMEM;
> +
> +	link->source = &source->pads[source_pad];
> +	link->sink = &sink->pads[sink_pad];
> +	link->flags = flags;
> +
> +	/* Create the backlink. Backlinks are used to help graph traversal and
> +	 * are not reported to userspace.
> +	 */
> +	backlink = media_entity_add_link(sink);
> +	if (backlink == NULL) {
> +		source->num_links--;
> +		return -ENOMEM;
> +	}
> +
> +	backlink->source = &source->pads[source_pad];
> +	backlink->sink = &sink->pads[sink_pad];
> +	backlink->flags = flags;
> +
> +	link->reverse = backlink;
> +	backlink->reverse = link;
> +
> +	sink->num_backlinks++;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(media_entity_create_link);
> diff --git a/include/media/media-device.h b/include/media/media-device.h
> index 4fe949e..7e2dac2 100644
> --- a/include/media/media-device.h
> +++ b/include/media/media-device.h
> @@ -23,8 +23,10 @@
>  
>  #include <linux/device.h>
>  #include <linux/list.h>
> +#include <linux/spinlock.h>
>  
>  #include <media/media-devnode.h>
> +#include <media/media-entity.h>
>  
>  /**
>   * struct media_device - Media device
> @@ -35,6 +37,9 @@
>   * @bus_info:	Unique and stable device location identifier
>   * @device_version: Hardware device version
>   * @driver_version: Device driver version
> + * @entity_id:	ID of the next entity to be registered
> + * @entities:	List of registered entities
> + * @lock:	Entities list lock
>   *
>   * This structure represents an abstract high-level media device. It allows easy
>   * access to entities and provides basic media device-level support. The
> @@ -56,9 +61,23 @@ struct media_device {
>  	u8 bus_info[32];
>  	u32 device_version;
>  	u32 driver_version;
> +
> +	u32 entity_id;
> +	struct list_head entities;
> +
> +	/* Protects the entities list */
> +	spinlock_t lock;
>  };
>  
>  int __must_check media_device_register(struct media_device *mdev);
>  void media_device_unregister(struct media_device *mdev);
>  
> +int __must_check media_device_register_entity(struct media_device *mdev,
> +					      struct media_entity *entity);
> +void media_device_unregister_entity(struct media_entity *entity);
> +
> +/* Iterate over all entities. */
> +#define media_device_for_each_entity(entity, mdev)			\
> +	list_for_each_entry(entity, &(mdev)->entities, list)
> +
>  #endif
> diff --git a/include/media/media-entity.h b/include/media/media-entity.h
> new file mode 100644
> index 0000000..32bb20a
> --- /dev/null
> +++ b/include/media/media-entity.h
> @@ -0,0 +1,96 @@
> +#ifndef _MEDIA_ENTITY_H
> +#define _MEDIA_ENTITY_H
> +
> +#include <linux/list.h>
> +
> +#define MEDIA_ENTITY_TYPE_SHIFT			16
> +#define MEDIA_ENTITY_TYPE_MASK			0x00ff0000
> +#define MEDIA_ENTITY_SUBTYPE_MASK		0x0000ffff
> +
> +#define MEDIA_ENTITY_TYPE_NODE			(1 << MEDIA_ENTITY_TYPE_SHIFT)
> +#define MEDIA_ENTITY_TYPE_NODE_V4L		(MEDIA_ENTITY_TYPE_NODE + 1)
> +#define MEDIA_ENTITY_TYPE_NODE_FB		(MEDIA_ENTITY_TYPE_NODE + 2)
> +#define MEDIA_ENTITY_TYPE_NODE_ALSA		(MEDIA_ENTITY_TYPE_NODE + 3)
> +#define MEDIA_ENTITY_TYPE_NODE_DVB		(MEDIA_ENTITY_TYPE_NODE + 4)
> +
> +#define MEDIA_ENTITY_TYPE_SUBDEV		(2 << MEDIA_ENTITY_TYPE_SHIFT)
> +#define MEDIA_ENTITY_TYPE_SUBDEV_SENSOR		(MEDIA_ENTITY_TYPE_SUBDEV + 1)
> +#define MEDIA_ENTITY_TYPE_SUBDEV_FLASH		(MEDIA_ENTITY_TYPE_SUBDEV + 2)
> +#define MEDIA_ENTITY_TYPE_SUBDEV_LENS		(MEDIA_ENTITY_TYPE_SUBDEV + 3)
> +
> +#define MEDIA_ENTITY_FLAG_DEFAULT		(1 << 0)
> +
> +#define MEDIA_LINK_FLAG_ACTIVE			(1 << 0)
> +#define MEDIA_LINK_FLAG_IMMUTABLE		(1 << 1)
> +
> +#define MEDIA_PAD_FLAG_INPUT			(1 << 0)
> +#define MEDIA_PAD_FLAG_OUTPUT			(1 << 1)
> +
> +struct media_link {
> +	struct media_pad *source;	/* Source pad */
> +	struct media_pad *sink;		/* Sink pad  */
> +	struct media_link *reverse;	/* Link in the reverse direction */
> +	unsigned long flags;		/* Link flags (MEDIA_LINK_FLAG_*) */
> +};
> +
> +struct media_pad {
> +	struct media_entity *entity;	/* Entity this pad belongs to */
> +	u16 index;			/* Pad index in the entity pads array */
> +	unsigned long flags;		/* Pad flags (MEDIA_PAD_FLAG_*) */
> +};
> +
> +struct media_entity {
> +	struct list_head list;
> +	struct media_device *parent;	/* Media device this entity belongs to*/
> +	u32 id;				/* Entity ID, unique in the parent media
> +					 * device context */
> +	const char *name;		/* Entity name */
> +	u32 type;			/* Entity type (MEDIA_ENTITY_TYPE_*) */
> +	u32 revision;			/* Entity revision, driver specific */
> +	unsigned long flags;		/* Entity flags (MEDIA_ENTITY_FLAG_*) */
> +	u32 group_id;			/* Entity group ID */
> +
> +	u16 num_pads;			/* Number of input and output pads */
> +	u16 num_links;			/* Number of existing links, both active
> +					 * and inactive */
> +	u16 num_backlinks;		/* Number of backlinks */
> +	u16 max_links;			/* Maximum number of links */
> +
> +	struct media_pad *pads;		/* Pads array (num_pads elements) */
> +	struct media_link *links;	/* Links array (max_links elements)*/
> +
> +	union {
> +		/* Node specifications */
> +		struct {
> +			u32 major;
> +			u32 minor;
> +		} v4l;
> +		struct {
> +			u32 major;
> +			u32 minor;
> +		} fb;
> +		int alsa;
> +		int dvb;
> +
> +		/* Sub-device specifications */
> +		/* Nothing needed yet */
> +	};
> +};
> +
> +static inline u32 media_entity_type(struct media_entity *entity)
> +{
> +	return entity->type & MEDIA_ENTITY_TYPE_MASK;
> +}
> +
> +static inline u32 media_entity_subtype(struct media_entity *entity)
> +{
> +	return entity->type & MEDIA_ENTITY_SUBTYPE_MASK;
> +}
> +
> +int media_entity_init(struct media_entity *entity, u16 num_pads,
> +		struct media_pad *pads, u16 extra_links);
> +void media_entity_cleanup(struct media_entity *entity);
> +int media_entity_create_link(struct media_entity *source, u16 source_pad,
> +		struct media_entity *sink, u16 sink_pad, u32 flags);
> +
> +#endif

--
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