Re: Anubis Electronics "Lifeview"(0x10fd:0x1513) (was: msi digivox mini ii)

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

 



Michael Krufky schrieb:
> Pierre Willenbrock wrote:
>> Hi list,
>>
>> I am owner of a "MSI DIGIVOX mini-II". I got it to work using the
>> attached patch and firmware. The patch and firmware are the result of
>> analyzing some usb logs from windows.
>>
>> The patch breaks all users of tda10046, as i don't understand how that
>> chip is supposed to work. The same goes for my driver implementation of
>> the Philips 8275a.
>>
>> So this mess needs to be fixed before it can go into the repository.
>>
>> The patch is against a fresh hg checkout from
>> http://linuxtv.org/hg/v4l-dvb at 2007-02-22 21:00 UTC.
>>
>> Regards,
>>   Pierre
> 
> Pierre-
> 
> I am very happy to hear that you got this device working...  Interestingly
> enough, we have already created a new tda827x dvb fe module, which might be
> better for your device...  This new tda827x module has not yet been merged into
> the master v4l-dvb repository, but it will be soon.  Could you try to use the
> code located in:
> 
> http://linuxtv.org/hg/~hhackmann/v4l-dvb
> 
> The tda827x module will be able to detect the difference between the tda8275 and
> the tda8275a ...  You do not have to fill the callback functions in the config
> struct -- that is really meant as a hack for some required GPIO handling in the
> saa7134-dvb driver for input switching.
> 
> If you can generate a new patch against the repository above, it would make it
> _much_ easier to integrate your patch into the sources.   After you get that
> done, we can work out the tda1004x differences.
> 
> You might also want to speak to aett and friedrich, regulars of the #linuxtv irc
> chat room on irc.freenode.net ... aet is the author of the m920x driver, and
> friedrich has the same device that you have.  They have been working on it, but
> haven't yet gotten successful results.
> 
> Good work!  Hopefully we can clean this up after you generate a new patch using
> the tda827x module from hhackmann's repository.
> 
> Regards,
> 
> Mike Krufky
> 

Hi Mike and Hartmut,

this time, the patch does not change tda827x.c at all. I fiddled with
the PHY2 value in tda1004x.c and found it to be related to the IF(there
seems to be some factor between the IF and PHY2 introduced somewhere
else). This leaves some differences in tda1004x.c. I don't know what to
do with these, so i would be glad to get any hints.

Regards,
  Pierre
diff -r 72b6c2479692 linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
--- a/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h	Thu Mar 01 10:16:13 2007 -0200
+++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h	Thu Mar 01 22:34:18 2007 +0100
@@ -37,6 +37,7 @@
 #define USB_VID_ULTIMA_ELECTRONIC		0x05d8
 #define USB_VID_UNIWILL				0x1584
 #define USB_VID_WIDEVIEW			0x14aa
+#define USB_VID_ANUBIS_ELECTRONIC		0x10fd
 
 /* Product IDs */
 #define USB_PID_ADSTECH_USB2_COLD			0xa333
@@ -140,6 +141,7 @@
 #define USB_PID_GENPIX_8PSK_COLD			0x0200
 #define USB_PID_GENPIX_8PSK_WARM			0x0201
 #define USB_PID_SIGMATEK_DVB_110			0x6610
+#define USB_PID_ANUBIS_ELECTRONIC_MSI_DIGI_VOX_MINI_II  0x1513
 
 
 #endif
diff -r 72b6c2479692 linux/drivers/media/dvb/dvb-usb/m920x.c
--- a/linux/drivers/media/dvb/dvb-usb/m920x.c	Thu Mar 01 10:16:13 2007 -0200
+++ b/linux/drivers/media/dvb/dvb-usb/m920x.c	Thu Mar 01 22:54:06 2007 +0100
@@ -14,6 +14,8 @@
 #include "mt352.h"
 #include "mt352_priv.h"
 #include "qt1010.h"
+#include "tda1004x.h"
+#include "tda827x.h"
 
 /* debug */
 static int dvb_usb_m920x_debug;
