New media framework user space usage

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

 



Hello Laurent,

my mt9p031 camera project for the omap3530 isp has come to the point
where the ISP registered video[0-6], media0 and v4l-subdev[0-7].

As far as I can see from the names...

cat /sys/class/video4linux/video*/names
OMAP3 ISP CCP2 input
OMAP3 ISP CSI2a output
OMAP3 ISP CCDC output
OMAP3 ISP preview input
OMAP3 ISP preview output
OMAP3 ISP resizer input
OMAP3 ISP resizer output

cat /sys/class/video4linux/v4l-subdev*/names
OMAP3 ISP CCP2
OMAP3 ISP CSI2a
OMAP3 ISP CCDC
OMAP3 ISP preview
OMAP3 ISP resizer
OMAP3 ISP AEWB
OMAP3 ISP AF
OMAP3 ISP histogram

... I want to read /dev/video2 (CCDC).

When I try out a little test program from the V4L2 doc, this line fails:
      ioctl (fd, VIDIOC_G_STD, &std_id)


So far I adopted your mt9t001 driver, merged it with Guennadis
mt9p031. It contains lot of stubs that I want to fill out when I
succeed to make them called inside the kernel.
I looked at your presentation for the media controller and wonder if I
have to set up a pipeline by myself before I can read /dev/video2
(http://linuxtv.org/downloads/presentations/summit_jun_2010/20100614-v4l2_summit-media.pdf).
I failed at the point where I wanted to try out the little snippet on
page 17 as I don't have definitions of the MEDIA_IOC_ENUM_ENTITIES.
Are there somewhere userspace headers available?

int fd;
fd = open(“/dev/media0”, O_RDWR);
while (1) {
 struct media_user_entity entity;
 struct media_user_links links;
 ret = ioctl(fd, MEDIA_IOC_ENUM_ENTITIES, &entity);
 if (ret < 0)
 break;
 while (1) {
 ret = ioctl(fd, MEDIA_IOC_ENUM_LINKS, &links);
 if (ret < 0)
 break;
}

Thanks for help,

 Bastian


APPENDIX A: dmesg

[  103.356018] Linux media interface: v0.10
[  103.356048] device class 'media': registering
[  103.442230] Linux video capture interface: v2.00
[  103.442260] device class 'video4linux': registering
[  103.814239] bus: 'i2c': add driver mt9p031
[  103.894622] bus: 'platform': add driver omap3isp
[  103.933959] address of isp_platform_data in boardconfig: bf065074
[  103.940155] Registering platform device 'omap3isp'. Parent at platform
[  103.940185] device: 'omap3isp': device_add
[  103.940246] bus: 'platform': add device omap3isp
[  103.940490] bus: 'platform': driver_probe_device: matched device
omap3isp with driver omap3isp
[  103.940490] bus: 'platform': really_probe: probing driver omap3isp
with device omap3isp
[  103.940551] address of isp_platform_data bf065074
[  103.954467] omap3isp omap3isp: Revision 2.0 found
[  103.962738] omap-iommu omap-iommu.0: isp: version 1.1
[  103.969879] omap3isp omap3isp: hist: DMA channel = 0
[  103.970001] omap3isp omap3isp: isp_set_xclk(): cam_xclka set to 5760000 Hz
[  103.972229] omap3isp omap3isp: -------------ISP Register dump--------------
[  103.972259] omap3isp omap3isp: ###ISP SYSCONFIG=0x00000001
[  103.972259] omap3isp omap3isp: ###ISP SYSSTATUS=0x00000001
[  103.972290] omap3isp omap3isp: ###ISP IRQ0ENABLE=0x00000000
[  103.972290] omap3isp omap3isp: ###ISP IRQ0STATUS=0x00000000
[  103.972320] omap3isp omap3isp: ###ISP TCTRL_GRESET_LENGTH=0x00000000
[  103.972320] omap3isp omap3isp: ###ISP TCTRL_PSTRB_REPLAY=0x00000000
[  103.972351] omap3isp omap3isp: ###ISP CTRL=0x00200200
[  103.972351] omap3isp omap3isp: ###ISP TCTRL_CTRL=0x0000001e
[  103.972381] omap3isp omap3isp: ###ISP TCTRL_FRAME=0x00000000
[  103.972381] omap3isp omap3isp: ###ISP TCTRL_PSTRB_DELAY=0x00000000
[  103.972412] omap3isp omap3isp: ###ISP TCTRL_STRB_DELAY=0x00000000
[  103.972442] omap3isp omap3isp: ###ISP TCTRL_SHUT_DELAY=0x00000000
[  103.972442] omap3isp omap3isp: ###ISP TCTRL_PSTRB_LENGTH=0x00000000
[  103.972473] omap3isp omap3isp: ###ISP TCTRL_STRB_LENGTH=0x00000000
[  103.972473] omap3isp omap3isp: ###ISP TCTRL_SHUT_LENGTH=0x00000000
[  103.972503] omap3isp omap3isp: ###SBL PCR=0x00000000
[  103.972503] omap3isp omap3isp: ###SBL SDR_REQ_EXP=0x00000000
[  103.972534] omap3isp omap3isp: --------------------------------------------
[  103.974700] device: 'media0': device_add
[  103.975128] device: 'v4l-subdev0': device_add
[  103.975524] device: 'video0': device_add
[  103.975799] device: 'v4l-subdev1': device_add
[  103.976104] device: 'video1': device_add
[  103.976409] device: 'v4l-subdev2': device_add
[  103.976684] device: 'video2': device_add
[  103.976959] device: 'v4l-subdev3': device_add
[  103.977294] device: 'video3': device_add
[  103.977600] device: 'video4': device_add
[  103.977905] device: 'v4l-subdev4': device_add
[  103.978210] device: 'video5': device_add
[  103.978485] device: 'video6': device_add
[  103.978759] device: 'v4l-subdev5': device_add
[  103.979156] device: 'v4l-subdev6': device_add
[  103.979461] device: 'v4l-subdev7': device_add
[  104.752685] device: '2-005d': device_add
[  104.752777] bus: 'i2c': add device 2-005d
[  104.753051] bus: 'i2c': driver_probe_device: matched device 2-005d
with driver mt9p031
[  104.753082] bus: 'i2c': really_probe: probing driver mt9p031 with
device 2-005d
[  104.769897] mt9p031 2-005d: Detected a MT9P031 chip ID 1801
[  104.771881] mt9p031 2-005d: reset succesful
[  104.771911] driver: '2-005d': driver_bound: bound to device 'mt9p031'
[  104.771942] bus: 'i2c': really_probe: bound device 2-005d to driver mt9p031
[  104.772003] driver: 'omap3isp': driver_bound: bound to device 'omap3isp'
[  104.772033] bus: 'platform': really_probe: bound device omap3isp to
driver omap3isp


APPENDIX B: mt9p031.c

#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/log2.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/videodev2.h>

//#include <media/soc_camera.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-subdev.h>

/*
 * mt9p031 i2c address 0x5d (0xBA read, 0xBB write) same as mt9t031
 * The platform has to define i2c_board_info and link to it from
 * struct soc_camera_link
 */

/* mt9p031 selected register addresses */
#define MT9P031_CHIP_VERSION		0x00
#define MT9P031_ROW_START		0x01
#define MT9P031_COLUMN_START		0x02
#define MT9P031_WINDOW_HEIGHT		0x03
#define MT9P031_WINDOW_WIDTH		0x04
#define MT9P031_HORIZONTAL_BLANKING	0x05
#define MT9P031_VERTICAL_BLANKING	0x06
#define MT9P031_OUTPUT_CONTROL		0x07
#define MT9P031_SHUTTER_WIDTH_UPPER	0x08
#define MT9P031_SHUTTER_WIDTH		0x09
#define MT9P031_PIXEL_CLOCK_CONTROL	0x0a
#define MT9P031_FRAME_RESTART		0x0b
#define MT9P031_SHUTTER_DELAY		0x0c
#define MT9P031_RESET			0x0d
#define MT9P031_READ_MODE_1		0x1e
#define MT9P031_READ_MODE_2		0x20
//#define MT9T031_READ_MODE_3		0x21 // NA readmode_2 is 2 bytes
#define MT9P031_ROW_ADDRESS_MODE	0x22
#define MT9P031_COLUMN_ADDRESS_MODE	0x23
#define MT9P031_GLOBAL_GAIN		0x35
//#define MT9T031_CHIP_ENABLE		0xF8 // PDN is pin signal. no i2c register

#define MT9P031_MAX_HEIGHT		1944 // adapted
#define MT9P031_MAX_WIDTH		2592 // adapted
#define MT9P031_MIN_HEIGHT		2  // could be 0
#define MT9P031_MIN_WIDTH		18 // could be 0
#define MT9P031_HORIZONTAL_BLANK	0  // adapted R0x05
#define MT9P031_VERTICAL_BLANK		25 // adapted R0x06
#define MT9P031_COLUMN_SKIP		16 // adapted
#define MT9P031_ROW_SKIP		54 // adapted

#define MT9P031_CHIP_VERSION_VALUE	0x1801

/*
#define MT9T031_BUS_PARAM	(SOCAM_PCLK_SAMPLE_RISING |	\
	SOCAM_PCLK_SAMPLE_FALLING | SOCAM_HSYNC_ACTIVE_HIGH |	\
	SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH |	\
	SOCAM_MASTER | SOCAM_DATAWIDTH_10)
*/
struct mt9p031 {
	struct v4l2_subdev subdev;
        struct media_entity_pad pad;

	struct v4l2_rect rect;	/* Sensor window */
	int model;	/* V4L2_IDENT_MT9P031* codes from v4l2-chip-ident.h */
	u16 xskip;
	u16 yskip;
	unsigned int gain;
	unsigned short y_skip_top;	/* Lines to skip at the top */
	unsigned int exposure;
	unsigned char autoexposure;
};

static struct mt9p031 *to_mt9p031(const struct i2c_client *client)
{
	return container_of(i2c_get_clientdata(client), struct mt9p031, subdev);
}

static int reg_read(struct i2c_client *client, const u8 reg)
{
	s32 data = i2c_smbus_read_word_data(client, reg);
	return data < 0 ? data : swab16(data);
}

static int reg_write(struct i2c_client *client, const u8 reg,
		     const u16 data)
{
	return i2c_smbus_write_word_data(client, reg, swab16(data));
}

static int reg_set(struct i2c_client *client, const u8 reg,
		   const u16 data)
{
	int ret;

	ret = reg_read(client, reg);
	if (ret < 0)
		return ret;
	return reg_write(client, reg, ret | data);
}

static int reg_clear(struct i2c_client *client, const u8 reg,
		     const u16 data)
{
	int ret;

	ret = reg_read(client, reg);
	if (ret < 0)
		return ret;
	return reg_write(client, reg, ret & ~data);
}

static int set_shutter(struct i2c_client *client, const u32 data)
{
	int ret;

	ret = reg_write(client, MT9P031_SHUTTER_WIDTH_UPPER, data >> 16);

	if (ret >= 0)
		ret = reg_write(client, MT9P031_SHUTTER_WIDTH, data & 0xffff);

	return ret;
}

static int get_shutter(struct i2c_client *client, u32 *data)
{
	int ret;

	ret = reg_read(client, MT9P031_SHUTTER_WIDTH_UPPER);
	*data = ret << 16;

	if (ret >= 0)
		ret = reg_read(client, MT9P031_SHUTTER_WIDTH);
	*data |= ret & 0xffff;

	return ret < 0 ? ret : 0;
}

static int mt9p031_idle(struct i2c_client *client)
{
        int ret;

        /* Disable chip output, synchronous option update */
        ret = reg_write(client, MT9P031_RESET, 1);
        if (ret >= 0)
                ret = reg_write(client, MT9P031_RESET, 0);
        if (ret >= 0)
                ret = reg_clear(client, MT9P031_OUTPUT_CONTROL, 2);

        return ret >= 0 ? 0 : -EIO;
}

/////////////////////////////////////////////////////////////////////////////////
STUB
static int mt9t001_enum_mbus_code(struct v4l2_subdev *subdev,
                                  struct v4l2_subdev_fh *fh,
                                  struct v4l2_subdev_pad_mbus_code_enum *code)
{
	printk(KERN_ALERT "pad_op 0\n");
        return 0;
}

static int mt9t001_enum_frame_size(struct v4l2_subdev *subdev,
                                   struct v4l2_subdev_fh *fh,
                                   struct v4l2_subdev_frame_size_enum *fse)
{
	printk(KERN_ALERT "pad_op 1\n");
       	return 0;
}

static int mt9t001_get_format(struct v4l2_subdev *subdev,
                              struct v4l2_subdev_fh *fh, unsigned int pad,
                              struct v4l2_mbus_framefmt *format,
                              enum v4l2_subdev_format which)
{	
	printk(KERN_ALERT "pad_op 4\n");
        return 0;
}

static int mt9t001_set_format(struct v4l2_subdev *subdev,
                              struct v4l2_subdev_fh *fh, unsigned int pad,
                              struct v4l2_mbus_framefmt *format,
                              enum v4l2_subdev_format which)
{	
	printk(KERN_ALERT "pad_op 5\n");
        return 0;
}
static int mt9t001_get_crop(struct v4l2_subdev *subdev,
                            struct v4l2_subdev_fh *fh,
                            struct v4l2_subdev_pad_crop *crop)
{	
	printk(KERN_ALERT "pad_op 6\n");
        return 0;
}

static int mt9t001_set_crop(struct v4l2_subdev *subdev,
                            struct v4l2_subdev_fh *fh,
                            struct v4l2_subdev_pad_crop *crop)
{	
	printk(KERN_ALERT "pad_op 7\n");
        return 0;
}


/////////////////////////////////////////////////////////////////////////////////
END STUB


static int mt9p031_s_stream(struct v4l2_subdev *sd, int enable)
{
	struct i2c_client *client = v4l2_get_subdevdata(sd);
	int ret;

	if (enable)
		/* Switch to master "normal" mode */
		ret = reg_set(client, MT9P031_OUTPUT_CONTROL, 2);
	else
		/* Stop sensor readout */
		ret = reg_clear(client, MT9P031_OUTPUT_CONTROL, 2);

	if (ret < 0)
		return -EIO;

	return 0;
}

enum {
        MT9P031_CTRL_VFLIP,
        MT9P031_CTRL_HFLIP,
        MT9P031_CTRL_GAIN,
        MT9P031_CTRL_EXPOSURE,
        MT9P031_CTRL_EXPOSURE_AUTO,
};

static const struct v4l2_queryctrl mt9p031_controls[] = {
	[MT9P031_CTRL_VFLIP] = {
		.id		= V4L2_CID_VFLIP,
		.type		= V4L2_CTRL_TYPE_BOOLEAN,
		.name		= "Flip Vertically",
		.minimum	= 0,
		.maximum	= 1,
		.step		= 1,
		.default_value	= 0,
	},
	[MT9P031_CTRL_HFLIP] = {
		.id		= V4L2_CID_HFLIP,
		.type		= V4L2_CTRL_TYPE_BOOLEAN,
		.name		= "Flip Horizontally",
		.minimum	= 0,
		.maximum	= 1,
		.step		= 1,
		.default_value	= 0,
	},
	[MT9P031_CTRL_GAIN] = {
		.id		= V4L2_CID_GAIN,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.name		= "Gain",
		.minimum	= 0,
		.maximum	= 127,
		.step		= 1,
		.default_value	= 64,
		.flags		= V4L2_CTRL_FLAG_SLIDER,
	},
	[MT9P031_CTRL_EXPOSURE] = {
		.id		= V4L2_CID_EXPOSURE,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.name		= "Exposure",
		.minimum	= 1,
		.maximum	= 255,
		.step		= 1,
		.default_value	= 255,
		.flags		= V4L2_CTRL_FLAG_SLIDER,
	},
	[MT9P031_CTRL_EXPOSURE_AUTO] = {
		.id		= V4L2_CID_EXPOSURE_AUTO,
		.type		= V4L2_CTRL_TYPE_BOOLEAN,
		.name		= "Automatic Exposure",
		.minimum	= 0,
		.maximum	= 1,
		.step		= 1,
		.default_value	= 1,
	}
};



static int mt9p031_g_chip_ident(struct v4l2_subdev *sd,
				struct v4l2_dbg_chip_ident *id)
{
	struct i2c_client *client = v4l2_get_subdevdata(sd);
	struct mt9p031 *mt9p031 = to_mt9p031(client);

	if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
		return -EINVAL;

	if (id->match.addr != client->addr)
		return -ENODEV;

	id->ident	= mt9p031->model;
	id->revision	= 0;

	return 0;
}

static int mt9p031_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
	struct i2c_client *client = v4l2_get_subdevdata(sd);
	struct mt9p031 *mt9p031 = to_mt9p031(client);
	int data;

	switch (ctrl->id) {
	case V4L2_CID_VFLIP:
		data = reg_read(client, MT9P031_READ_MODE_2);
		if (data < 0)
			return -EIO;
		ctrl->value = !!(data & 0x8000);
		break;
	case V4L2_CID_HFLIP:
		data = reg_read(client, MT9P031_READ_MODE_2);
		if (data < 0)
			return -EIO;
		ctrl->value = !!(data & 0x4000);
		break;
	case V4L2_CID_EXPOSURE_AUTO:
		ctrl->value = mt9p031->autoexposure;
		break;
	case V4L2_CID_GAIN:
		ctrl->value = mt9p031->gain;
		break;
	case V4L2_CID_EXPOSURE:
		ctrl->value = mt9p031->exposure;
		break;
	}
	return 0;
}

static int mt9p031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
	printk(KERN_ALERT "s_ctrl\n");
	return 0;
}


