summaryrefslogtreecommitdiffstats
path: root/sys/dev/usb
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/usb')
-rw-r--r--sys/dev/usb/controller/dwc_otg.c108
-rw-r--r--sys/dev/usb/quirk/usb_quirk.c1
-rw-r--r--sys/dev/usb/template/usb_template.c3
-rw-r--r--sys/dev/usb/template/usb_template.h1
-rw-r--r--sys/dev/usb/template/usb_template_midi.c240
-rw-r--r--sys/dev/usb/usb_dev.c3
-rw-r--r--sys/dev/usb/usb_ioctl.h1
-rw-r--r--sys/dev/usb/usbdevs1
8 files changed, 301 insertions, 57 deletions
diff --git a/sys/dev/usb/controller/dwc_otg.c b/sys/dev/usb/controller/dwc_otg.c
index 50ead5c..868c7c7 100644
--- a/sys/dev/usb/controller/dwc_otg.c
+++ b/sys/dev/usb/controller/dwc_otg.c
@@ -180,6 +180,22 @@ dwc_otg_get_hw_ep_profile(struct usb_device *udev,
*ppf = NULL;
}
+static void
+dwc_otg_tx_fifo_reset(struct dwc_otg_softc *sc, uint32_t value)
+{
+ uint32_t temp;
+
+ /* reset FIFO */
+ DWC_OTG_WRITE_4(sc, DOTG_GRSTCTL, value);
+
+ /* wait for reset to complete */
+ for (temp = 0; temp != 16; temp++) {
+ value = DWC_OTG_READ_4(sc, DOTG_GRSTCTL);
+ if (!(value & (GRSTCTL_TXFFLSH | GRSTCTL_RXFFLSH)))
+ break;
+ }
+}
+
static int
dwc_otg_init_fifo(struct dwc_otg_softc *sc, uint8_t mode)
{
@@ -331,12 +347,11 @@ dwc_otg_init_fifo(struct dwc_otg_softc *sc, uint8_t mode)
}
/* reset RX FIFO */
- DWC_OTG_WRITE_4(sc, DOTG_GRSTCTL,
- GRSTCTL_RXFFLSH);
+ dwc_otg_tx_fifo_reset(sc, GRSTCTL_RXFFLSH);
if (mode != DWC_MODE_OTG) {
/* reset all TX FIFOs */
- DWC_OTG_WRITE_4(sc, DOTG_GRSTCTL,
+ dwc_otg_tx_fifo_reset(sc,
GRSTCTL_TXFIFO(0x10) |
GRSTCTL_TXFFLSH);
} else {
@@ -947,15 +962,21 @@ dwc_otg_setup_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
if (GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) != 0)
goto not_complete;
- if ((sc->sc_last_rx_status & GRXSTSRD_DPID_MASK) !=
- GRXSTSRD_DPID_DATA0) {
+ if ((sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) !=
+ GRXSTSRD_STP_DATA) {
+ if ((sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) !=
+ GRXSTSRD_STP_COMPLETE || td->remainder != 0) {
+ /* release FIFO */
+ dwc_otg_common_rx_ack(sc);
+ goto not_complete;
+ }
/* release FIFO */
dwc_otg_common_rx_ack(sc);
- goto not_complete;
+ return (0); /* complete */
}
- if ((sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) !=
- GRXSTSRD_STP_DATA) {
+ if ((sc->sc_last_rx_status & GRXSTSRD_DPID_MASK) !=
+ GRXSTSRD_DPID_DATA0) {
/* release FIFO */
dwc_otg_common_rx_ack(sc);
goto not_complete;
@@ -969,14 +990,6 @@ dwc_otg_setup_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
/* get the packet byte count */
count = GRXSTSRD_BCNT_GET(sc->sc_last_rx_status);
- /* verify data length */
- if (count != td->remainder) {
- DPRINTFN(0, "Invalid SETUP packet "
- "length, %d bytes\n", count);
- /* release FIFO */
- dwc_otg_common_rx_ack(sc);
- goto not_complete;
- }
if (count != sizeof(req)) {
DPRINTFN(0, "Unsupported SETUP packet "
"length, %d bytes\n", count);
@@ -1002,43 +1015,27 @@ dwc_otg_setup_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
}
/* don't send any data by default */
- DWC_OTG_WRITE_4(sc, DOTG_DIEPTSIZ(0),
- DXEPTSIZ_SET_NPKT(0) |
- DXEPTSIZ_SET_NBYTES(0));
-
- temp = sc->sc_in_ctl[0];
-
- /* enable IN endpoint */
- DWC_OTG_WRITE_4(sc, DOTG_DIEPCTL(0),
- temp | DIEPCTL_EPENA);
- DWC_OTG_WRITE_4(sc, DOTG_DIEPCTL(0),
- temp | DIEPCTL_SNAK);
+ DWC_OTG_WRITE_4(sc, DOTG_DIEPTSIZ(0), DIEPCTL_EPDIS);
+ DWC_OTG_WRITE_4(sc, DOTG_DOEPTSIZ(0), DOEPCTL_EPDIS);
/* reset IN endpoint buffer */
- DWC_OTG_WRITE_4(sc, DOTG_GRSTCTL,
+ dwc_otg_tx_fifo_reset(sc,
GRSTCTL_TXFIFO(0) |
GRSTCTL_TXFFLSH);
/* acknowledge RX status */
dwc_otg_common_rx_ack(sc);
- return (0); /* complete */
+ td->did_stall = 1;
not_complete:
/* abort any ongoing transfer, before enabling again */
-
- temp = sc->sc_out_ctl[0];
-
- temp |= DOEPCTL_EPENA |
- DOEPCTL_SNAK;
-
- /* enable OUT endpoint */
- DWC_OTG_WRITE_4(sc, DOTG_DOEPCTL(0), temp);
-
if (!td->did_stall) {
td->did_stall = 1;
DPRINTFN(5, "stalling IN and OUT direction\n");
+ temp = sc->sc_out_ctl[0];
+
/* set stall after enabling endpoint */
DWC_OTG_WRITE_4(sc, DOTG_DOEPCTL(0),
temp | DOEPCTL_STALL);
@@ -1049,13 +1046,6 @@ not_complete:
DWC_OTG_WRITE_4(sc, DOTG_DIEPCTL(0),
temp | DIEPCTL_STALL);
}
-
- /* setup number of buffers to receive */
- DWC_OTG_WRITE_4(sc, DOTG_DOEPTSIZ(0),
- DXEPTSIZ_SET_MULTI(3) |
- DXEPTSIZ_SET_NPKT(1) |
- DXEPTSIZ_SET_NBYTES(sizeof(req)));
-
return (1); /* not complete */
}
@@ -1499,7 +1489,9 @@ dwc_otg_data_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
/* check for SETUP packet */
if ((sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) ==
- GRXSTSRD_STP_DATA) {
+ GRXSTSRD_STP_DATA ||
+ (sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) ==
+ GRXSTSRD_STP_COMPLETE) {
if (td->remainder == 0) {
/*
* We are actually complete and have
@@ -1584,15 +1576,10 @@ dwc_otg_data_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
not_complete:
- temp = sc->sc_out_ctl[td->ep_no];
-
- temp |= DOEPCTL_EPENA | DOEPCTL_CNAK;
-
- DWC_OTG_WRITE_4(sc, DOTG_DOEPCTL(td->ep_no), temp);
-
/* enable SETUP and transfer complete interrupt */
if (td->ep_no == 0) {
DWC_OTG_WRITE_4(sc, DOTG_DOEPTSIZ(0),
+ DXEPTSIZ_SET_MULTI(3) |
DXEPTSIZ_SET_NPKT(1) |
DXEPTSIZ_SET_NBYTES(td->max_packet_size));
} else {
@@ -1601,8 +1588,12 @@ not_complete:
DXEPTSIZ_SET_MULTI(1) |
DXEPTSIZ_SET_NPKT(4) |
DXEPTSIZ_SET_NBYTES(4 *
- ((td->max_packet_size + 3) & ~3)));
+ ((td->max_packet_size + 3) & ~3)));
}
+ temp = sc->sc_out_ctl[td->ep_no];
+ DWC_OTG_WRITE_4(sc, DOTG_DOEPCTL(td->ep_no), temp |
+ DOEPCTL_EPENA | DOEPCTL_CNAK);
+
return (1); /* not complete */
}
@@ -2004,7 +1995,9 @@ repeat:
(GRXSTSRD_CHNUM_GET(temp) == 0)) {
if ((temp & GRXSTSRD_PKTSTS_MASK) !=
- GRXSTSRD_STP_DATA) {
+ GRXSTSRD_STP_DATA &&
+ (temp & GRXSTSRD_PKTSTS_MASK) !=
+ GRXSTSRD_STP_COMPLETE) {
/* dump data - wrong direction */
dwc_otg_common_rx_ack(sc);
@@ -2209,7 +2202,9 @@ not_complete:
(GRXSTSRD_CHNUM_GET(temp) == 0)) {
if ((temp & GRXSTSRD_PKTSTS_MASK) ==
- GRXSTSRD_STP_DATA) {
+ GRXSTSRD_STP_DATA ||
+ (temp & GRXSTSRD_PKTSTS_MASK) ==
+ GRXSTSRD_STP_COMPLETE) {
DPRINTFN(5, "faking complete\n");
/*
* Race condition: We are complete!
@@ -2634,6 +2629,7 @@ repeat:
/* non-data messages we simply skip */
if (temp != GRXSTSRD_STP_DATA &&
+ temp != GRXSTSRD_STP_COMPLETE &&
temp != GRXSTSRD_OUT_DATA) {
dwc_otg_common_rx_ack(sc);
goto repeat;
@@ -3669,7 +3665,7 @@ dwc_otg_clear_stall_sub_locked(struct dwc_otg_softc *sc, uint32_t mps,
/* we only reset the transmit FIFO */
if (ep_dir) {
- DWC_OTG_WRITE_4(sc, DOTG_GRSTCTL,
+ dwc_otg_tx_fifo_reset(sc,
GRSTCTL_TXFIFO(ep_no) |
GRSTCTL_TXFFLSH);
diff --git a/sys/dev/usb/quirk/usb_quirk.c b/sys/dev/usb/quirk/usb_quirk.c
index 3cfaef0..c756b24 100644
--- a/sys/dev/usb/quirk/usb_quirk.c
+++ b/sys/dev/usb/quirk/usb_quirk.c
@@ -94,6 +94,7 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = {
USB_QUIRK(SILICONPORTALS, YAPPHONE, 0x100, 0x100, UQ_AU_INP_ASYNC),
USB_QUIRK(LOGITECH, UN53B, 0x0000, 0xffff, UQ_NO_STRINGS),
USB_QUIRK(REALTEK, RTL8196EU, 0x0000, 0xffff, UQ_CFG_INDEX_1),
+ USB_QUIRK(REALTEK, RTL8153, 0x0000, 0xffff, UQ_CFG_INDEX_1),
USB_QUIRK(ELSA, MODEM1, 0x0000, 0xffff, UQ_CFG_INDEX_1),
USB_QUIRK(PLANEX2, MZKUE150N, 0x0000, 0xffff, UQ_CFG_INDEX_1),
/* Quirks for printer devices */
diff --git a/sys/dev/usb/template/usb_template.c b/sys/dev/usb/template/usb_template.c
index 49bd373..140c0b9 100644
--- a/sys/dev/usb/template/usb_template.c
+++ b/sys/dev/usb/template/usb_template.c
@@ -1374,6 +1374,9 @@ usb_temp_setup_by_index(struct usb_device *udev, uint16_t index)
case USB_TEMP_SERIALNET:
err = usb_temp_setup(udev, &usb_template_serialnet);
break;
+ case USB_TEMP_MIDI:
+ err = usb_temp_setup(udev, &usb_template_midi);
+ break;
default:
return (USB_ERR_INVAL);
}
diff --git a/sys/dev/usb/template/usb_template.h b/sys/dev/usb/template/usb_template.h
index 484608d..1b320c6 100644
--- a/sys/dev/usb/template/usb_template.h
+++ b/sys/dev/usb/template/usb_template.h
@@ -107,6 +107,7 @@ extern const struct usb_temp_device_desc usb_template_msc;
extern const struct usb_temp_device_desc usb_template_mtp;
extern const struct usb_temp_device_desc usb_template_phone;
extern const struct usb_temp_device_desc usb_template_serialnet;
+extern const struct usb_temp_device_desc usb_template_midi;
usb_error_t usb_temp_setup(struct usb_device *,
const struct usb_temp_device_desc *);
diff --git a/sys/dev/usb/template/usb_template_midi.c b/sys/dev/usb/template/usb_template_midi.c
new file mode 100644
index 0000000..9be1b9e
--- /dev/null
+++ b/sys/dev/usb/template/usb_template_midi.c
@@ -0,0 +1,240 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2015 Hans Petter Selasky. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This file contains the USB template for an USB MIDI Device.
+ */
+
+#ifdef USB_GLOBAL_INCLUDE_FILE
+#include USB_GLOBAL_INCLUDE_FILE
+#else
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usb_core.h>
+
+#include <dev/usb/template/usb_template.h>
+#endif /* USB_GLOBAL_INCLUDE_FILE */
+
+enum {
+ INDEX_MIDI_LANG,
+ INDEX_MIDI_IF,
+ INDEX_MIDI_PRODUCT,
+ INDEX_MIDI_MAX,
+};
+
+#define STRING_MIDI_PRODUCT \
+ "M\0I\0D\0I\0 \0T\0e\0s\0t\0 \0D\0e\0v\0i\0c\0e"
+
+#define STRING_MIDI_IF \
+ "M\0I\0D\0I\0 \0i\0n\0t\0e\0r\0f\0a\0c\0e"
+
+/* make the real string descriptors */
+
+USB_MAKE_STRING_DESC(STRING_MIDI_IF, string_midi_if);
+USB_MAKE_STRING_DESC(STRING_MIDI_PRODUCT, string_midi_product);
+
+/* prototypes */
+
+static const uint8_t midi_desc_raw_0[9] = {
+ 0x09, 0x24, 0x01, 0x00, 0x01, 0x09, 0x00, 0x01, 0x01
+};
+
+static const void *midi_descs_0[] = {
+ &midi_desc_raw_0,
+ NULL
+};
+
+static const struct usb_temp_interface_desc midi_iface_0 = {
+ .ppEndpoints = NULL, /* no endpoints */
+ .ppRawDesc = midi_descs_0,
+ .bInterfaceClass = 1,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .iInterface = INDEX_MIDI_IF,
+};
+
+static const struct usb_temp_packet_size midi_mps = {
+ .mps[USB_SPEED_LOW] = 8,
+ .mps[USB_SPEED_FULL] = 64,
+ .mps[USB_SPEED_HIGH] = 512,
+};
+
+static const uint8_t midi_desc_raw_7[5] = {
+ 0x05, 0x25, 0x01, 0x01, 0x01
+};
+
+static const void *midi_descs_2[] = {
+ &midi_desc_raw_7,
+ NULL
+};
+
+static const struct usb_temp_endpoint_desc midi_bulk_out_ep = {
+ .ppRawDesc = midi_descs_2,
+ .pPacketSize = &midi_mps,
+ .bEndpointAddress = UE_DIR_OUT,
+ .bmAttributes = UE_BULK,
+};
+
+static const uint8_t midi_desc_raw_6[5] = {
+ 0x05, 0x25, 0x01, 0x01, 0x03,
+};
+
+static const void *midi_descs_3[] = {
+ &midi_desc_raw_6,
+ NULL
+};
+
+static const struct usb_temp_endpoint_desc midi_bulk_in_ep = {
+ .ppRawDesc = midi_descs_3,
+ .pPacketSize = &midi_mps,
+ .bEndpointAddress = UE_DIR_IN,
+ .bmAttributes = UE_BULK,
+};
+
+static const struct usb_temp_endpoint_desc *midi_iface_1_ep[] = {
+ &midi_bulk_out_ep,
+ &midi_bulk_in_ep,
+ NULL,
+};
+
+static const uint8_t midi_desc_raw_1[7] = {
+ 0x07, 0x24, 0x01, 0x00, 0x01, /* wTotalLength: */ 0x41, 0x00
+};
+
+static const uint8_t midi_desc_raw_2[6] = {
+ 0x06, 0x24, 0x02, 0x01, 0x01, 0x00
+};
+
+static const uint8_t midi_desc_raw_3[6] = {
+ 0x06, 0x24, 0x02, 0x02, 0x02, 0x00
+};
+
+static const uint8_t midi_desc_raw_4[9] = {
+ 0x09, 0x24, 0x03, 0x01, 0x03, 0x01, 0x02, 0x01, 0x00
+};
+
+static const uint8_t midi_desc_raw_5[9] = {
+ 0x09, 0x24, 0x03, 0x02, 0x04, 0x01, 0x01, 0x01, 0x00
+};
+
+static const void *midi_descs_1[] = {
+ &midi_desc_raw_1,
+ &midi_desc_raw_2,
+ &midi_desc_raw_3,
+ &midi_desc_raw_4,
+ &midi_desc_raw_5,
+ NULL
+};
+
+static const struct usb_temp_interface_desc midi_iface_1 = {
+ .ppRawDesc = midi_descs_1,
+ .ppEndpoints = midi_iface_1_ep,
+ .bInterfaceClass = 0x01, /* MIDI */
+ .bInterfaceSubClass = 3, /* MIDI streaming */
+ .bInterfaceProtocol = 0,
+ .iInterface = INDEX_MIDI_IF,
+};
+
+static const struct usb_temp_interface_desc *midi_interfaces[] = {
+ &midi_iface_0,
+ &midi_iface_1,
+ NULL,
+};
+
+static const struct usb_temp_config_desc midi_config_desc = {
+ .ppIfaceDesc = midi_interfaces,
+ .bmAttributes = UC_BUS_POWERED,
+ .bMaxPower = 25, /* 50 mA */
+ .iConfiguration = INDEX_MIDI_PRODUCT,
+};
+
+static const struct usb_temp_config_desc *midi_configs[] = {
+ &midi_config_desc,
+ NULL,
+};
+
+static usb_temp_get_string_desc_t midi_get_string_desc;
+
+const struct usb_temp_device_desc usb_template_midi = {
+ .getStringDesc = &midi_get_string_desc,
+ .ppConfigDesc = midi_configs,
+ .idVendor = USB_TEMPLATE_VENDOR,
+ .idProduct = 0x00BB,
+ .bcdDevice = 0x0100,
+ .bDeviceClass = 0,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+ .iManufacturer = 0,
+ .iProduct = INDEX_MIDI_PRODUCT,
+ .iSerialNumber = 0,
+};
+
+/*------------------------------------------------------------------------*
+ * midi_get_string_desc
+ *
+ * Return values:
+ * NULL: Failure. No such string.
+ * Else: Success. Pointer to string descriptor is returned.
+ *------------------------------------------------------------------------*/
+static const void *
+midi_get_string_desc(uint16_t lang_id, uint8_t string_index)
+{
+ static const void *ptr[INDEX_MIDI_MAX] = {
+ [INDEX_MIDI_LANG] = &usb_string_lang_en,
+ [INDEX_MIDI_IF] = &string_midi_if,
+ [INDEX_MIDI_PRODUCT] = &string_midi_product,
+ };
+
+ if (string_index == 0) {
+ return (&usb_string_lang_en);
+ }
+ if (lang_id != 0x0409) {
+ return (NULL);
+ }
+ if (string_index < INDEX_MIDI_MAX) {
+ return (ptr[string_index]);
+ }
+ return (NULL);
+}
diff --git a/sys/dev/usb/usb_dev.c b/sys/dev/usb/usb_dev.c
index 1076a9f..c48bfc9 100644
--- a/sys/dev/usb/usb_dev.c
+++ b/sys/dev/usb/usb_dev.c
@@ -830,7 +830,8 @@ usb_fifo_close(struct usb_fifo *f, int fflags)
(!f->flag_iserror)) {
/* wait until all data has been written */
f->flag_sleeping = 1;
- err = cv_wait_sig(&f->cv_io, f->priv_mtx);
+ err = cv_timedwait_sig(&f->cv_io, f->priv_mtx,
+ USB_MS_TO_TICKS(USB_DEFAULT_TIMEOUT));
if (err) {
DPRINTF("signal received\n");
break;
diff --git a/sys/dev/usb/usb_ioctl.h b/sys/dev/usb/usb_ioctl.h
index 539f881..683f3e6 100644
--- a/sys/dev/usb/usb_ioctl.h
+++ b/sys/dev/usb/usb_ioctl.h
@@ -66,6 +66,7 @@ enum {
USB_TEMP_MOUSE, /* USB Mouse */
USB_TEMP_PHONE, /* USB Phone */
USB_TEMP_SERIALNET, /* USB CDC Ethernet and Modem */
+ USB_TEMP_MIDI, /* USB MIDI */
USB_TEMP_MAX,
};
diff --git a/sys/dev/usb/usbdevs b/sys/dev/usb/usbdevs
index f8ba5b5..b19f766 100644
--- a/sys/dev/usb/usbdevs
+++ b/sys/dev/usb/usbdevs
@@ -3748,6 +3748,7 @@ product REALTEK USB20CRW 0x0158 USB20CRW Card Reader
product REALTEK RTL8188ETV 0x0179 RTL8188ETV
product REALTEK RTL8188CTV 0x018a RTL8188CTV
product REALTEK USBKR100 0x8150 USBKR100 USB Ethernet
+product REALTEK RTL8153 0x8153 RTL8153 USB Ethernet
product REALTEK RTL8188CE_0 0x8170 RTL8188CE
product REALTEK RTL8171 0x8171 RTL8171
product REALTEK RTL8172 0x8172 RTL8172
OpenPOWER on IntegriCloud