@@ -44,14 +46,25 @@ static inline int m9206_read(struct usb_
 {
 	int ret;
 
+	deb_rc("m9206_read(..., %x, %x, %x, %p, %x)\n",
+	       request, value, index, data, size);
+
 	ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
 			      request, USB_TYPE_VENDOR | USB_DIR_IN,
 			      value, index, data, size, 2000);
-	if (ret < 0)
-		return ret;
-
-	if (ret != size)
+	if (ret < 0) {
+		printk(KERN_INFO "m9206_read = error: %d\n", ret);
+		return ret;
+	}
+
+	if (ret != size) {
+		deb_rc("m9206_read = no data\n");
 		return -EIO;
+	}
+	
+
+	deb_rc("m9206_read = %x\n",
+	       ((unsigned char*)data)[0]);
 
 	return 0;
 }
@@ -60,10 +73,17 @@ static inline int m9206_write(struct usb
 			      u16 value, u16 index)
 {
 	int ret;
+
+	deb_rc("m9206_write(..., %x, %x, %x)=...\n",
+	       request, value, index);
 
 	ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
 			      request, USB_TYPE_VENDOR | USB_DIR_OUT,
 			      value, index, NULL, 0, 2000);
+
+	deb_rc("m9206_write...=%d\n",
+	       ret);
+	
 	return ret;
 }
 
