Commit 659d4954 authored by Kuninori Morimoto's avatar Kuninori Morimoto Committed by Greg Kroah-Hartman

usb: renesas_usbhs: modify data transfer method

On current driver, main data transfer function was implemented in fifo.c,
but the overall controlling was implementing in mod_gadget.c.
This style is not useful to support host and DMAEngine in the future.

But the interrupt for data transfer cannot separate easily for now,
because it is deeply related to mod_gadget.

This patch move the overall data transfer method
into fifo.c except interrupt.
Signed-off-by: default avatarKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 6acb95d4
...@@ -27,20 +27,17 @@ void usbhs_pkt_init(struct usbhs_pkt *pkt) ...@@ -27,20 +27,17 @@ void usbhs_pkt_init(struct usbhs_pkt *pkt)
INIT_LIST_HEAD(&pkt->node); INIT_LIST_HEAD(&pkt->node);
} }
void usbhs_pkt_update(struct usbhs_pkt *pkt, void *buf, int len) void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt,
{ void *buf, int len, int zero)
pkt->buf = buf;
pkt->length = len;
pkt->actual = 0;
pkt->maxp = 0;
}
void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt)
{ {
list_del_init(&pkt->node); list_del_init(&pkt->node);
list_add_tail(&pkt->node, &pipe->list); list_add_tail(&pkt->node, &pipe->list);
pkt->pipe = pipe; pkt->pipe = pipe;
pkt->buf = buf;
pkt->length = len;
pkt->zero = zero;
pkt->actual = 0;
} }
void usbhs_pkt_pop(struct usbhs_pkt *pkt) void usbhs_pkt_pop(struct usbhs_pkt *pkt)
...@@ -56,6 +53,47 @@ struct usbhs_pkt *usbhs_pkt_get(struct usbhs_pipe *pipe) ...@@ -56,6 +53,47 @@ struct usbhs_pkt *usbhs_pkt_get(struct usbhs_pipe *pipe)
return list_entry(pipe->list.next, struct usbhs_pkt, node); return list_entry(pipe->list.next, struct usbhs_pkt, node);
} }
/*
* irq enable/disable function
*/
#define usbhsf_irq_empty_ctrl(p, e) usbhsf_irq_callback_ctrl(p, bempsts, e)
#define usbhsf_irq_ready_ctrl(p, e) usbhsf_irq_callback_ctrl(p, brdysts, e)
#define usbhsf_irq_callback_ctrl(pipe, status, enable) \
({ \
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); \
struct usbhs_mod *mod = usbhs_mod_get_current(priv); \
u16 status = (1 << usbhs_pipe_number(pipe)); \
if (!mod) \
return; \
if (enable) \
mod->irq_##status |= status; \
else \
mod->irq_##status &= ~status; \
usbhs_irq_callback_update(priv, mod); \
})
static void usbhsf_tx_irq_ctrl(struct usbhs_pipe *pipe, int enable)
{
/*
* And DCP pipe can NOT use "ready interrupt" for "send"
* it should use "empty" interrupt.
* see
* "Operation" - "Interrupt Function" - "BRDY Interrupt"
*
* on the other hand, normal pipe can use "ready interrupt" for "send"
* even though it is single/double buffer
*/
if (usbhs_pipe_is_dcp(pipe))
usbhsf_irq_empty_ctrl(pipe, enable);
else
usbhsf_irq_ready_ctrl(pipe, enable);
}
static void usbhsf_rx_irq_ctrl(struct usbhs_pipe *pipe, int enable)
{
usbhsf_irq_ready_ctrl(pipe, enable);
}
/* /*
* FIFO ctrl * FIFO ctrl
*/ */
...@@ -135,34 +173,38 @@ int usbhs_fifo_write(struct usbhs_pkt *pkt) ...@@ -135,34 +173,38 @@ int usbhs_fifo_write(struct usbhs_pkt *pkt)
struct usbhs_pipe *pipe = pkt->pipe; struct usbhs_pipe *pipe = pkt->pipe;
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv); struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv);
struct device *dev = usbhs_priv_to_dev(priv);
void __iomem *addr = priv->base + CFIFO; void __iomem *addr = priv->base + CFIFO;
u8 *buf;
int maxp = usbhs_pipe_get_maxpacket(pipe); int maxp = usbhs_pipe_get_maxpacket(pipe);
int total_len; int total_len;
u8 *buf = pkt->buf;
int i, ret, len; int i, ret, len;
int is_short, is_done;
ret = usbhs_pipe_is_accessible(pipe); ret = usbhs_pipe_is_accessible(pipe);
if (ret < 0) if (ret < 0)
return ret; goto usbhs_fifo_write_busy;
ret = usbhsf_fifo_select(pipe, 1); ret = usbhsf_fifo_select(pipe, 1);
if (ret < 0) if (ret < 0)
return ret; goto usbhs_fifo_write_busy;
ret = usbhsf_fifo_barrier(priv); ret = usbhsf_fifo_barrier(priv);
if (ret < 0) if (ret < 0)
return ret; goto usbhs_fifo_write_busy;
len = min(pkt->length, maxp); buf = pkt->buf + pkt->actual;
total_len = len; len = pkt->length - pkt->actual;
len = min(len, maxp);
total_len = len;
is_short = total_len < maxp;
/* /*
* FIXME * FIXME
* *
* 32-bit access only * 32-bit access only
*/ */
if (len >= 4 && if (len >= 4 && !((unsigned long)buf & 0x03)) {
!((unsigned long)buf & 0x03)) {
iowrite32_rep(addr, buf, len / 4); iowrite32_rep(addr, buf, len / 4);
len %= 4; len %= 4;
buf += total_len - len; buf += total_len - len;
...@@ -172,19 +214,52 @@ int usbhs_fifo_write(struct usbhs_pkt *pkt) ...@@ -172,19 +214,52 @@ int usbhs_fifo_write(struct usbhs_pkt *pkt)
for (i = 0; i < len; i++) for (i = 0; i < len; i++)
iowrite8(buf[i], addr + (0x03 - (i & 0x03))); iowrite8(buf[i], addr + (0x03 - (i & 0x03)));
if (total_len < maxp) /*
* variable update
*/
pkt->actual += total_len;
if (pkt->actual < pkt->length)
is_done = 0; /* there are remainder data */
else if (is_short)
is_done = 1; /* short packet */
else
is_done = !pkt->zero; /* send zero packet ? */
/*
* pipe/irq handling
*/
if (is_short)
usbhsf_send_terminator(pipe); usbhsf_send_terminator(pipe);
usbhsf_tx_irq_ctrl(pipe, !is_done);
usbhs_pipe_enable(pipe); usbhs_pipe_enable(pipe);
/* update pkt */ dev_dbg(dev, " send %d (%d/ %d/ %d/ %d)\n",
if (info->tx_done) { usbhs_pipe_number(pipe),
pkt->actual = total_len; pkt->length, pkt->actual, is_done, pkt->zero);
pkt->maxp = maxp;
info->tx_done(pkt); /*
* Transmission end
*/
if (is_done) {
if (usbhs_pipe_is_dcp(pipe))
usbhs_dcp_control_transfer_done(pipe);
if (info->tx_done)
info->tx_done(pkt);
} }
return 0; return 0;
usbhs_fifo_write_busy:
/*
* pipe is busy.
* retry in interrupt
*/
usbhsf_tx_irq_ctrl(pipe, 1);
return ret;
} }
int usbhs_fifo_prepare_read(struct usbhs_pipe *pipe) int usbhs_fifo_prepare_read(struct usbhs_pipe *pipe)
...@@ -199,6 +274,7 @@ int usbhs_fifo_prepare_read(struct usbhs_pipe *pipe) ...@@ -199,6 +274,7 @@ int usbhs_fifo_prepare_read(struct usbhs_pipe *pipe)
return ret; return ret;
usbhs_pipe_enable(pipe); usbhs_pipe_enable(pipe);
usbhsf_rx_irq_ctrl(pipe, 1);
return ret; return ret;
} }
...@@ -207,13 +283,15 @@ int usbhs_fifo_read(struct usbhs_pkt *pkt) ...@@ -207,13 +283,15 @@ int usbhs_fifo_read(struct usbhs_pkt *pkt)
{ {
struct usbhs_pipe *pipe = pkt->pipe; struct usbhs_pipe *pipe = pkt->pipe;
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv); struct device *dev = usbhs_priv_to_dev(priv);
void __iomem *addr = priv->base + CFIFO; void __iomem *addr = priv->base + CFIFO;
u8 *buf = pkt->buf; u8 *buf;
u32 data = 0;
int maxp = usbhs_pipe_get_maxpacket(pipe);
int rcv_len, len; int rcv_len, len;
int i, ret; int i, ret;
int total_len = 0; int total_len = 0;
u32 data = 0; int is_done = 0;
ret = usbhsf_fifo_select(pipe, 0); ret = usbhsf_fifo_select(pipe, 0);
if (ret < 0) if (ret < 0)
...@@ -225,6 +303,11 @@ int usbhs_fifo_read(struct usbhs_pkt *pkt) ...@@ -225,6 +303,11 @@ int usbhs_fifo_read(struct usbhs_pkt *pkt)
rcv_len = usbhsf_fifo_rcv_len(priv); rcv_len = usbhsf_fifo_rcv_len(priv);
buf = pkt->buf + pkt->actual;
len = pkt->length - pkt->actual;
len = min(len, rcv_len);
total_len = len;
/* /*
* Buffer clear if Zero-Length packet * Buffer clear if Zero-Length packet
* *
...@@ -236,19 +319,15 @@ int usbhs_fifo_read(struct usbhs_pkt *pkt) ...@@ -236,19 +319,15 @@ int usbhs_fifo_read(struct usbhs_pkt *pkt)
goto usbhs_fifo_read_end; goto usbhs_fifo_read_end;
} }
len = min(rcv_len, pkt->length);
total_len = len;
/* /*
* FIXME * FIXME
* *
* 32-bit access only * 32-bit access only
*/ */
if (len >= 4 && if (len >= 4 && !((unsigned long)buf & 0x03)) {
!((unsigned long)buf & 0x03)) {
ioread32_rep(addr, buf, len / 4); ioread32_rep(addr, buf, len / 4);
len %= 4; len %= 4;
buf += rcv_len - len; buf += total_len - len;
} }
/* the rest operation */ /* the rest operation */
...@@ -259,12 +338,25 @@ int usbhs_fifo_read(struct usbhs_pkt *pkt) ...@@ -259,12 +338,25 @@ int usbhs_fifo_read(struct usbhs_pkt *pkt)
buf[i] = (data >> ((i & 0x03) * 8)) & 0xff; buf[i] = (data >> ((i & 0x03) * 8)) & 0xff;
} }
pkt->actual += total_len;
usbhs_fifo_read_end: usbhs_fifo_read_end:
if (info->rx_done) { if ((pkt->actual == pkt->length) || /* receive all data */
/* update pkt */ (total_len < maxp)) /* short packet */
pkt->actual = total_len; is_done = 1;
pkt->maxp = usbhs_pipe_get_maxpacket(pipe);
info->rx_done(pkt); dev_dbg(dev, " recv %d (%d/ %d/ %d/ %d)\n",
usbhs_pipe_number(pipe),
pkt->length, pkt->actual, is_done, pkt->zero);
if (is_done) {
struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv);
usbhsf_rx_irq_ctrl(pipe, 0);
usbhs_pipe_disable(pipe);
if (info->rx_done)
info->rx_done(pkt);
} }
return 0; return 0;
......
...@@ -22,10 +22,10 @@ ...@@ -22,10 +22,10 @@
struct usbhs_pkt { struct usbhs_pkt {
struct list_head node; struct list_head node;
struct usbhs_pipe *pipe; struct usbhs_pipe *pipe;
int maxp;
void *buf; void *buf;
int length; int length;
int actual; int actual;
int zero;
}; };
/* /*
...@@ -40,8 +40,8 @@ int usbhs_fifo_prepare_read(struct usbhs_pipe *pipe); ...@@ -40,8 +40,8 @@ int usbhs_fifo_prepare_read(struct usbhs_pipe *pipe);
* packet info * packet info
*/ */
void usbhs_pkt_init(struct usbhs_pkt *pkt); void usbhs_pkt_init(struct usbhs_pkt *pkt);
void usbhs_pkt_update(struct usbhs_pkt *pkt, void *buf, int len); void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt,
void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt); void *buf, int len, int zero);
void usbhs_pkt_pop(struct usbhs_pkt *pkt); void usbhs_pkt_pop(struct usbhs_pkt *pkt);
struct usbhs_pkt *usbhs_pkt_get(struct usbhs_pipe *pipe); struct usbhs_pkt *usbhs_pkt_get(struct usbhs_pipe *pipe);
......
...@@ -60,7 +60,6 @@ struct usbhsg_gpriv { ...@@ -60,7 +60,6 @@ struct usbhsg_gpriv {
struct usbhsg_pipe_handle { struct usbhsg_pipe_handle {
int (*prepare)(struct usbhsg_uep *uep, struct usbhsg_request *ureq); int (*prepare)(struct usbhsg_uep *uep, struct usbhsg_request *ureq);
int (*try_run)(struct usbhsg_uep *uep, struct usbhsg_request *ureq); int (*try_run)(struct usbhsg_uep *uep, struct usbhsg_request *ureq);
void (*irq_mask)(struct usbhsg_uep *uep, int enable);
}; };
struct usbhsg_recip_handle { struct usbhsg_recip_handle {
...@@ -160,17 +159,18 @@ static void usbhsg_queue_push(struct usbhsg_uep *uep, ...@@ -160,17 +159,18 @@ static void usbhsg_queue_push(struct usbhsg_uep *uep,
struct device *dev = usbhsg_gpriv_to_dev(gpriv); struct device *dev = usbhsg_gpriv_to_dev(gpriv);
struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
struct usbhs_pkt *pkt = usbhsg_ureq_to_pkt(ureq); struct usbhs_pkt *pkt = usbhsg_ureq_to_pkt(ureq);
struct usb_request *req = &ureq->req;
/* /*
********* assume under spin lock ********* ********* assume under spin lock *********
*/ */
usbhs_pkt_push(pipe, pkt); usbhs_pkt_push(pipe, pkt, req->buf, req->length, req->zero);
ureq->req.actual = 0; req->actual = 0;
ureq->req.status = -EINPROGRESS; req->status = -EINPROGRESS;
dev_dbg(dev, "pipe %d : queue push (%d)\n", dev_dbg(dev, "pipe %d : queue push (%d)\n",
usbhs_pipe_number(pipe), usbhs_pipe_number(pipe),
ureq->req.length); req->length);
} }
static struct usbhsg_request *usbhsg_queue_get(struct usbhsg_uep *uep) static struct usbhsg_request *usbhsg_queue_get(struct usbhsg_uep *uep)
...@@ -329,83 +329,27 @@ static int usbhsg_try_run_ctrl_stage_end(struct usbhsg_uep *uep, ...@@ -329,83 +329,27 @@ static int usbhsg_try_run_ctrl_stage_end(struct usbhsg_uep *uep,
/* /*
* packet send hander * packet send hander
*/ */
static void usbhsg_try_run_send_packet_bh(struct usbhs_pkt *pkt) static void usbhsg_send_packet_done(struct usbhs_pkt *pkt)
{ {
struct usbhs_pipe *pipe = pkt->pipe; struct usbhs_pipe *pipe = pkt->pipe;
struct usbhsg_uep *uep = usbhsg_pipe_to_uep(pipe); struct usbhsg_uep *uep = usbhsg_pipe_to_uep(pipe);
struct usbhsg_request *ureq = usbhsg_pkt_to_ureq(pkt); struct usbhsg_request *ureq = usbhsg_pkt_to_ureq(pkt);
struct usb_request *req = &ureq->req;
struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
struct device *dev = usbhsg_gpriv_to_dev(gpriv);
int remainder, send, maxp;
int is_done = 0;
int enable;
maxp = pkt->maxp; ureq->req.actual = pkt->actual;
send = pkt->actual;
remainder = pkt->length;
/* usbhsg_queue_pop(uep, ureq, 0);
* send = 0 : send zero packet
* send > 0 : send data
*
* send <= max_packet
*/
req->actual += send;
/* send all packet ? */
if (send < remainder)
is_done = 0; /* there are remainder data */
else if (send < maxp)
is_done = 1; /* short packet */
else
is_done = !req->zero; /* send zero packet ? */
dev_dbg(dev, " send %d (%d/ %d/ %d/ %d)\n",
usbhs_pipe_number(pipe),
remainder, send, is_done, req->zero);
/*
* enable interrupt and send again in irq handler
* if it still have remainder data which should be sent.
*/
enable = !is_done;
uep->handler->irq_mask(uep, enable);
/*
* all data were sent ?
*/
if (is_done) {
/* it care below call in
"function mode" */
if (usbhsg_is_dcp(uep))
usbhs_dcp_control_transfer_done(pipe);
usbhsg_queue_pop(uep, ureq, 0);
}
} }
static int usbhsg_try_run_send_packet(struct usbhsg_uep *uep, static int usbhsg_try_run_send_packet(struct usbhsg_uep *uep,
struct usbhsg_request *ureq) struct usbhsg_request *ureq)
{ {
struct usb_request *req = &ureq->req;
struct usbhs_pkt *pkt = usbhsg_ureq_to_pkt(ureq); struct usbhs_pkt *pkt = usbhsg_ureq_to_pkt(ureq);
int ret;
/* /*
********* assume under spin lock ********* ********* assume under spin lock *********
*/ */
usbhs_pkt_update(pkt, usbhs_fifo_write(pkt);
req->buf + req->actual,
req->length - req->actual);
ret = usbhs_fifo_write(pkt);
if (ret < 0) {
/* pipe is busy.
* retry in interrupt */
uep->handler->irq_mask(uep, 1);
}
return 0; return 0;
} }
...@@ -428,63 +372,26 @@ static int usbhsg_prepare_send_packet(struct usbhsg_uep *uep, ...@@ -428,63 +372,26 @@ static int usbhsg_prepare_send_packet(struct usbhsg_uep *uep,
/* /*
* packet recv hander * packet recv hander
*/ */
static void usbhsg_try_run_receive_packet_bh(struct usbhs_pkt *pkt) static void usbhsg_receive_packet_done(struct usbhs_pkt *pkt)
{ {
struct usbhs_pipe *pipe = pkt->pipe; struct usbhs_pipe *pipe = pkt->pipe;
struct usbhsg_uep *uep = usbhsg_pipe_to_uep(pipe); struct usbhsg_uep *uep = usbhsg_pipe_to_uep(pipe);
struct usbhsg_request *ureq = usbhsg_pkt_to_ureq(pkt); struct usbhsg_request *ureq = usbhsg_pkt_to_ureq(pkt);
struct usb_request *req = &ureq->req;
struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
struct device *dev = usbhsg_gpriv_to_dev(gpriv);
int remainder, recv, maxp;
int is_done = 0;
maxp = pkt->maxp;
remainder = pkt->length;
recv = pkt->actual;
/*
* recv < 0 : pipe busy
* recv >= 0 : receive data
*
* recv <= max_packet
*/
/* update parameters */
req->actual += recv;
if ((recv == remainder) || /* receive all data */
(recv < maxp)) /* short packet */
is_done = 1;
dev_dbg(dev, " recv %d (%d/ %d/ %d/ %d)\n", ureq->req.actual = pkt->actual;
usbhs_pipe_number(pipe),
remainder, recv, is_done, req->zero);
/* read all data ? */
if (is_done) {
int disable = 0;
uep->handler->irq_mask(uep, disable); usbhsg_queue_pop(uep, ureq, 0);
usbhs_pipe_disable(pipe);
usbhsg_queue_pop(uep, ureq, 0);
}
} }
static int usbhsg_try_run_receive_packet(struct usbhsg_uep *uep, static int usbhsg_try_run_receive_packet(struct usbhsg_uep *uep,
struct usbhsg_request *ureq) struct usbhsg_request *ureq)
{ {
struct usb_request *req = &ureq->req;
struct usbhs_pkt *pkt = usbhsg_ureq_to_pkt(ureq); struct usbhs_pkt *pkt = usbhsg_ureq_to_pkt(ureq);
/* /*
********* assume under spin lock ********* ********* assume under spin lock *********
*/ */
usbhs_pkt_update(pkt,
req->buf + req->actual,
req->length - req->actual);
return usbhs_fifo_read(pkt); return usbhs_fifo_read(pkt);
} }
...@@ -492,41 +399,27 @@ static int usbhsg_prepare_receive_packet(struct usbhsg_uep *uep, ...@@ -492,41 +399,27 @@ static int usbhsg_prepare_receive_packet(struct usbhsg_uep *uep,
struct usbhsg_request *ureq) struct usbhsg_request *ureq)
{ {
struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
int enable = 1;
int ret;
/* /*
********* assume under spin lock ********* ********* assume under spin lock *********
*/ */
ret = usbhs_fifo_prepare_read(pipe); return usbhs_fifo_prepare_read(pipe);
if (ret < 0)
return ret;
/*
* data will be read in interrupt handler
*/
uep->handler->irq_mask(uep, enable);
return ret;
} }
static struct usbhsg_pipe_handle usbhsg_handler_send_by_empty = { static struct usbhsg_pipe_handle usbhsg_handler_send_by_empty = {
.prepare = usbhsg_prepare_send_packet, .prepare = usbhsg_prepare_send_packet,
.try_run = usbhsg_try_run_send_packet, .try_run = usbhsg_try_run_send_packet,
.irq_mask = usbhsg_irq_empty_ctrl,
}; };
static struct usbhsg_pipe_handle usbhsg_handler_send_by_ready = { static struct usbhsg_pipe_handle usbhsg_handler_send_by_ready = {
.prepare = usbhsg_prepare_send_packet, .prepare = usbhsg_prepare_send_packet,
.try_run = usbhsg_try_run_send_packet, .try_run = usbhsg_try_run_send_packet,
.irq_mask = usbhsg_irq_ready_ctrl,
}; };
static struct usbhsg_pipe_handle usbhsg_handler_recv_by_ready = { static struct usbhsg_pipe_handle usbhsg_handler_recv_by_ready = {
.prepare = usbhsg_prepare_receive_packet, .prepare = usbhsg_prepare_receive_packet,
.try_run = usbhsg_try_run_receive_packet, .try_run = usbhsg_try_run_receive_packet,
.irq_mask = usbhsg_irq_ready_ctrl,
}; };
static struct usbhsg_pipe_handle usbhsg_handler_ctrl_stage_end = { static struct usbhsg_pipe_handle usbhsg_handler_ctrl_stage_end = {
...@@ -1113,8 +1006,8 @@ static int usbhsg_try_start(struct usbhs_priv *priv, u32 status) ...@@ -1113,8 +1006,8 @@ static int usbhsg_try_start(struct usbhs_priv *priv, u32 status)
* pipe initialize and enable DCP * pipe initialize and enable DCP
*/ */
usbhs_pipe_init(priv, usbhs_pipe_init(priv,
usbhsg_try_run_send_packet_bh, usbhsg_send_packet_done,
usbhsg_try_run_receive_packet_bh); usbhsg_receive_packet_done);
usbhsg_uep_init(gpriv); usbhsg_uep_init(gpriv);
usbhsg_dcp_enable(dcp); usbhsg_dcp_enable(dcp);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment