Re: [PATCH v2] USB device driver of Topcliff PCH

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

 



On Tue, 14 Sep 2010 10:43:42 +0200, Masayuki Ohtake <masa-korg@xxxxxxxxxxxxxxx> wrote:
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
+config PCH_USBDEV
+	tristate
+	depends on USB_GADGET_PCH
+	default USB_GADGET
+	select USB_GADGET_SELECTED

What's the use of this symbol?  Why is it missing help and prompt?

+/**
+ * struct pch_udc_data_dma_desc - Structure to hold DMA descriptor information
+ *				  for data
+ * @status:		Status quadlet
+ * @reserved:		Reserved
+ * @dataptr:		Buffer descriptor
+ * @next:		Next descriptor
+*/
   ^ space missing.

+/**
+ * union pch_udc_ep - Structure holding setup request data
+ *

Needless line.

+ * @data[2]:		8 bytes of setup data

Needless "[2]".

+ * @request:		setup request for gadget driver
+ */
+union pch_udc_setup_data {
+	u32		data[2];
+	struct usb_ctrlrequest	request;
+};

Besides, do you really need this union?

+/**
+ * struct pch_udc_dev - Structure holding complete information
+ *			of the PCH USB device
+ *

Needless empty line.

+ * @gadget:		gadget driver data
+ * @driver:		reference to gadget driver bound
+ * @pdev:		reference to the PCI device
+ * @ep[PCH_UDC_EP_NUM]:	array of endpoints

Needless "[...]".

+ * @lock:		protects all state
+ * @active:		enabled the PCI device
+ * @stall:		stall requested
+ * @prot_stall:		protcol stall requested
+ * @irq_registered:	irq registered with system
+ * @mem_region:		device memory mapped
+ * @registered:		driver regsitered with system
+ * @suspended:		driver in suspended state
+ * @connected:		gadget driver associated
+ * @set_cfg_not_acked:	pending acknowledgement 4 setup
+ * @waiting_zlp_ack:	pending acknowledgement 4 ZLP
+ * @data_requests:	DMA pool for data requests
+ * @stp_requests:	DMA pool for setup requests
+ * @dma_addr:		DMA pool for received
+ * @ep0out_buf[64]:	Buffer for DMA
+ * @setup_data:		Received setup data
+ * @phys_addr:		of device memory
+ * @base_addr:		for mapped device memory
+ * @irq:		IRQ line for the device
+ * @cfg_data:		current cfg, intf, and alt in use
+ */

+/* macro to set a specified bit(mask) at the specified address */
+#define PCH_UDC_BIT_SET(dev, reg, bitmask) \
+     pch_udc_writel((dev), ((pch_udc_readl((dev), (reg)) | (bitmask))), (reg))
+
+/* macro to clear a specified bit(mask) at the specified address */
+#define PCH_UDC_BIT_CLR(dev, reg, bitMask) \
+     pch_udc_writel((dev), (pch_udc_readl((dev), (reg)) & (~(bitMask))), (reg))

You might want to make it into a static inline -- it should make some
people more happy. ;) You can put it after the pch_udc_writel() function.

+/**
+ * struct pch_udc_request - Structure holding a PCH USB device request
+ * @req:		embedded ep request
+ * @td_data_phys:	phys. address
+ * @td_data:		first dma desc. of chain
+ * @td_data_last:	last dma desc. of chain
+ * @queue:		associated queue
+ * @dma_going:		DMA in progress for request
+ * @dma_mapped:		DMA memory mapped for request
+ * @dma_done:		DMA completed for request
+ * @chain_len:		chain length
+ */
+struct pch_udc_request /* request packet */
+{

"{" should go at the same line as "struct".

+	struct usb_request		req;
+	dma_addr_t			td_data_phys;
+	struct pch_udc_data_dma_desc	*td_data;
+	struct pch_udc_data_dma_desc	*td_data_last;
+	struct list_head		queue;
+	unsigned			dma_going:1,
+					dma_mapped:1,
+					dma_done:1;
+	unsigned			chain_len;
+};

