提交 45d80f16 编写于 作者: L Linus Torvalds

Merge tag 'media/v4.3-4' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media

Pull media fixes from Mauro Carvalho Chehab:
 "Some regression fixes and potential security issues:

   - netup_unidvb: fix potential crash when spi is NULL
   - rtl28xxu: fix control message flaws
   - m88ds3103: fix a regression on Kernel 4.2
   - c8sectpfe: fix some issues on this new driver
   - v4l2-flash-led-class: fix a Kbuild dependency
   - si2157 and si2158: check for array boundary when uploading firmware
     files
   - horus3a and lnbh25: fix some building troubles when some options
     aren't selected
   - ir-hix5hd2: drop the use of IRQF_NO_SUSPEND"

* tag 'media/v4.3-4' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media:
  [media] m88ds3103: use own reg update_bits() implementation
  [media] rtl28xxu: fix control message flaws
  [media] v4l2-flash-led-class: Add missing VIDEO_V4L2 Kconfig dependency
  [media] netup_unidvb: fix potential crash when spi is NULL
  [media] si2168: Bounds check firmware
  [media] si2157: Bounds check firmware
  [media] ir-hix5hd2: drop the use of IRQF_NO_SUSPEND
  [media] c8sectpfe: fix return of garbage
  [media] c8sectpfe: fix ininitialized error return on firmware load failure
  [media] lnbh25: Fix lnbh25_attach() function return type
  [media] horus3a: Fix horus3a_attach() function parameters