static struct dev_pm_ops mt9p031_dev_pm_ops = {
/*
	.runtime_suspend	= mt9t031_runtime_suspend,
	.runtime_resume		= mt9t031_runtime_resume,
*/
};

static struct device_type mt9p031_dev_type = {
	.name	= "MT9P031",
	.pm	= &mt9p031_dev_pm_ops,
};

/*
 * Interface active, can use i2c. If it fails, it can indeed mean, that
 * this wasn't our capture interface, so, we wait for the right one
 */
static int mt9p031_video_probe(struct i2c_client *client)
{
	struct mt9p031 *mt9p031 = to_mt9p031(client);
	s32 data;
	int ret;

	/* Enable the chip */
	//data = reg_write(client, MT9P031_CHIP_ENABLE, 1);
	//dev_dbg(&client->dev, "write: %d\n", data);

	/* Read out the chip version register */
	data = reg_read(client, MT9P031_CHIP_VERSION);

	switch (data) {
	case MT9P031_CHIP_VERSION_VALUE:
		mt9p031->model = V4L2_IDENT_MT9P031;
		break;
	default:
		dev_err(&client->dev,
			"No MT9P031 chip detected, register read %x\n", data);
		return -ENODEV;
	}

	dev_info(&client->dev, "Detected a MT9P031 chip ID %x\n", data);

	ret = mt9p031_idle(client);
	if (ret < 0)
		dev_err(&client->dev, "Failed to initialise the camera\n");
	else
		dev_info(&client->dev, "reset succesful\n");//vdev->dev.type =
&mt9p031_dev_type;

	/* mt9t031_idle() has reset the chip to default. */
	mt9p031->exposure = 255;
	mt9p031->gain = 64;

	return ret;
}

