From nobody Sat Jul 25 19:28:39 2026 Received: from mail-pf1-f180.google.com (mail-pf1-f180.google.com [209.85.210.180]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 67E891C860C for ; Tue, 14 Jul 2026 13:21:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784035295; cv=none; b=HJ9qdr0Ndck99stFiyvmvXEydx6nDo5cGM+Fiv+Rsv3FRMnMDyQmBKlsT6uCUwcEhLFQAzhd/QiOE97j0cJRv6JQhqPY+tEX19Tu3cO8PIZmsm0UJbbg61vjTGBFoPLCsPHgZmoK1CtdMx/jOJDsKOELnjp8vTkv8y+R++qOs44= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784035295; c=relaxed/simple; bh=Srv5fLjlcBt+I4Y2YUvAixlGYoBjMd5dwR1EsTURdrY=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=l2+p1TUPq41Raqr2rEKO1Vr7gc/r3Gj8Tb3qXwjLyYDykprh3DbSZChX+nfA3DjMPKM2TpB0DUIbMfNGnu56Kc3Xleho9yByhNLa0kRyd2MpaKibkdE94bkdITEUN4Q3xnwc6CWYXFZwDBfSRJzZ7KYJF6TdCv32/ZkKYo7YOIQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=FvRCxAVC; arc=none smtp.client-ip=209.85.210.180 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="FvRCxAVC" Received: by mail-pf1-f180.google.com with SMTP id d2e1a72fcca58-848743155bcso426997b3a.0 for ; Tue, 14 Jul 2026 06:21:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784035293; x=1784640093; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=SWA2pdRQ6l8w4+g2rx0Pkw+ZVozyVuWGGSCZMNK3koU=; b=FvRCxAVCpASYzRUVuj1QMBj3PjRY6yd3PH4WG1yj/+O9lkFGvTWuuPhRNE4Trmqkrc HrS18QTAmcP1h4OraD0xuIunWquf6oTRbfiX1BqyRC5TUcIGhXIGFnKT1oagreWost/q sA2t2dLFlwAwVPI2iG2X+MyIlwZUB6SwDthntEVTsGVKQxtQO0a2M/4wwjJDA8EjXZzX zSACmLoZLXObgNCoL/XliZZYpmTTX1B+3e50Gq/6I83C0w5wTSYh9OcR8GDEXLYAjq1y g0Y04EZ4BTI5Ew81Mn3nIySTppJbfhFFvKb9/Q1CRd0EyCr9Hjrm5Hd8ftuS/kYs6lGY X0iA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784035293; x=1784640093; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=SWA2pdRQ6l8w4+g2rx0Pkw+ZVozyVuWGGSCZMNK3koU=; b=Xqc/A0LOIV0GATFl3lrsMGo3DQzF38zjQeS/H+pRaKCT/XAKZAChyKm/czHB1FbVqc AhjX91/ksp3WrQsecFxbDDUTitsKPuHaaub5+Of2hX4D5G6+v1TWYzmvWy3PGcDJXQY/ us5cMNGtYkJnq7TStjO9/FJNieCE+Jov3Zv1hfLvh2Umha7sYkPQqPO9YTWJafTVGckO sp6VT6FhxotD4shQf/VJBFXTAgbrDv4zCLah05h1r7H4T7zX/VjnYfryS04Wge4mUaTg wu7/ymCT5RM3i8l2tixsOpcctrVytrJ86paQlx9SREGaewpFf/ZuwiGRXX+NZJXgTPoI JnAQ== X-Forwarded-Encrypted: i=1; AHgh+RrIhJGmuw4zC7bqEIP78eJ6CgFqQ/Uiqjf6UjfNuZ1vKrN0pnxCrgm+j/cV0sSlPVSSt4iq4uHKVZjqMgM=@vger.kernel.org X-Gm-Message-State: AOJu0YyfVYDAeJI1Rbae7GSny0qVsW98XqZgSgc2YmrSCu/i17eWlR2n 1CJWDOKQoGg5wCQ+tXiUaq3GXD3B0/Qk2C0wKBvtGmyzxoiKTJ/xhzhL X-Gm-Gg: AfdE7clM33//FTkZEfucalbbxt0gpUo2XG8SHT3nYTyGbMhHu+wdCnLybdVvosbz65e R/FA51My1p8zMh1A8WR2CZbsFWM8a7dQLgnygStu0AsWeHKY5e3RNcneJ5pQy3iVezJov7xxIGf 7QtL56OXJv1y5fAu8ITAM70QoJTMjDFqU5U2FFWc7C4kiO8LKCGFCxf1q2Nh44IZrP1ka0ce4r/ SHwZC6O0fDE4RLDZRx9qhrrBT2yxlpiw2uT+5UHrjKs/kNSP+WmWMaGhTdm7cziSg/DSUULKg+W ih3vCpvRgqIqTGawANikvzj/MXtY1vPOiTbOAn+Wtx0DjYAASczhi7SQw4gRC4MJx4mRzL9IUuC UnlbMucTIrhIYPf8EJp685+FF66wC3XOplBYuLn2paaRqS4xMWUAu8jMsyXEhdsI7uGGJoKbS9d ViQmxBwict5Qb77neIaclpS+xnCAU4+m7C74RGh3s= X-Received: by 2002:a05:6a00:9295:b0:847:7a61:e68e with SMTP id d2e1a72fcca58-84889618fdfmr12660747b3a.31.1784035292328; Tue, 14 Jul 2026 06:21:32 -0700 (PDT) Received: from d.home.yangfl.dn42 ([2a09:bac5:661f:3046::4cf:36]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-84a4f6be668sm1515482b3a.35.2026.07.14.06.21.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 Jul 2026 06:21:31 -0700 (PDT) From: David Yang To: linux-media@vger.kernel.org Cc: David Yang , Mauro Carvalho Chehab , linux-kernel@vger.kernel.org Subject: [PATCH v4] media: dvb-usb-v2: nerc: add support for NERC DtmbUSB DTMB receiver Date: Tue, 14 Jul 2026 21:21:19 +0800 Message-ID: <20260714132121.109385-1-mmyangfl@gmail.com> X-Mailer: git-send-email 2.53.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" NERC DtmbUSB (04b4:1004) is a highly integrated DTMB USB receiver. * USB Bridge: Cypress CY7C68013A * Receiver: HDIC HD2312A * Tuner: MaxLinear MxL608 * ROM: 24-series flash Three hardware variants is found: Letv, Aiwa, and CVB. Signed-off-by: David Yang --- v3: https://lore.kernel.org/linux-media/20260713100211.29548-1-mmyangfl@gma= il.com - fix timeout issue v2: https://lore.kernel.org/linux-media/20260711140811.1689525-1-mmyangfl@g= mail.com - fix wrong memcpy arguement in nerc_control_msg - fix warning from swzigzag v1: https://lore.kernel.org/linux-media/20260710063237.1430919-1-mmyangfl@g= mail.com - add a comment for checkpatch warning - fix an uninitialized variable MAINTAINERS | 8 + drivers/media/usb/dvb-usb-v2/Kconfig | 8 + drivers/media/usb/dvb-usb-v2/Makefile | 3 + drivers/media/usb/dvb-usb-v2/nerc.c | 381 ++++++++++++++++++++++++++ drivers/media/usb/dvb-usb-v2/nerc.h | 40 +++ include/media/dvb-usb-ids.h | 1 + 6 files changed, 441 insertions(+) create mode 100644 drivers/media/usb/dvb-usb-v2/nerc.c create mode 100644 drivers/media/usb/dvb-usb-v2/nerc.h diff --git a/MAINTAINERS b/MAINTAINERS index f37a81950e25..a86e218e79e1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9158,6 +9158,14 @@ Q: http://patchwork.linuxtv.org/project/linux-media/= list/ T: git git://linuxtv.org/mkrufky/mxl111sf.git F: drivers/media/usb/dvb-usb-v2/mxl111sf* =20 +DVB_USB_NERC MEDIA DRIVER +M: David Yang +L: linux-media@vger.kernel.org +S: Maintained +W: https://linuxtv.org +Q: http://patchwork.linuxtv.org/project/linux-media/list/ +F: drivers/media/usb/dvb-usb-v2/nerc* + DVB_USB_RTL28XXU MEDIA DRIVER L: linux-media@vger.kernel.org S: Orphan diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-u= sb-v2/Kconfig index 4eadc9539b4c..8f0918728de1 100644 --- a/drivers/media/usb/dvb-usb-v2/Kconfig +++ b/drivers/media/usb/dvb-usb-v2/Kconfig @@ -144,6 +144,14 @@ config DVB_USB_MXL111SF help Say Y here to support the MxL111SF USB2.0 DTV receiver. =20 +config DVB_USB_NERC + tristate "NERC DTMB USB2.0 support" + depends on DVB_USB_V2 + select DVB_EC100 + help + Say Y here to support the NERC DTMB (Cypress CY7C68013A + HDIC + HD2312A) USB2.0 receiver. + config DVB_USB_RTL28XXU tristate "Realtek RTL28xxU DVB USB support" depends on DVB_USB_V2 && I2C_MUX diff --git a/drivers/media/usb/dvb-usb-v2/Makefile b/drivers/media/usb/dvb-= usb-v2/Makefile index 58c0140e19de..0c33dd50f2f7 100644 --- a/drivers/media/usb/dvb-usb-v2/Makefile +++ b/drivers/media/usb/dvb-usb-v2/Makefile @@ -35,6 +35,9 @@ obj-$(CONFIG_DVB_USB_MXL111SF) +=3D dvb-usb-mxl111sf.o obj-$(CONFIG_DVB_USB_MXL111SF) +=3D mxl111sf-demod.o obj-$(CONFIG_DVB_USB_MXL111SF) +=3D mxl111sf-tuner.o =20 +dvb-usb-nerc-objs :=3D nerc.o +obj-$(CONFIG_DVB_USB_NERC) +=3D dvb-usb-nerc.o + dvb-usb-rtl28xxu-objs :=3D rtl28xxu.o obj-$(CONFIG_DVB_USB_RTL28XXU) +=3D dvb-usb-rtl28xxu.o =20 diff --git a/drivers/media/usb/dvb-usb-v2/nerc.c b/drivers/media/usb/dvb-us= b-v2/nerc.c new file mode 100644 index 000000000000..fe25614067cf --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/nerc.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * China NERC (National Engineering Research Center of Digital Television) + * DTMB (Cypress CY7C68013A + HDIC HD2312A) USB2.0 receiver. + * + * Copyright (c) 2026 David Yang + */ + +#include "dvb_usb.h" + +#include "nerc.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static const char *nerc_variant_name(enum nerc_variant variant) +{ + switch (variant) { + case NERC_VARIANT_LETV: + return "Letv"; + case NERC_VARIANT_AIWA: + return "Aiwa"; + case NERC_VARIANT_CVB: + return "CVB"; + default: + return "unknown"; + } +} + +static int +nerc_control_msg(struct dvb_usb_device *d, u8 request, bool read, + void *data, u16 size) +{ + struct nerc_priv *priv =3D d_to_priv(d); + unsigned int pipe; + u8 requesttype; + int res; + + if (WARN_ON(size > sizeof(priv->buf))) + return -EINVAL; + + lockdep_assert_held_once(&d->usb_mutex); + + if (read) { + requesttype =3D USB_TYPE_VENDOR | USB_DIR_IN; + pipe =3D usb_rcvctrlpipe(d->udev, 0); + } else { + requesttype =3D USB_TYPE_VENDOR | USB_DIR_OUT; + pipe =3D usb_sndctrlpipe(d->udev, 0); + if (size) + memcpy(priv->buf, data, size); + } + + /* value seems to be ignored, but just play safe */ + res =3D usb_control_msg(d->udev, pipe, request, requesttype, 0xfe, + 0, priv->buf, size, NERC_USB_TIMEOUT); + dvb_usb_dbg_usb_control_msg(d->udev, request, requesttype, 0xfe, + 0, priv->buf, size); + + if (res < 0) + return res; + if (res !=3D size) + return -EIO; + if (size && read) + memcpy(data, priv->buf, res); + return 0; +} + +static int +nerc_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *s) +{ + s->min_delay_ms =3D 800; + s->step_size =3D 0; + s->max_drift =3D 0; + + return 0; +} + +static int nerc_read_status(struct dvb_frontend *fe, enum fe_status *statu= s) +{ + struct dvb_usb_device *d =3D fe_to_d(fe); + bool has_signal; + bool has_lock; + int res; + + mutex_lock(&d->usb_mutex); + res =3D nerc_control_msg(d, NERC_HAS_SIGNAL, true, + &has_signal, sizeof(has_signal)); + if (!res && has_signal) { + res =3D nerc_control_msg(d, NERC_HAS_LOCK, true, + &has_lock, sizeof(has_lock)); + } + mutex_unlock(&d->usb_mutex); + if (res) + return res; + + if (!has_signal) + *status =3D 0; + else if (!has_lock) + *status =3D FE_HAS_SIGNAL | FE_HAS_CARRIER; + else + *status =3D FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | + FE_HAS_SYNC | FE_HAS_LOCK; + return 0; +} + +static int +nerc_get_frontend(struct dvb_frontend *fe, struct dtv_frontend_properties = *c) +{ + struct dvb_usb_device *d =3D fe_to_d(fe); + struct nerc_priv *priv =3D d_to_priv(d); + unsigned char frontend[6]; + unsigned char snr[2]; + unsigned char strength[4]; + int res; + + mutex_lock(&d->usb_mutex); + + res =3D nerc_control_msg(d, NERC_FRONTEND, true, + frontend, sizeof(frontend)); + if (res) + goto end; + + res =3D nerc_control_msg(d, NERC_SNR, true, snr, sizeof(snr)); + if (res) + goto end; + + res =3D nerc_control_msg(d, NERC_STRENGTH, true, + strength, sizeof(strength)); + if (res) + goto end; + +end: + mutex_unlock(&d->usb_mutex); + if (res) + return res; + + switch (frontend[0]) { + case 0: + c->transmission_mode =3D TRANSMISSION_MODE_C1; + break; + case 1: + c->transmission_mode =3D TRANSMISSION_MODE_C3780; + break; + default: + c->transmission_mode =3D TRANSMISSION_MODE_AUTO; + } + + switch (frontend[1]) { + case 0: + c->guard_interval =3D GUARD_INTERVAL_PN945; + break; + case 1: + c->guard_interval =3D GUARD_INTERVAL_PN595; + break; + case 2: + c->guard_interval =3D GUARD_INTERVAL_PN420; + break; + default: + c->guard_interval =3D GUARD_INTERVAL_AUTO; + } + + switch (frontend[2]) { + case 0: + c->fec_inner =3D FEC_2_5; + break; + case 1: + c->fec_inner =3D FEC_3_5; + break; + case 2: + c->fec_inner =3D FEC_4_5; + break; + default: + c->fec_inner =3D FEC_AUTO; + } + + switch (frontend[3]) { + case 0: + c->interleaving =3D INTERLEAVING_720; + break; + case 1: + c->interleaving =3D INTERLEAVING_240; + break; + default: + c->interleaving =3D INTERLEAVING_AUTO; + } + + switch (frontend[4]) { + case 0: + c->modulation =3D QAM_4_NR; + break; + case 1: + c->modulation =3D QPSK; + break; + case 2: + c->modulation =3D QAM_16; + break; + case 3: + c->modulation =3D QAM_32; + break; + case 4: + c->modulation =3D QAM_64; + break; + default: + c->modulation =3D QAM_AUTO; + } + + switch (frontend[5]) { + case 0: + c->inversion =3D INVERSION_ON; + break; + case 1: + c->inversion =3D INVERSION_OFF; + break; + default: + c->inversion =3D INVERSION_AUTO; + } + + if (priv->variant =3D=3D NERC_VARIANT_CVB) + c->strength.stat[0].svalue =3D -1000 * strength[3]; + else + c->strength.stat[0].uvalue =3D strength[3] * 0xffff / 100; + c->cnr.stat[0].svalue =3D 10 * (100 * snr[0] + snr[1]); + + return 0; +} + +static int nerc_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c =3D &fe->dtv_property_cache; + __be32 freq =3D cpu_to_be32(c->frequency); + struct dvb_usb_device *d =3D fe_to_d(fe); + int res; + + mutex_lock(&d->usb_mutex); + res =3D nerc_control_msg(d, NERC_FREQ_SET, false, &freq, sizeof(freq)); + mutex_unlock(&d->usb_mutex); + + return res; +} + +static const struct dvb_frontend_ops nerc_ops =3D { + .delsys =3D { SYS_DTMB }, + .info =3D { + .name =3D "HDIC HD2312A (in NERC DtmbUSB)", + .frequency_min_hz =3D 52 * MHz, + .frequency_max_hz =3D 866 * MHz, + .frequency_stepsize_hz =3D 10 * kHz, + .caps =3D FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO | + FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_BANDWIDTH_AUTO | FE_CAN_GUARD_INTERVAL_AUTO + }, + .get_tune_settings =3D nerc_get_tune_settings, + .read_status =3D nerc_read_status, + .get_frontend =3D nerc_get_frontend, + .set_frontend =3D nerc_set_frontend, +}; + +static int nerc_streaming_ctrl(struct dvb_frontend *fe, int on) +{ + struct dvb_usb_device *d =3D fe_to_d(fe); + int res; + + mutex_lock(&d->usb_mutex); + res =3D nerc_control_msg(d, on ? NERC_STREAM_START : NERC_STREAM_STOP, + false, NULL, 0); + mutex_unlock(&d->usb_mutex); + + return res; +} + +static int nerc_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct nerc_priv *priv =3D adap_to_priv(adap); + struct dvb_frontend *fe =3D &priv->fe; + struct dtv_frontend_properties *c =3D &fe->dtv_property_cache; + + /* init frontend callback ops */ + memcpy(&fe->ops, &nerc_ops, sizeof(struct dvb_frontend_ops)); + + c->strength.len =3D 1; + if (priv->variant =3D=3D NERC_VARIANT_CVB) + c->strength.stat[0].scale =3D FE_SCALE_DECIBEL; + else + c->strength.stat[0].scale =3D FE_SCALE_RELATIVE; + c->cnr.len =3D 1; + c->cnr.stat[0].scale =3D FE_SCALE_DECIBEL; + + adap->fe[0] =3D fe; + return 0; +} + +static int nerc_power_ctrl(struct dvb_usb_device *d, int on) +{ + int res; + + mutex_lock(&d->usb_mutex); + res =3D nerc_control_msg(d, on ? NERC_POWER_ON : NERC_POWER_OFF, + false, NULL, 0); + mutex_unlock(&d->usb_mutex); + + return res; +} + +static int nerc_probe(struct dvb_usb_device *d) +{ + struct nerc_priv *priv =3D d_to_priv(d); + unsigned char buf[4]; + int res; + + mutex_lock(&d->usb_mutex); + res =3D nerc_control_msg(d, NERC_VERSION, true, buf, sizeof(buf)); + mutex_unlock(&d->usb_mutex); + if (res) + return res; + + if (buf[1] =3D=3D 8 && buf[2] =3D=3D 32 && buf[3] =3D=3D 68) { + if (buf[0] =3D=3D 3) + priv->variant =3D NERC_VARIANT_LETV; + else if (buf[0] =3D=3D 5) + priv->variant =3D NERC_VARIANT_AIWA; + else if (buf[0] =3D=3D 6) + priv->variant =3D NERC_VARIANT_CVB; + } + + if (priv->variant =3D=3D NERC_VARIANT_UNKNOWN) { + dev_err(&d->udev->dev, "Unknown NERC DtmbUSB v%u.%u.%u%u", + buf[0], buf[1], buf[2], buf[3]); + return -ENODEV; + } + + /* yes, missing the last dot */ + dev_info(&d->udev->dev, "NERC DtmbUSB v%u.%u.%u%u (%s)", + buf[0], buf[1], buf[2], buf[3], + nerc_variant_name(priv->variant)); + return 0; +} + +static const struct dvb_usb_device_properties nerc_props =3D { + .driver_name =3D KBUILD_MODNAME, + .owner =3D THIS_MODULE, + .adapter_nr =3D adapter_nr, + .size_of_priv =3D sizeof(struct nerc_priv), + + .streaming_ctrl =3D nerc_streaming_ctrl, + .frontend_attach =3D nerc_frontend_attach, + .power_ctrl =3D nerc_power_ctrl, + .probe =3D nerc_probe, + + .num_adapters =3D 1, + .adapter =3D { + { + .stream =3D DVB_USB_STREAM_BULK(0x82, 8, 4096), + }, + }, +}; + +static const struct usb_device_id nerc_id_table[] =3D { + { DVB_USB_DEVICE(USB_VID_CYPRESS, USB_PID_NERC_DTMBUSB, + &nerc_props, "NERC DtmbUSB", NULL) }, + { } +}; +MODULE_DEVICE_TABLE(usb, nerc_id_table); + +static struct usb_driver nerc_usb_driver =3D { + .name =3D KBUILD_MODNAME, + .id_table =3D nerc_id_table, + .probe =3D dvb_usbv2_probe, + .disconnect =3D dvb_usbv2_disconnect, + .suspend =3D dvb_usbv2_suspend, + .resume =3D dvb_usbv2_resume, + .reset_resume =3D dvb_usbv2_reset_resume, + .no_dynamic_id =3D 1, + .soft_unbind =3D 1, +}; + +module_usb_driver(nerc_usb_driver); + +MODULE_AUTHOR("David Yang "); +MODULE_DESCRIPTION("Driver for NERC DtmbUSB"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb-v2/nerc.h b/drivers/media/usb/dvb-us= b-v2/nerc.h new file mode 100644 index 000000000000..bfe3fc24e401 --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/nerc.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2026 David Yang + */ + +#ifndef _DVB_USB_NERC_H_ +#define _DVB_USB_NERC_H_ + +#include "dvb_usb.h" + +#define NERC_STREAM_START 0xab +#define NERC_STREAM_STOP 0xac +#define NERC_POWER_ON 0xad +#define NERC_POWER_OFF 0xae +#define NERC_FRONTEND 0xe7 +#define NERC_SNR 0xe8 +#define NERC_QUALITY 0xe9 +#define NERC_HAS_LOCK 0xea /* 1.7s timeout */ +#define NERC_STRENGTH 0xeb +#define NERC_HAS_SIGNAL 0xec /* 0.7s after set freq */ +#define NERC_VERSION 0xed +#define NERC_FREQ_SET 0xfc + +#define NERC_USB_TIMEOUT 2000 + +enum nerc_variant { + NERC_VARIANT_UNKNOWN, + NERC_VARIANT_LETV, + NERC_VARIANT_AIWA, + NERC_VARIANT_CVB, +}; + +struct nerc_priv { + struct dvb_frontend fe; + + unsigned char variant; + u8 buf[31]; +}; + +#endif diff --git a/include/media/dvb-usb-ids.h b/include/media/dvb-usb-ids.h index 1b7d10f3d4aa..7bc4e3c395f5 100644 --- a/include/media/dvb-usb-ids.h +++ b/include/media/dvb-usb-ids.h @@ -308,6 +308,7 @@ #define USB_PID_MYGICA_T230C2 0xc68a #define USB_PID_MYGICA_T230C2_LITE 0xc69a #define USB_PID_MYGICA_T230C_LITE 0xc699 +#define USB_PID_NERC_DTMBUSB 0x1004 #define USB_PID_NOXON_DAB_STICK 0x00b3 #define USB_PID_NOXON_DAB_STICK_REV2 0x00e0 #define USB_PID_NOXON_DAB_STICK_REV3 0x00b4 --=20 2.53.0