...@@ -46,8 +46,8 @@ extern struct dvb_frontend *horus3a_attach(struct dvb_frontend *fe, ...@@ -46,8 +46,8 @@ extern struct dvb_frontend *horus3a_attach(struct dvb_frontend *fe,
const struct horus3a_config *config, const struct horus3a_config *config,
struct i2c_adapter *i2c); struct i2c_adapter *i2c);
#else #else
static inline struct dvb_frontend *horus3a_attach( static inline struct dvb_frontend *horus3a_attach(struct dvb_frontend *fe,
const struct cxd2820r_config *config, const struct horus3a_config *config,
struct i2c_adapter *i2c) struct i2c_adapter *i2c)
{ {
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
......
...@@ -43,7 +43,7 @@ struct dvb_frontend *lnbh25_attach( ...@@ -43,7 +43,7 @@ struct dvb_frontend *lnbh25_attach(
struct lnbh25_config *cfg, struct lnbh25_config *cfg,
struct i2c_adapter *i2c); struct i2c_adapter *i2c);
#else #else
static inline dvb_frontend *lnbh25_attach( static inline struct dvb_frontend *lnbh25_attach(
struct dvb_frontend *fe, struct dvb_frontend *fe,
struct lnbh25_config *cfg, struct lnbh25_config *cfg,
struct i2c_adapter *i2c) struct i2c_adapter *i2c)
......
...@@ -18,6 +18,27 @@ ...@@ -18,6 +18,27 @@
static struct dvb_frontend_ops m88ds3103_ops; static struct dvb_frontend_ops m88ds3103_ops;
/* write single register with mask */
static int m88ds3103_update_bits(struct m88ds3103_dev *dev,
u8 reg, u8 mask, u8 val)
{
int ret;
u8 tmp;
/* no need for read if whole reg is written */
if (mask != 0xff) {
ret = regmap_bulk_read(dev->regmap, reg, &tmp, 1);
if (ret)
return ret;
val &= mask;
tmp &= ~mask;
val |= tmp;
}
return regmap_bulk_write(dev->regmap, reg, &val, 1);
}
/* write reg val table using reg addr auto increment */ /* write reg val table using reg addr auto increment */
static int m88ds3103_wr_reg_val_tab(struct m88ds3103_dev *dev, static int m88ds3103_wr_reg_val_tab(struct m88ds3103_dev *dev,
const struct m88ds3103_reg_val *tab, int tab_len) const struct m88ds3103_reg_val *tab, int tab_len)
...@@ -394,10 +415,10 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) ...@@ -394,10 +415,10 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
u8tmp2 = 0x00; /* 0b00 */ u8tmp2 = 0x00; /* 0b00 */
break; break;
} }
ret = regmap_update_bits(dev->regmap, 0x22, 0xc0, u8tmp1 << 6); ret = m88ds3103_update_bits(dev, 0x22, 0xc0, u8tmp1 << 6);
if (ret) if (ret)
goto err; goto err;
ret = regmap_update_bits(dev->regmap, 0x24, 0xc0, u8tmp2 << 6); ret = m88ds3103_update_bits(dev, 0x24, 0xc0, u8tmp2 << 6);
if (ret) if (ret)
goto err; goto err;
} }
...@@ -455,13 +476,13 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) ...@@ -455,13 +476,13 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
if (ret) if (ret)
goto err; goto err;
} }
ret = regmap_update_bits(dev->regmap, 0x9d, 0x08, 0x08); ret = m88ds3103_update_bits(dev, 0x9d, 0x08, 0x08);
if (ret) if (ret)
goto err; goto err;
ret = regmap_write(dev->regmap, 0xf1, 0x01); ret = regmap_write(dev->regmap, 0xf1, 0x01);
if (ret) if (ret)
goto err; goto err;
ret = regmap_update_bits(dev->regmap, 0x30, 0x80, 0x80); ret = m88ds3103_update_bits(dev, 0x30, 0x80, 0x80);
if (ret) if (ret)
goto err; goto err;
} }
...@@ -498,7 +519,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) ...@@ -498,7 +519,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
switch (dev->cfg->ts_mode) { switch (dev->cfg->ts_mode) {
case M88DS3103_TS_SERIAL: case M88DS3103_TS_SERIAL:
case M88DS3103_TS_SERIAL_D7: case M88DS3103_TS_SERIAL_D7:
ret = regmap_update_bits(dev->regmap, 0x29, 0x20, u8tmp1); ret = m88ds3103_update_bits(dev, 0x29, 0x20, u8tmp1);
if (ret) if (ret)
goto err; goto err;
u8tmp1 = 0; u8tmp1 = 0;
...@@ -567,11 +588,11 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) ...@@ -567,11 +588,11 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
if (ret) if (ret)
goto err; goto err;
ret = regmap_update_bits(dev->regmap, 0x4d, 0x02, dev->cfg->spec_inv << 1); ret = m88ds3103_update_bits(dev, 0x4d, 0x02, dev->cfg->spec_inv << 1);
if (ret) if (ret)
goto err; goto err;
ret = regmap_update_bits(dev->regmap, 0x30, 0x10, dev->cfg->agc_inv << 4); ret = m88ds3103_update_bits(dev, 0x30, 0x10, dev->cfg->agc_inv << 4);
if (ret) if (ret)
goto err; goto err;
...@@ -625,13 +646,13 @@ static int m88ds3103_init(struct dvb_frontend *fe) ...@@ -625,13 +646,13 @@ static int m88ds3103_init(struct dvb_frontend *fe)
dev->warm = false; dev->warm = false;
/* wake up device from sleep */ /* wake up device from sleep */
ret = regmap_update_bits(dev->regmap, 0x08, 0x01, 0x01); ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x01);
if (ret) if (ret)
goto err; goto err;
ret = regmap_update_bits(dev->regmap, 0x04, 0x01, 0x00); ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x00);
if (ret) if (ret)
goto err; goto err;
ret = regmap_update_bits(dev->regmap, 0x23, 0x10, 0x00); ret = m88ds3103_update_bits(dev, 0x23, 0x10, 0x00);
if (ret) if (ret)
goto err; goto err;
...@@ -749,18 +770,18 @@ static int m88ds3103_sleep(struct dvb_frontend *fe) ...@@ -749,18 +770,18 @@ static int m88ds3103_sleep(struct dvb_frontend *fe)
utmp = 0x29; utmp = 0x29;
else else
utmp = 0x27; utmp = 0x27;
ret = regmap_update_bits(dev->regmap, utmp, 0x01, 0x00); ret = m88ds3103_update_bits(dev, utmp, 0x01, 0x00);
if (ret) if (ret)
goto err; goto err;
/* sleep */ /* sleep */
ret = regmap_update_bits(dev->regmap, 0x08, 0x01, 0x00); ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x00);
if (ret) if (ret)
goto err; goto err;
ret = regmap_update_bits(dev->regmap, 0x04, 0x01, 0x01); ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x01);
if (ret) if (ret)
goto err; goto err;
ret = regmap_update_bits(dev->regmap, 0x23, 0x10, 0x10); ret = m88ds3103_update_bits(dev, 0x23, 0x10, 0x10);
if (ret) if (ret)
goto err; goto err;
...@@ -992,12 +1013,12 @@ static int m88ds3103_set_tone(struct dvb_frontend *fe, ...@@ -992,12 +1013,12 @@ static int m88ds3103_set_tone(struct dvb_frontend *fe,
} }
utmp = tone << 7 | dev->cfg->envelope_mode << 5; utmp = tone << 7 | dev->cfg->envelope_mode << 5;
ret = regmap_update_bits(dev->regmap, 0xa2, 0xe0, utmp); ret = m88ds3103_update_bits(dev, 0xa2, 0xe0, utmp);
if (ret) if (ret)
goto err; goto err;
utmp = 1 << 2; utmp = 1 << 2;
ret = regmap_update_bits(dev->regmap, 0xa1, reg_a1_mask, utmp); ret = m88ds3103_update_bits(dev, 0xa1, reg_a1_mask, utmp);
if (ret) if (ret)
goto err; goto err;
...@@ -1047,7 +1068,7 @@ static int m88ds3103_set_voltage(struct dvb_frontend *fe, ...@@ -1047,7 +1068,7 @@ static int m88ds3103_set_voltage(struct dvb_frontend *fe,
voltage_dis ^= dev->cfg->lnb_en_pol; voltage_dis ^= dev->cfg->lnb_en_pol;
utmp = voltage_dis << 1 | voltage_sel << 0; utmp = voltage_dis << 1 | voltage_sel << 0;
ret = regmap_update_bits(dev->regmap, 0xa2, 0x03, utmp); ret = m88ds3103_update_bits(dev, 0xa2, 0x03, utmp);
if (ret) if (ret)
goto err; goto err;
...@@ -1080,7 +1101,7 @@ static int m88ds3103_diseqc_send_master_cmd(struct dvb_frontend *fe, ...@@ -1080,7 +1101,7 @@ static int m88ds3103_diseqc_send_master_cmd(struct dvb_frontend *fe,
} }
utmp = dev->cfg->envelope_mode << 5; utmp = dev->cfg->envelope_mode << 5;
ret = regmap_update_bits(dev->regmap, 0xa2, 0xe0, utmp); ret = m88ds3103_update_bits(dev, 0xa2, 0xe0, utmp);
if (ret) if (ret)
goto err; goto err;
...@@ -1115,12 +1136,12 @@ static int m88ds3103_diseqc_send_master_cmd(struct dvb_frontend *fe, ...@@ -1115,12 +1136,12 @@ static int m88ds3103_diseqc_send_master_cmd(struct dvb_frontend *fe,
} else { } else {
dev_dbg(&client->dev, "diseqc tx timeout\n"); dev_dbg(&client->dev, "diseqc tx timeout\n");
ret = regmap_update_bits(dev->regmap, 0xa1, 0xc0, 0x40); ret = m88ds3103_update_bits(dev, 0xa1, 0xc0, 0x40);
if (ret) if (ret)
goto err; goto err;
} }
ret = regmap_update_bits(dev->regmap, 0xa2, 0xc0, 0x80); ret = m88ds3103_update_bits(dev, 0xa2, 0xc0, 0x80);
if (ret) if (ret)
goto err; goto err;
...@@ -1152,7 +1173,7 @@ static int m88ds3103_diseqc_send_burst(struct dvb_frontend *fe, ...@@ -1152,7 +1173,7 @@ static int m88ds3103_diseqc_send_burst(struct dvb_frontend *fe,
} }
utmp = dev->cfg->envelope_mode << 5; utmp = dev->cfg->envelope_mode << 5;
ret = regmap_update_bits(dev->regmap, 0xa2, 0xe0, utmp); ret = m88ds3103_update_bits(dev, 0xa2, 0xe0, utmp);
if (ret) if (ret)
goto err; goto err;
...@@ -1194,12 +1215,12 @@ static int m88ds3103_diseqc_send_burst(struct dvb_frontend *fe, ...@@ -1194,12 +1215,12 @@ static int m88ds3103_diseqc_send_burst(struct dvb_frontend *fe,
} else { } else {
dev_dbg(&client->dev, "diseqc tx timeout\n"); dev_dbg(&client->dev, "diseqc tx timeout\n");
ret = regmap_update_bits(dev->regmap, 0xa1, 0xc0, 0x40); ret = m88ds3103_update_bits(dev, 0xa1, 0xc0, 0x40);
if (ret) if (ret)
goto err; goto err;
} }
ret = regmap_update_bits(dev->regmap, 0xa2, 0xc0, 0x80); ret = m88ds3103_update_bits(dev, 0xa2, 0xc0, 0x80);
if (ret) if (ret)
goto err; goto err;
...@@ -1435,13 +1456,13 @@ static int m88ds3103_probe(struct i2c_client *client, ...@@ -1435,13 +1456,13 @@ static int m88ds3103_probe(struct i2c_client *client,
goto err_kfree; goto err_kfree;
/* sleep */ /* sleep */
ret = regmap_update_bits(dev->regmap, 0x08, 0x01, 0x00); ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x00);
if (ret) if (ret)
goto err_kfree; goto err_kfree;
ret = regmap_update_bits(dev->regmap, 0x04, 0x01, 0x01); ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x01);
if (ret) if (ret)
goto err_kfree; goto err_kfree;
ret = regmap_update_bits(dev->regmap, 0x23, 0x10, 0x10); ret = m88ds3103_update_bits(dev, 0x23, 0x10, 0x10);
if (ret) if (ret)
goto err_kfree; goto err_kfree;
......
...@@ -502,6 +502,10 @@ static int si2168_init(struct dvb_frontend *fe) ...@@ -502,6 +502,10 @@ static int si2168_init(struct dvb_frontend *fe)
/* firmware is in the new format */ /* firmware is in the new format */
for (remaining = fw->size; remaining > 0; remaining -= 17) { for (remaining = fw->size; remaining > 0; remaining -= 17) {
len = fw->data[fw->size - remaining]; len = fw->data[fw->size - remaining];
if (len > SI2168_ARGLEN) {
ret = -EINVAL;
break;
}
memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len); memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len);
cmd.wlen = len; cmd.wlen = len;
cmd.rlen = 1; cmd.rlen = 1;
......
...@@ -80,11 +80,9 @@ irqreturn_t netup_spi_interrupt(struct netup_spi *spi) ...@@ -80,11 +80,9 @@ irqreturn_t netup_spi_interrupt(struct netup_spi *spi)
u16 reg; u16 reg;
unsigned long flags; unsigned long flags;
if (!spi) { if (!spi)
dev_dbg(&spi->master->dev,
"%s(): SPI not initialized\n", __func__);
return IRQ_NONE; return IRQ_NONE;
}
spin_lock_irqsave(&spi->lock, flags); spin_lock_irqsave(&spi->lock, flags);
reg = readw(&spi->regs->control_stat); reg = readw(&spi->regs->control_stat);
if (!(reg & NETUP_SPI_CTRL_IRQ)) { if (!(reg & NETUP_SPI_CTRL_IRQ)) {
...@@ -234,11 +232,9 @@ void netup_spi_release(struct netup_unidvb_dev *ndev) ...@@ -234,11 +232,9 @@ void netup_spi_release(struct netup_unidvb_dev *ndev)
unsigned long flags; unsigned long flags;
struct netup_spi *spi = ndev->spi; struct netup_spi *spi = ndev->spi;
if (!spi) { if (!spi)
dev_dbg(&spi->master->dev,
"%s(): SPI not initialized\n", __func__);
return; return;
}
spin_lock_irqsave(&spi->lock, flags); spin_lock_irqsave(&spi->lock, flags);
reg = readw(&spi->regs->control_stat); reg = readw(&spi->regs->control_stat);
writew(reg | NETUP_SPI_CTRL_IRQ, &spi->regs->control_stat); writew(reg | NETUP_SPI_CTRL_IRQ, &spi->regs->control_stat);
......
...@@ -1097,7 +1097,7 @@ static int load_slim_core_fw(const struct firmware *fw, void *context) ...@@ -1097,7 +1097,7 @@ static int load_slim_core_fw(const struct firmware *fw, void *context)
Elf32_Ehdr *ehdr; Elf32_Ehdr *ehdr;
Elf32_Phdr *phdr; Elf32_Phdr *phdr;
u8 __iomem *dst; u8 __iomem *dst;
int err, i; int err = 0, i;
if (!fw || !context) if (!fw || !context)
return -EINVAL; return -EINVAL;
...@@ -1106,7 +1106,7 @@ static int load_slim_core_fw(const struct firmware *fw, void *context) ...@@ -1106,7 +1106,7 @@ static int load_slim_core_fw(const struct firmware *fw, void *context)
phdr = (Elf32_Phdr *)(fw->data + ehdr->e_phoff); phdr = (Elf32_Phdr *)(fw->data + ehdr->e_phoff);
/* go through the available ELF segments */ /* go through the available ELF segments */
for (i = 0; i < ehdr->e_phnum && !err; i++, phdr++) { for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
/* Only consider LOAD segments */ /* Only consider LOAD segments */
if (phdr->p_type != PT_LOAD) if (phdr->p_type != PT_LOAD)
...@@ -1192,7 +1192,6 @@ static void load_c8sectpfe_fw_cb(const struct firmware *fw, void *context) ...@@ -1192,7 +1192,6 @@ static void load_c8sectpfe_fw_cb(const struct firmware *fw, void *context)
static int load_c8sectpfe_fw_step1(struct c8sectpfei *fei) static int load_c8sectpfe_fw_step1(struct c8sectpfei *fei)
{ {
int ret;
int err; int err;
dev_info(fei->dev, "Loading firmware: %s\n", FIRMWARE_MEMDMA); dev_info(fei->dev, "Loading firmware: %s\n", FIRMWARE_MEMDMA);
...@@ -1207,7 +1206,7 @@ static int load_c8sectpfe_fw_step1(struct c8sectpfei *fei) ...@@ -1207,7 +1206,7 @@ static int load_c8sectpfe_fw_step1(struct c8sectpfei *fei)
if (err) { if (err) {
dev_err(fei->dev, "request_firmware_nowait err: %d.\n", err); dev_err(fei->dev, "request_firmware_nowait err: %d.\n", err);
complete_all(&fei->fw_ack); complete_all(&fei->fw_ack);
return ret; return err;
} }
return 0; return 0;
......
...@@ -257,7 +257,7 @@ static int hix5hd2_ir_probe(struct platform_device *pdev) ...@@ -257,7 +257,7 @@ static int hix5hd2_ir_probe(struct platform_device *pdev)
goto clkerr; goto clkerr;
if (devm_request_irq(dev, priv->irq, hix5hd2_ir_rx_interrupt, if (devm_request_irq(dev, priv->irq, hix5hd2_ir_rx_interrupt,
IRQF_NO_SUSPEND, pdev->name, priv) < 0) { 0, pdev->name, priv) < 0) {
dev_err(dev, "IRQ %d register failed\n", priv->irq); dev_err(dev, "IRQ %d register failed\n", priv->irq);
ret = -EINVAL; ret = -EINVAL;
goto regerr; goto regerr;
......
...@@ -166,6 +166,10 @@ static int si2157_init(struct dvb_frontend *fe) ...@@ -166,6 +166,10 @@ static int si2157_init(struct dvb_frontend *fe)
for (remaining = fw->size; remaining > 0; remaining -= 17) { for (remaining = fw->size; remaining > 0; remaining -= 17) {
len = fw->data[fw->size - remaining]; len = fw->data[fw->size - remaining];
if (len > SI2157_ARGLEN) {
dev_err(&client->dev, "Bad firmware length\n");
goto err_release_firmware;
}
memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len); memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len);
cmd.wlen = len; cmd.wlen = len;
cmd.rlen = 1; cmd.rlen = 1;
......
...@@ -34,6 +34,14 @@ static int rtl28xxu_ctrl_msg(struct dvb_usb_device *d, struct rtl28xxu_req *req) ...@@ -34,6 +34,14 @@ static int rtl28xxu_ctrl_msg(struct dvb_usb_device *d, struct rtl28xxu_req *req)
unsigned int pipe; unsigned int pipe;
u8 requesttype; u8 requesttype;
mutex_lock(&d->usb_mutex);
if (req->size > sizeof(dev->buf)) {
dev_err(&d->intf->dev, "too large message %u\n", req->size);
ret = -EINVAL;
goto err_mutex_unlock;
}
if (req->index & CMD_WR_FLAG) { if (req->index & CMD_WR_FLAG) {
/* write */ /* write */
memcpy(dev->buf, req->data, req->size); memcpy(dev->buf, req->data, req->size);
...@@ -50,14 +58,17 @@ static int rtl28xxu_ctrl_msg(struct dvb_usb_device *d, struct rtl28xxu_req *req) ...@@ -50,14 +58,17 @@ static int rtl28xxu_ctrl_msg(struct dvb_usb_device *d, struct rtl28xxu_req *req)
dvb_usb_dbg_usb_control_msg(d->udev, 0, requesttype, req->value, dvb_usb_dbg_usb_control_msg(d->udev, 0, requesttype, req->value,
req->index, dev->buf, req->size); req->index, dev->buf, req->size);
if (ret < 0) if (ret < 0)
goto err; goto err_mutex_unlock;
/* read request, copy returned data to return buf */ /* read request, copy returned data to return buf */
if (requesttype == (USB_TYPE_VENDOR | USB_DIR_IN)) if (requesttype == (USB_TYPE_VENDOR | USB_DIR_IN))
memcpy(req->data, dev->buf, req->size); memcpy(req->data, dev->buf, req->size);
mutex_unlock(&d->usb_mutex);
return 0; return 0;
err: err_mutex_unlock:
mutex_unlock(&d->usb_mutex);
dev_dbg(&d->intf->dev, "failed=%d\n", ret); dev_dbg(&d->intf->dev, "failed=%d\n", ret);
return ret; return ret;
} }
......
...@@ -71,7 +71,7 @@ ...@@ -71,7 +71,7 @@
struct rtl28xxu_dev { struct rtl28xxu_dev {
u8 buf[28]; u8 buf[128];
u8 chip_id; u8 chip_id;
u8 tuner; u8 tuner;
char *tuner_name; char *tuner_name;
......
...@@ -47,7 +47,7 @@ config V4L2_MEM2MEM_DEV ...@@ -47,7 +47,7 @@ config V4L2_MEM2MEM_DEV
# Used by LED subsystem flash drivers # Used by LED subsystem flash drivers
config V4L2_FLASH_LED_CLASS config V4L2_FLASH_LED_CLASS
tristate "V4L2 flash API for LED flash class devices" tristate "V4L2 flash API for LED flash class devices"
depends on VIDEO_V4L2_SUBDEV_API depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
depends on LEDS_CLASS_FLASH depends on LEDS_CLASS_FLASH
---help--- ---help---
Say Y here to enable V4L2 flash API support for LED flash Say Y here to enable V4L2 flash API support for LED flash
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册