static int mt9p031_open(struct v4l2_subdev *subdev, u32 i)
{
        printk(KERN_ALERT "mt9p031 open\n");
        return 0;
}
static int mt9p031_query_ctrl(struct v4l2_subdev *subdev,
                              struct v4l2_queryctrl *qc)
{
        return 0;
}



static struct v4l2_subdev_core_ops mt9p031_subdev_core_ops = {
	.g_ctrl		= mt9p031_g_ctrl,
	.s_ctrl		= mt9p031_s_ctrl,
	.g_chip_ident	= mt9p031_g_chip_ident,
	.init           = mt9p031_open,
        .queryctrl      = mt9p031_query_ctrl,

};

static struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = {
	.s_stream	= mt9p031_s_stream,
};


static struct v4l2_subdev_pad_ops mt9p031_subdev_pad_ops = {
        .enum_mbus_code = mt9t001_enum_mbus_code,
        .enum_frame_size = mt9t001_enum_frame_size,
        .get_fmt = mt9t001_get_format,
        .set_fmt = mt9t001_set_format,
        .get_crop = mt9t001_get_crop,
        .set_crop = mt9t001_set_crop,
};


static struct v4l2_subdev_ops mt9p031_subdev_ops = {
	.core	= &mt9p031_subdev_core_ops,
	.video	= &mt9p031_subdev_video_ops,
	.pad	= &mt9p031_subdev_pad_ops,
};