@@ -144,52 +164,78 @@ static int m9206_i2c_xfer(struct i2c_ada
 			  int num)
 {
 	struct dvb_usb_device *d = i2c_get_adapdata(adap);
-	struct m9206_state *m = d->priv;
-	int i;
+	int i, j;
 	int ret = 0;
 
 	if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
 		return -EAGAIN;
 
-	if (num > 2)
-		return -EINVAL;
+/*
+sequences found in logs:
+[index value]
+0x80 write addr
+(0x00 out byte)*
+0x40 out byte
+
+0x80 write addr
+(0x00 out byte)*
+0x80 read addr
+(0x21 in byte)*
+0x60 in byte
+
+this sequence works:
+0x80 read addr
+(0x21 in byte)*
+0x60 in byte
+
+_my guess_:
+0x80: begin i2c transfer using address. value=address<<1|(reading?1:0)
+0x00: write byte
+0x21: read byte, more to follow
+0x40: write last byte of message sequence
+0x60: read last byte of message sequence
+
+bits:
+0x80: start clock before byte
+0x40: stop clock after byte
+0x20: receive byte
+0x01: send ack
+ */
+
 
 	for (i = 0; i < num; i++) {
-		if ((ret = m9206_write(d->udev, M9206_I2C, msg[i].addr, 0x80)) != 0)
-			goto unlock;
-
-		if ((ret = m9206_write(d->udev, M9206_I2C, msg[i].buf[0], 0x0)) != 0)
-			goto unlock;
-
-		if (i + 1 < num && msg[i + 1].flags & I2C_M_RD) {
-			int i2c_i;
-
-			for (i2c_i = 0; i2c_i < M9206_I2C_MAX; i2c_i++)
-				if (msg[i].addr == m->i2c_r[i2c_i].addr)
-					break;
-
-			if (i2c_i >= M9206_I2C_MAX) {
-				deb_rc("No magic for i2c addr!\n");
-				ret = -EINVAL;
+		if (msg[i].flags & I2C_M_RD) {
+
+			if ((ret = m9206_write(d->udev, M9206_I2C, (msg[i].addr << 1) | 0x01, 0x80)) != 0)
 				goto unlock;
+
+			for(j = 0; j < msg[i].len; j++) {
+				if (j + 1 == msg[i].len && i + 1== num) {
+					if ((ret = m9206_read(d->udev, M9206_I2C, 0x0, 0x60, &msg[i].buf[j], msg[i].len)) != 0)
+						goto unlock;
+				} else {
+					if ((ret = m9206_read(d->udev, M9206_I2C, 0x0, 0x21, &msg[i].buf[j], msg[i].len)) != 0)
+						goto unlock;
+				}
 			}
 
-			if ((ret = m9206_write(d->udev, M9206_I2C, m->i2c_r[i2c_i].magic, 0x80)) != 0)
+		} else {
+			if ((ret = m9206_write(d->udev, M9206_I2C, (msg[i].addr << 1), 0x80)) != 0)
 				goto unlock;
-
-			if ((ret = m9206_read(d->udev, M9206_I2C, 0x0, 0x60, msg[i + 1].buf, msg[i + 1].len)) != 0)
-				goto unlock;
-
-			i++;
-		} else {
-			if (msg[i].len != 2)
-				return -EINVAL;
-
-			if ((ret = m9206_write(d->udev, M9206_I2C, msg[i].buf[1], 0x40)) != 0)
-				goto unlock;
-		}
-	}
-	ret = i;
+		
+			for(j = 0; j < msg[i].len; j++) {
+				if (j + 1 == msg[i].len && i + 1== num) {
+					if ((ret = m9206_write(d->udev, M9206_I2C, msg[i].buf[j], 0x40)) != 0)
+						goto unlock;
+					
+				} else {
+					if ((ret = m9206_write(d->udev, M9206_I2C, msg[i].buf[j], 0x0)) != 0)
+						goto unlock;
+				}
+			}
+		}
+	}
+	ret = num;
 	unlock:
 	mutex_unlock(&d->i2c_mutex);
 
@@ -331,6 +377,7 @@ static int m9206_firmware_download(struc
 			i += size;
 		}
 		if (i != fw->size) {
+			deb_rc("bad firmware file!\n");
 			ret = -EINVAL;
 			goto done;
 		}
@@ -388,19 +435,14 @@ static int megasky_mt352_demod_init(stru
 }
 
 static struct mt352_config megasky_mt352_config = {
-	.demod_address = 0x1e,
+	.demod_address = 0x0f,
 	.no_tuner = 1,
 	.demod_init = megasky_mt352_demod_init,
 };
 
 static int megasky_mt352_frontend_attach(struct dvb_usb_adapter *adap)
 {
-	struct m9206_state *m = adap->dev->priv;
-
 	deb_rc("megasky_frontend_attach!\n");
-
-	m->i2c_r[M9206_I2C_DEMOD].addr = megasky_mt352_config.demod_address;
-	m->i2c_r[M9206_I2C_DEMOD].magic = 0x1f;
 
 	if ((adap->fe = dvb_attach(mt352_attach, &megasky_mt352_config, &adap->dev->i2c_adap)) == NULL)
 		return -EIO;
@@ -409,16 +451,11 @@ static int megasky_mt352_frontend_attach
 }
 
 static struct qt1010_config megasky_qt1010_config = {
-	.i2c_address = 0xc4
+	.i2c_address = 0x62
 };
 
 static int megasky_qt1010_tuner_attach(struct dvb_usb_adapter *adap)
 {
-	struct m9206_state *m = adap->dev->priv;
-
-	m->i2c_r[M9206_I2C_TUNER].addr = megasky_qt1010_config.i2c_address;
-	m->i2c_r[M9206_I2C_TUNER].magic = 0xc5;
-
 	if (dvb_attach(qt1010_attach, adap->fe, &adap->dev->i2c_adap,
 		       &megasky_qt1010_config) == NULL)
 		return -ENODEV;
@@ -426,8 +463,65 @@ static int megasky_qt1010_tuner_attach(s
 	return 0;
 }
 
+static struct tda1004x_config digivox_tda10046_config = {
+        /* the demodulator's i2c address */
+	.demod_address = 0x08,
+
+        /* does the "inversion" need inverted? */
+        .invert = 0,
+
+        /* Does the OCLK signal need inverted? */
+        .invert_oclk = 0,
+
+        /* Xtal frequency, 4 or 16MHz*/
+        .xtal_freq = TDA10046_XTAL_16M,
+
+        /* IF frequency */
+        .if_freq = TDA10046_FREQ_045,/*currently doing our own*/
+
+        /* AGC configuration */
+/*        .agc_config = TDA10046_AGC_DIGIVOX,*/
+
+	/* setting of GPIO1 and 3 */
+/*	.gpio_config;*/
+
+	/* slave address and configuration of the tuner */
+/*	.tuner_address;*/
+/*	.tuner_config;*/
+/*	.antenna_switch;*/
+
+	/* if the board uses another I2c Bridge (tda8290), its address */
+/*	.i2c_gate;*/
+
+        /* request firmware for device */
+        /* set this to NULL if the card has a firmware EEPROM */
+        .request_firmware = NULL,/*uses firmware EEPROM - TODO: invalid revision*/
+};
+
+static int digivox_tda10046_frontend_attach(struct dvb_usb_adapter *adap)
+{
+	deb_rc("digivox_tda10046_frontend_attach!\n");
+
+	if ((adap->fe = dvb_attach(tda10046_attach, &digivox_tda10046_config, &adap->dev->i2c_adap)) == NULL)
+		return -EIO;
+
+	return 0;
+}
+
+static struct tda827x_config digivox_tda8275_config = {
+};
+
+static int digivox_tda8275_tuner_attach(struct dvb_usb_adapter *adap)
+{
+	if (dvb_attach(tda827x_attach, adap->fe, 0x60, &adap->dev->i2c_adap,
+		       &digivox_tda8275_config) == NULL)
+		return -ENODEV;
+	return 0;
+}
+
 /* DVB USB Driver stuff */
 static struct dvb_usb_device_properties megasky_properties;
+static struct dvb_usb_device_properties digivox_properties;
 
 static int m920x_probe(struct usb_interface *intf,
 		       const struct usb_device_id *id)
@@ -454,12 +548,30 @@ static int m920x_probe(struct usb_interf
 
 		if ((ret = m9206_rc_init(d->udev)) != 0)
 			return ret;
+	} else 	if ((ret = dvb_usb_device_init(intf, &digivox_properties, THIS_MODULE, &d)) == 0) {
+		deb_rc("probed!\n");
+
+		alt = usb_altnum_to_altsetting(intf, 1);
+		if (alt == NULL) {
+			deb_rc("not alt found!\n");
+			return -ENODEV;
+		}
+
+		ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber,
+					alt->desc.bAlternateSetting);
+		if (ret < 0)
+			return ret;
+
+		if ((ret = m9206_rc_init(d->udev)) != 0)
+			return ret;
 	}
 	return ret;
 }
 
 static struct usb_device_id m920x_table [] = {
 		{ USB_DEVICE(USB_VID_MSI, USB_PID_MSI_MEGASKY580) },
+		{ USB_DEVICE(USB_VID_ANUBIS_ELECTRONIC, 
+			     USB_PID_ANUBIS_ELECTRONIC_MSI_DIGI_VOX_MINI_II) },
 		{ }		/* Terminating entry */
 };
 MODULE_DEVICE_TABLE (usb, m920x_table);