+/**
+ * pch_udc_write_csr() - Write the command and status registers.
+ * @val:	value to be written to CSR register
+ * @addr:	address of CSR register
+ */
+static void pch_udc_write_csr(struct pch_udc_dev *dev, unsigned long val,
+			       unsigned long reg)
+{
+	unsigned int count = MAX_LOOP;
+
+	/* Wait till idle */
+	while ((pch_udc_readl(dev, UDC_CSR_BUSY_ADDR) & UDC_CSR_BUSY)
+		&& (count > 0))
+		count--;

Come to think of it, you sort of ignore the result of the last read if
all possible loop iterations were done.  I mean, when count hits zero
you still read the register but it does not really matter what the
read value is.  Because of that, I'd change the while to:

+	while ((pch_udc_readl(...) & UDC_CSR_BUSY) && --count)
+		/* nop */;

This also applies to other loops in other functions as well.

+	if (!count)
+		dev_err(&dev->pdev->dev, "%s: wait error; count = %x",
+			__func__, count);

Writing count here is rather useless.  It's always zero.
This also applies to other loops in other functions as well.

Besides, the loop repeats in four different places (if I notices all of them).
Why not make a function out of it?

+	pch_udc_writel(dev, val, reg);
+	/* Wait till idle */
+	count = MAX_LOOP;
+	while ((pch_udc_readl(dev, UDC_CSR_BUSY_ADDR) & UDC_CSR_BUSY)
+		&& (count > 0))
+		count--;
+	if (!count)
+		dev_err(&dev->pdev->dev, "%s: wait error; count = %x",
+			__func__, count);
+}

+static u32 pch_udc_read_csr(struct pch_udc_dev *dev, unsigned long reg)
+{
[...]
+	/* Dummy read */
+	pch_udc_readl(dev, reg);

O_o...  I'll just assume this makes sense for the hardware. ;)
A comment about why this is done in such a way would be nice
though.

[...]
+}

+static inline void pch_udc_vbus_session(struct pch_udc_dev *dev,
+					  int is_active)
+{
+	if (is_active == 0)
+		pch_udc_set_disconnect(dev);
+	else
+		pch_udc_clear_disconnect(dev);
+}

+static void pch_udc_ep_clear_nak(struct pch_udc_ep *ep)
+{
+	unsigned int loopcnt = 0;
+	struct pch_udc_dev *dev = ep->dev;
+
+	if (!(pch_udc_readl(ep->dev, ep->offset_addr + UDC_EPCTL_ADDR) &
+	      UDC_EPCTL_NAK))

Just a thought, but maybe it would be feasible to create pch_ep_readl()
and pch_ep_writel() functions so that you wouldn't have to add the
ep->offset_addr each time.

+		return;
+	if (!ep->in) {

Useless "{ ... }".

+		while ((pch_udc_read_ep_status(ep) &
+			UDC_EPSTS_MRXFIFO_EMP) == 0) {
+			if (loopcnt++ > 100000) {

Why not preincrementation?  Why not loop form 100000 to zero rather
than from zero to 100000.

+				dev_dbg(&dev->pdev->dev, "%s: RxFIFO not Empty "
+					"loop count = %d", __func__, loopcnt);
+				break;
+			}
+			udelay(100);
+		}
+	}

You need to zero loopcnt here.  Also, I'd use a loop from 25 to zero rather
than from zero to 25 here the same as above.

+	while (pch_udc_readl(ep->dev, ep->offset_addr + UDC_EPCTL_ADDR) &
+		UDC_EPCTL_NAK) {
+		PCH_UDC_BIT_SET(ep->dev, ep->offset_addr + UDC_EPCTL_ADDR,
+				UDC_EPCTL_CNAK);
+		udelay(5);
+		if (loopcnt++ >= 25) {
+			dev_dbg(&dev->pdev->dev, "%s: Clear NAK not set "
+				"for ep%d%s: counter=%d", __func__, ep->num,
+				 (ep->in ? "in" : "out"), loopcnt);
+			break;
+		}
+	}
+}