static int mt9p031_probe(struct i2c_client *client,
			 const struct i2c_device_id *did)
{
	struct mt9p031 *mt9p031;
	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
	int ret;



	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
		dev_warn(&adapter->dev,
			 "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
		return -EIO;
	}

	mt9p031 = kzalloc(sizeof(struct mt9p031), GFP_KERNEL);
	if (!mt9p031)
		return -ENOMEM;

	v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops);

//       struct isp_device *isp = v4l2_dev_to_isp_device(subdev->v4l2_dev);
//       isp_set_xclk(isp, 16*1000*1000, ISP_XCLK_A);

	mt9p031->y_skip_top	= 0;
	mt9p031->rect.left	= MT9P031_COLUMN_SKIP;
	mt9p031->rect.top	= MT9P031_ROW_SKIP;
	mt9p031->rect.width	= MT9P031_MAX_WIDTH;
	mt9p031->rect.height	= MT9P031_MAX_HEIGHT;

	/*
	 * Simulated autoexposure. If enabled, we calculate shutter width
	 * ourselves in the driver based on vertical blanking and frame width
	 */
	mt9p031->autoexposure = 1;

	mt9p031->xskip = 1;
	mt9p031->yskip = 1;

	mt9p031_idle(client);

	ret = mt9p031_video_probe(client);

	//mt9p031_disable(client);

	if (ret) {
		kfree(mt9p031);
		return ret;
	}

        mt9p031->pad.type = MEDIA_PAD_TYPE_OUTPUT;
        ret = media_entity_init(&mt9p031->subdev.entity, 1, &mt9p031->pad, 0);
        if (ret) {
                kfree(mt9p031);
                return ret;
        }



	return ret;
}

static int mt9p031_remove(struct i2c_client *client)
{
	struct mt9p031 *mt9p031 = to_mt9p031(client);

	client->driver = NULL;
	kfree(mt9p031);

	return 0;
}

static const struct i2c_device_id mt9p031_id[] = {
	{ "mt9p031", 0 },
	{ }
};
MODULE_DEVICE_TABLE(i2c, mt9p031_id);

static struct i2c_driver mt9p031_i2c_driver = {
	.driver = {
		.name = "mt9p031",
	},
	.probe		= mt9p031_probe,
	.remove		= mt9p031_remove,
	.id_table	= mt9p031_id,
};

static int __init mt9p031_mod_init(void)
{
	return i2c_add_driver(&mt9p031_i2c_driver);
}

static void __exit mt9p031_mod_exit(void)
{
	i2c_del_driver(&mt9p031_i2c_driver);
}

module_init(mt9p031_mod_init);
module_exit(mt9p031_mod_exit);

MODULE_DESCRIPTION("Micron MT9T031 Camera driver");
MODULE_AUTHOR("Guennadi Liakhovetski <lg@xxxxxxx>");
MODULE_LICENSE("GPL v2");
--
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