@@ -509,6 +621,55 @@ static struct dvb_usb_device_properties 
 		{   "MSI Mega Sky 580 DVB-T USB2.0",
 			{ &m920x_table[0], NULL },
 			{ NULL },
+		}
+	}
+};
+
+static struct dvb_usb_device_properties digivox_properties = {
+	.caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+	.usb_ctrl = DEVICE_SPECIFIC,
+	.firmware = "dvb-usb-digivox-02.fw",
+	.download_firmware = m9206_firmware_download,
+
+/*	.rc_interval      = 100,
+	.rc_key_map       = digivox_rc_keys,
+	.rc_key_map_size  = ARRAY_SIZE(digivox_rc_keys),
+	.rc_query         = m9206_rc_query,*/
+
+	.size_of_priv     = sizeof(struct m9206_state),
+
+	.identify_state   = megasky_identify_state,
+	.num_adapters = 1,
+	.adapter = {{
+		.caps = DVB_USB_ADAP_HAS_PID_FILTER |
+		DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+
+		.pid_filter_count = 8,
+		.pid_filter       = m9206_pid_filter,
+		.pid_filter_ctrl  = m9206_pid_filter_ctrl,
+
+		.frontend_attach  = digivox_tda10046_frontend_attach,
+		.tuner_attach     = digivox_tda8275_tuner_attach,
+
+		.stream = {
+			.type = USB_BULK,
+			.count = 8,
+			.endpoint = 0x81,
+			.u = {
+				.bulk = {
+					.buffersize = 0x4000,
+				}
+			}
+		},
+	}},
+	.i2c_algo         = &m9206_i2c_algo,
+
+	.num_device_descs = 1,
+	.devices = {
+		{   "MSI DIGI VOX mini II DVB-T USB2.0",
+			{ &m920x_table[1], NULL },
+			{ NULL },
 		},
 	}
 };