+static void pch_udc_ep_fifo_flush(struct pch_udc_ep *ep, int dir)
+{
+	unsigned int loopcnt = 0;
+	struct pch_udc_dev *dev = ep->dev;
+
+	dev_dbg(&dev->pdev->dev, "%s: ep%d%s", __func__, ep->num,
+		(ep->in ? "in" : "out"));
+	if (dir) {	/* IN ep */
+		PCH_UDC_BIT_SET(ep->dev, ep->offset_addr + UDC_EPCTL_ADDR,
+				UDC_EPCTL_F);
+		return;
+	} else {

Drop "else", "{" ... "}" and indention.

+		if (pch_udc_read_ep_status(ep) & UDC_EPSTS_MRXFIFO_EMP)
+			return;
+		PCH_UDC_BIT_SET(ep->dev, ep->offset_addr + UDC_EPCTL_ADDR,
+				UDC_EPCTL_MRXFLUSH);
+		/* Wait for RxFIFO Empty */
+		while ((pch_udc_read_ep_status(ep) &
+			UDC_EPSTS_MRXFIFO_EMP) == 0) {
+			if (loopcnt++ > 1000000) {
+				dev_dbg(&dev->pdev->dev, "RxFIFO not Empty "
+					"loop count = %d", loopcnt);
+				break;
+			}
+			udelay(100);
+		}
+		PCH_UDC_BIT_CLR(ep->dev, ep->offset_addr + UDC_EPCTL_ADDR,
+				UDC_EPCTL_MRXFLUSH);
+	}
+}

+static void pch_udc_init(struct pch_udc_dev *dev)
+{
[...]
+	/* enable dynamic CSR programmingi, self powered and device speed */
+	if (speed_fs) {
+		PCH_UDC_BIT_SET(dev, UDC_DEVCFG_ADDR, UDC_DEVCFG_CSR_PRG |
+				UDC_DEVCFG_SP | UDC_DEVCFG_SPD_FS);
+	} else { /* defaul high speed */
+		PCH_UDC_BIT_SET(dev, UDC_DEVCFG_ADDR, UDC_DEVCFG_CSR_PRG |
+				UDC_DEVCFG_SP | UDC_DEVCFG_SPD_HS);
+	}

Useless "{ ... }".

[...]
+}

+static int pch_udc_pcd_pullup(struct usb_gadget *gadget, int is_on)
+{
+	struct pch_udc_dev	*dev;
+
+	if (gadget == NULL) {
+		pr_debug("%s: exit -EINVAL", __func__);
+		return -EINVAL;
+	}
+	dev = container_of(gadget, struct pch_udc_dev, gadget);
+	if (is_on == 0)
+		pch_udc_set_disconnect(dev);
+	else
+		pch_udc_clear_disconnect(dev);
+	return 0;

You can use pch_udc_vbus_session() here.

+}

+static int pch_udc_pcd_vbus_draw(struct usb_gadget *gadget, unsigned int mA)
+{
+	if ((gadget == NULL) || (mA > 250)) { /* Max is 250 in 2mA unit */
+		pr_debug("%s: exit -EINVAL", __func__);
+		return -EINVAL;
+	}
+	/* Could not find any regs where we can set the limit	*/
+	return -EOPNOTSUPP;
+}

Wouldn't it be easier to just always return -EOPNOTSUPP?

+const struct usb_gadget_ops pch_udc_ops = {

Why not static?

+	.get_frame = pch_udc_pcd_get_frame,
+	.wakeup = pch_udc_pcd_wakeup,
+	.set_selfpowered = pch_udc_pcd_selfpowered,
+	.pullup = pch_udc_pcd_pullup,
+	.vbus_session = pch_udc_pcd_vbus_session,
+	.vbus_draw = pch_udc_pcd_vbus_draw,
+};

+static void complete_req(struct pch_udc_ep *ep, struct pch_udc_request *req,
+								 int status)
+{

+	if (req->dma_mapped) {
+		if (ep->in) {
+			pci_unmap_single(dev->pdev, req->req.dma,
+					 req->req.length, PCI_DMA_TODEVICE);
+		} else {
+			pci_unmap_single(dev->pdev, req->req.dma,
+					 req->req.length, PCI_DMA_FROMDEVICE);
+		}

Useless "{ ... }".

+		req->dma_mapped = 0;
+		req->req.dma = DMA_ADDR_INVALID;
+	}

+}
+
+/**
+ * empty_req_queue() - This API empties the request queue of an endpoint
+ * @ep:		Reference to the endpoint structure
+ */
+static void empty_req_queue(struct pch_udc_ep *ep)
+{
+	struct pch_udc_request	*req;
+
+	ep->halted = 1;
+	while (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next, struct pch_udc_request, queue);
+		pr_debug("%s: complete_req  ep%d%s", __func__, ep->num,
+			  (ep->in ? "in" : "out"));
+		complete_req(ep, req, -ESHUTDOWN);

complete_req() removes from list, right? I think it's worth adding a comment.

+	}
+}