diff -r 72b6c2479692 linux/drivers/media/dvb/dvb-usb/m920x.h
--- a/linux/drivers/media/dvb/dvb-usb/m920x.h	Thu Mar 01 10:16:13 2007 -0200
+++ b/linux/drivers/media/dvb/dvb-usb/m920x.h	Thu Mar 01 22:37:04 2007 +0100
@@ -27,9 +27,5 @@ struct m9206_state {
 	u16 filters[M9206_MAX_FILTERS];
 	int filtering_enabled;
 	int rep_count;
-	struct {
-		unsigned char addr;
-		unsigned char magic;
-	}i2c_r[M9206_I2C_MAX];
 };
 #endif
diff -r 72b6c2479692 linux/drivers/media/dvb/frontends/tda1004x.c
--- a/linux/drivers/media/dvb/frontends/tda1004x.c	Thu Mar 01 10:16:13 2007 -0200
+++ b/linux/drivers/media/dvb/frontends/tda1004x.c	Fri Mar 02 22:16:08 2007 +0100
@@ -256,6 +256,17 @@ static int tda10046h_set_bandwidth(struc
 static int tda10046h_set_bandwidth(struct tda1004x_state *state,
 				   fe_bandwidth_t bandwidth)
 {
+#if 1
+	static u8 digivox_vhf[] = { 0x79, 0x16, 0x1c, 0x21, 0xc2 };
+	static u8 digivox_uhf[] = { 0x5b, 0x02, 0xd0, 0x2d, 0x03 };
+
+	tda1004x_write_buf(state, TDA10046H_TIME_WREF1, digivox_uhf,
+			   sizeof(digivox_uhf));
+/*for vhf: 0x09, 0xd9 with if=4.5MHz, uhf: 0x0b, 0xb2 with if=4.75MHz*/
+/*0x0c,0x4f with if=5.0MHz(retrieved by trial and error)*/
+	tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0c);
+	tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x4f);
+#else
 	static u8 bandwidth_6mhz_53M[] = { 0x7b, 0x2e, 0x11, 0xf0, 0xd2 };
 	static u8 bandwidth_7mhz_53M[] = { 0x6a, 0x02, 0x6a, 0x43, 0x9f };
 	static u8 bandwidth_8mhz_53M[] = { 0x5c, 0x32, 0xc2, 0x96, 0x6d };
@@ -313,6 +324,7 @@ static int tda10046h_set_bandwidth(struc
 	default:
 		return -EINVAL;
 	}
+#endif
 
 	return 0;
 }
@@ -359,8 +371,10 @@ static int tda1004x_check_upload_ok(stru
 	u8 data1, data2;
 	unsigned long timeout;
 
+	dprintk("%s\n", __FUNCTION__);
+
 	if (state->demod_type == TDA1004X_DEMOD_TDA10046) {
-		timeout = jiffies + 2 * HZ;
+		timeout = jiffies + 4 * HZ;
 		while(!(tda1004x_read_byte(state, TDA1004X_STATUS_CD) & 0x20)) {
 			if (time_after(jiffies, timeout)) {
 				printk(KERN_ERR "tda1004x: timeout waiting for DSP ready\n");
@@ -437,6 +451,11 @@ static void tda10046_init_plls(struct dv
 		tda10046_clk53m = 1;
 
 	tda1004x_write_byteI(state, TDA10046H_CONFPLL1, 0xf0);
+#if 1
+	printk(KERN_INFO "tda1004x: setting up plls for 52MHz sampling clock\n");
+	tda1004x_write_byteI(state, TDA10046H_CONFPLL2, 0x07); // PLL M = 7
+#endif
+#if 0
 	if(tda10046_clk53m) {
 		printk(KERN_INFO "tda1004x: setting up plls for 53MHz sampling clock\n");
 		tda1004x_write_byteI(state, TDA10046H_CONFPLL2, 0x08); // PLL M = 8
@@ -444,6 +463,7 @@ static void tda10046_init_plls(struct dv
 		printk(KERN_INFO "tda1004x: setting up plls for 48MHz sampling clock\n");
 		tda1004x_write_byteI(state, TDA10046H_CONFPLL2, 0x03); // PLL M = 3
 	}
+#endif
 	if (state->config->xtal_freq == TDA10046_XTAL_4M ) {
 		dprintk("%s: setting up PLLs for a 4 MHz Xtal\n", __FUNCTION__);
 		tda1004x_write_byteI(state, TDA10046H_CONFPLL3, 0); // PLL P = N = 0
@@ -632,6 +652,12 @@ static int tda10046_init(struct dvb_fron
 			return -EIO;
 	}
 
+/*from windows driver*/
+	tda1004x_write_byteI(state, TDA1004X_CONFC4, 0x00);
+	tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE2, 0x2f);
+	tda1004x_write_byteI(state, TDA10046H_GPIO_OUT_SEL, 0xcf);
+/*from windows driver*/
+
 	// tda setup
 	tda1004x_write_mask(state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer
 	tda1004x_write_byteI(state, TDA1004X_AUTO, 0x87);    // 100 ppm crystal, select HP stream
@@ -656,9 +682,19 @@ static int tda10046_init(struct dvb_fron
 		tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize
 		tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x60);  // set AGC polarities
 		break;
-	}
+	case TDA10046_AGC_DIGIVOX:
+		tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x0a); // AGC setup
+		tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70); // set AGC threshold
+		tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x24); // set AGC polarities
+		break;
+	}
+#if 1
+	tda1004x_write_byteI(state, TDA1004X_CONFADC2, 0x34);
+	tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE1, 0xa1); 
+#else
 	tda1004x_write_byteI(state, TDA1004X_CONFADC2, 0x38);
 	tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE1, 0x79); // Turn IF AGC output on
+#endif
 	tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MIN, 0);	  // }
 	tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MAX, 0xff); // } AGC min/max values
 	tda1004x_write_byteI(state, TDA10046H_AGC_IF_MIN, 0);	  // }
diff -r 72b6c2479692 linux/drivers/media/dvb/frontends/tda1004x.h
--- a/linux/drivers/media/dvb/frontends/tda1004x.h	Thu Mar 01 10:16:13 2007 -0200
+++ b/linux/drivers/media/dvb/frontends/tda1004x.h	Sat Feb 24 17:35:15 2007 +0100
@@ -36,6 +36,7 @@ enum tda10046_agc {
 	TDA10046_AGC_IFO_AUTO_NEG,	/* IF AGC only, automatic, negtive */
 	TDA10046_AGC_IFO_AUTO_POS,	/* IF AGC only, automatic, positive */
 	TDA10046_AGC_TDA827X,		/* IF AGC only, special setup for tda827x */
+	TDA10046_AGC_DIGIVOX,           /* setup for msi digivox mini ii*/
 };
 
 /* Many (hybrid) boards use GPIO 1 and 3
_______________________________________________
linux-dvb mailing list
linux-dvb@xxxxxxxxxxx
http://www.linuxtv.org/cgi-bin/mailman/listinfo/linux-dvb

[Index of Archives]     [Linux Media]     [Video 4 Linux]     [Asterisk]     [Samba]     [Xorg]     [Xfree86]     [Linux USB]

  Powered by Linux