+static int pch_udc_create_dma_chain(struct pch_udc_ep *ep,
+				     struct pch_udc_request *req,
+				     unsigned long buf_len,
+				     gfp_t gfp_flags)
+{

+	for (i = buf_len; i < bytes; i += buf_len) {
+		dma_addr = DMA_ADDR_INVALID;
+		/* create or determine next desc. */
+		td = pci_pool_alloc(ep->dev->data_requests, gfp_flags,
+				    &dma_addr);
+		if (td == NULL)
+			return -ENOMEM;
+
+		td->status = 0;
+		td->dataptr = req->req.dma + i; /* assign buffer */
+
+		if ((bytes - i) >= buf_len) {
+			txbytes = buf_len;
+		} else { /* short packet */
+			txbytes = bytes - i;
+		}

Useless "{ ... }".

+static int prepare_dma(struct pch_udc_ep *ep, struct pch_udc_request *req,
+			  gfp_t gfp)
+{

+	/* Allocate and create a DMA chain */
+	retval = pch_udc_create_dma_chain(ep, req, ep->ep.maxpacket, gfp);
+	if (retval) {
+		if (retval == -ENOMEM)
+			pr_err("%s: Out of DMA memory", __func__);
+		return retval;
+	}
+	if (ep->in) {
+		if (req->req.length <= ep->ep.maxpacket) {
+			/* write tx bytes */
+			req->td_data->status = PCH_UDC_DMA_LAST |
+					       PCH_UDC_BS_HST_BSY |
+					       req->req.length;
+		}

Useless "{ ... "}".

+/**
+ * process_zlp() - This function process zero length packets
+ *			from the gadget driver
+ * @ep:		Reference to the endpoint structure
+ * @req:	Reference to the request
+ */
+static void process_zlp(struct pch_udc_ep *ep, struct pch_udc_request *req)
+{
+	struct pch_udc_dev	*dev = ep->dev;
+
+	dev_dbg(&dev->pdev->dev, "%s: enter  ep%d%s", __func__, ep->num,
+		(ep->in ? "in" : "out"));
+	/* IN zlp's are handled by hardware */
+	complete_req(ep, req, 0);
+
+	/* if set_config or set_intf is waiting for ack by zlp
+	 *then set CSR_DONE
            ^ missing space.


+	 */

+	/* setup command is ACK'ed now by zlp */
+	if (!dev->stall) {
+		if (dev->waiting_zlp_ack) {

Join both ifs into a single if with &&.

+			/* clear NAK by writing CNAK in EP0_IN */
+			pch_udc_ep_clear_nak(&(dev->ep[UDC_EP0IN_IDX]));
+			dev->waiting_zlp_ack = 0;
+		}
+	}
+}
+
+/**
+ * pch_udc_start_rxrequest() - This function starts the receive requirement.
+ * @ep:		Reference to the endpoint structure
+ * @req:	Reference to the request structure
+ */
+static void pch_udc_start_rxrequest(struct pch_udc_ep *ep,
+					 struct pch_udc_request *req)
+{
+	struct pch_udc_data_dma_desc *td_data;
+
+	pch_udc_clear_dma(ep->dev, DMA_DIR_RX);
+	td_data = req->td_data;
+	ep->td_data = req->td_data;
+	/* Set the status bits for all descriptors */
+	while (1) {
+		td_data->status = (td_data->status & ~PCH_UDC_BUFF_STS) |
+				    PCH_UDC_BS_HST_RDY;
+		if ((td_data->status & PCH_UDC_DMA_LAST) ==  PCH_UDC_DMA_LAST)
+			break;
+
+		td_data = (struct pch_udc_data_dma_desc *) \
+			   phys_to_virt(td_data->next);

Useless cast.  phys_to_virt() returns void*.

+	}

+		if (ep->in) {
+			usbreq->dma = pci_map_single(dev->pdev, usbreq->buf,
+					usbreq->length, PCI_DMA_TODEVICE);
+		} else {
+			usbreq->dma = pci_map_single(dev->pdev, usbreq->buf,
+					usbreq->length, PCI_DMA_FROMDEVICE);
+		}

Useless "{ ... }".

+static void pch_udc_complete_transfer(struct pch_udc_ep *ep)
+{
+	struct pch_udc_request *req;
+	struct pch_udc_dev *dev = ep->dev;
+
+	if (list_empty(&ep->queue))
+		return;
+
+	dev_dbg(&dev->pdev->dev, "%s: list_entry", __func__);
+	req = list_entry(ep->queue.next, struct pch_udc_request, queue);
+	if (!req)
+		return;

This can never happen.  list_head's next is never set to NULL.  Moreover,
you already check if the queue is not empty a few lines above.

+	if ((req->td_data_last->status & PCH_UDC_BUFF_STS) !=
+						PCH_UDC_BS_DMA_DONE)
+		return;
+
+#ifdef DMA_PPB_WITH_DESC_UPDATE
+	for (i = 0; i < req->chain_len; i++) {

+		req->td_data = (struct pch_udc_data_dma_desc *)
+				 phys_to_virt(req->td_data->next);

Useless cast.

+	}
+#else

+#endif

+static void pch_udc_complete_receiver(struct pch_udc_ep *ep)
+{
+	struct pch_udc_request *req;
+	struct pch_udc_dev *dev = ep->dev;
+	unsigned int count;
+
+	if (list_empty(&ep->queue))
+		return;
+
+	/* next request */
+	req = list_entry(ep->queue.next, struct pch_udc_request, queue);
+	if (!req || (req->td_data_last->status & PCH_UDC_BUFF_STS) !=
+						PCH_UDC_BS_DMA_DONE) {
+#ifdef DMA_PPB_WITH_DESC_UPDATE
+		dev_dbg(&dev->pdev->dev, "%s: ep%d%s  DMA not Done",
+			__func__, ep->num, (ep->in ? "in" : "out"));
+		pch_udc_ep_set_rrdy(ep);
+#endif
+		return;
+	}
+	dev_dbg(&dev->pdev->dev, "%s: ep%d%s  DMA Done", __func__, ep->num,
+		 (ep->in ? "in" : "out"));
+	/* Disable DMA */
+	pch_udc_clear_dma(ep->dev, DMA_DIR_RX);
+#ifdef DMA_PPB_WITH_DESC_UPDATE
+{

This should be indented.

+	/* Get Rx bytes */
+	struct pch_udc_data_dma_desc *td_data = req->td_data;
+	for (i = 0, count = 0; i < req->chain_len; i++) {
+		if ((td_data->status & PCH_UDC_RXTX_STS) != PCH_UDC_RTS_SUCC) {
+			dev_err(&dev->pdev->dev, "Invalid RXTX status (0x%08x) "
+				"epstatus=0x%08x\n",
+				(td_data->status & PCH_UDC_RXTX_STS),
+				(int)(ep->epsts));
+			return;
+		}
+		count += td_data->status & PCH_UDC_RXTX_BYTES;
+		td_data =
+		  (struct pch_udc_data_dma_desc *) phys_to_virt(td_data->next);

Useless cast.
+	}
+}
+#else

+#endif

+/**
+ * pch_udc_svc_data_out() - Handles interrupts from OUT endpoint
+ * @dev:	Reference to the device structure
+ * @ep_num:	Endpoint that generated the interrupt
+ */
+static void pch_udc_svc_data_out(struct pch_udc_dev *dev, int ep_num)
+{
+	u32			epsts;
+	struct pch_udc_ep		*ep;
+	struct pch_udc_request		*req = NULL;
+
+	ep = &dev->ep[2*ep_num + 1];
+	epsts = ep->epsts;
+	ep->epsts = 0;
+
+	dev_dbg(&dev->pdev->dev, "%s: enter  ep%d%s status = 0x%08x", __func__,
+		ep->num, (ep->in ? "in" : "out"), epsts);
+	if (epsts & UDC_EPSTS_BNA) { /* Just log it; only in DMA mode */
+		if (!list_empty(&ep->queue)) {

Those two ifs can be joined into one with &&.

[...]
+		}
+	}

+}
+
+/**
+ * pch_udc_svc_control_in() - Handle Control IN endpoint interrupts
+ * @dev:	Reference to the device structure
+ */
+static void pch_udc_svc_control_in(struct pch_udc_dev *dev)
+{

+	if (epsts & UDC_EPSTS_TXEMPTY) { /* Tx empty */
+		dev_dbg(&dev->pdev->dev, "%s: TXEMPTY", __func__);
+	}

Useless "{ ... }".

+}
+
+/**
+ * pch_udc_svc_control_out() - Routine that handle Control
+ *					OUT endpoint interrupts
+ * @dev:	Reference to the device structure
+ */
+static void pch_udc_svc_control_out(struct pch_udc_dev *dev)
+{
+	u32	stat;
+	int setup_supported;
+	struct pch_udc_ep	*ep;
+
+	ep = &dev->ep[UDC_EP0OUT_IDX];
+	stat = ep->epsts;
+	ep->epsts = 0;
+
+	if (stat & UDC_EPSTS_BNA)
+		dev_dbg(&dev->pdev->dev, "%s: EP0: BNA", __func__);
+		/* When we get a request, we will populate the descriptors. */
+		/* Anything else to do? */
+	/* If setup data */
+	if (((stat & UDC_EPSTS_OUT_MASK) >> UDC_EPSTS_OUT_OFS) ==
+	    UDC_EPSTS_OUT_SETUP) {
+		dev->stall = 0;
+		dev->ep[UDC_EP0IN_IDX].halted = 0;
+		dev->ep[UDC_EP0OUT_IDX].halted = 0;
+		/* In data not ready */
+		pch_udc_ep_set_nak(&(dev->ep[UDC_EP0IN_IDX]));
+		dev->setup_data.data[0] = ep->td_stp->data12;
+		dev->setup_data.data[1] = ep->td_stp->data34;
+		dev_dbg(&dev->pdev->dev, "%s: EP0 setup data12: 0x%x "
+			"data34:0x%x", __func__, ep->td_stp->data12,
+			ep->td_stp->data34);
+		pch_udc_init_setup_buff(ep->td_stp);
+		pch_udc_clear_dma(dev, DMA_DIR_TX);
+		pch_udc_ep_fifo_flush(&(dev->ep[UDC_EP0IN_IDX]),
+				      dev->ep[UDC_EP0IN_IDX].in);
+		if ((dev->setup_data.request.bRequestType & USB_DIR_IN) != 0) {
+			dev->gadget.ep0 = &dev->ep[UDC_EP0IN_IDX].ep;
+		} else { /*	OUT */
+			dev->gadget.ep0 = &ep->ep;
+		}

Useless "{ ... }".

+static void pch_udc_dev_isr(struct pch_udc_dev *dev, u32 dev_intr)
+{
[...]
+	/* RWKP interrupt */
+	if (dev_intr & UDC_DEVINT_RWKP)
+		dev_dbg(&dev->pdev->dev, "RWKP");
+

Useless empty line.

+}

+static void pch_udc_pcd_reinit(struct pch_udc_dev *dev)
+{
+	static const char *ep_string[] = {

Why not static "const char *const ep_string[]"?

+		ep0_string, "ep0out", "ep1in", "ep1out", "ep2in", "ep2out",
+		"ep3in", "ep3out", "ep4in", "ep4out", "ep5in", "ep5out",
+		"ep6in", "ep6out", "ep7in", "ep7out", "ep8in", "ep8out",
+		"ep9in", "ep9out", "ep10in", "ep10out", "ep11in", "ep11out",
+		"ep12in", "ep12out", "ep13in", "ep13out", "ep14in", "ep14out",
+		"ep15in", "ep15out",
+	};

+}

Also, please run checkpatch.pl on the patch.

--
Best regards,                                        _     _
| Humble Liege of Serenely Enlightened Majesty of  o' \,=./ `o
| Computer Science,  Michał "mina86" Nazarewicz       (o o)
+----[mina86*mina86.com]---[mina86*jabber.org]----ooO--(_)--Ooo--
--
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