Loading drivers/rpmsg/qcom_glink_native.c +216 −26 Original line number Diff line number Diff line Loading @@ -59,6 +59,26 @@ struct glink_defer_cmd { u8 data[]; }; /** * struct glink_core_rx_intent - RX intent * RX intent * * data: pointer to the data (may be NULL for zero-copy) * id: remote or local intent ID * size: size of the original intent (do not modify) * reuse: To mark if the intent can be reused after first use * in_use: To mark if intent is already in use for the channel * offset: next write offset (initially 0) */ struct glink_core_rx_intent { void *data; u32 id; size_t size; bool reuse; bool in_use; u32 offset; }; /** * struct qcom_glink - driver context, relates to one remote subsystem * @dev: reference to the associated struct device Loading Loading @@ -117,6 +137,8 @@ enum { * @name: unique channel name/identifier * @lcid: channel id, in local space * @rcid: channel id, in remote space * @intent_lock: lock for protection of @liids * @liids: idr of all local intents * @buf: receive buffer, for gathering fragments * @buf_offset: write offset in @buf * @buf_size: size of current @buf Loading @@ -137,7 +159,10 @@ struct glink_channel { unsigned int lcid; unsigned int rcid; void *buf; spinlock_t intent_lock; struct idr liids; struct glink_core_rx_intent *buf; int buf_offset; int buf_size; Loading @@ -154,6 +179,9 @@ static const struct rpmsg_endpoint_ops glink_endpoint_ops; #define RPM_CMD_OPEN 2 #define RPM_CMD_CLOSE 3 #define RPM_CMD_OPEN_ACK 4 #define RPM_CMD_INTENT 5 #define RPM_CMD_RX_INTENT_REQ 7 #define RPM_CMD_RX_INTENT_REQ_ACK 8 #define RPM_CMD_TX_DATA 9 #define RPM_CMD_CLOSE_ACK 11 #define RPM_CMD_TX_DATA_CONT 12 Loading @@ -172,12 +200,14 @@ static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink, /* Setup glink internal glink_channel data */ spin_lock_init(&channel->recv_lock); spin_lock_init(&channel->intent_lock); channel->glink = glink; channel->name = kstrdup(name, GFP_KERNEL); init_completion(&channel->open_req); init_completion(&channel->open_ack); idr_init(&channel->liids); kref_init(&channel->refcount); return channel; Loading @@ -187,6 +217,11 @@ static void qcom_glink_channel_release(struct kref *ref) { struct glink_channel *channel = container_of(ref, struct glink_channel, refcount); unsigned long flags; spin_lock_irqsave(&channel->intent_lock, flags); idr_destroy(&channel->liids); spin_unlock_irqrestore(&channel->intent_lock, flags); kfree(channel->name); kfree(channel); Loading Loading @@ -424,6 +459,130 @@ static void qcom_glink_receive_version_ack(struct qcom_glink *glink, } } /** * qcom_glink_send_intent_req_ack() - convert an rx intent request ack cmd to wire format and transmit * @glink: The transport to transmit on. * @channel: The glink channel * @granted: The request response to encode. * * Return: 0 on success or standard Linux error code. */ static int qcom_glink_send_intent_req_ack(struct qcom_glink *glink, struct glink_channel *channel, bool granted) { struct glink_msg msg; msg.cmd = cpu_to_le16(RPM_CMD_RX_INTENT_REQ_ACK); msg.param1 = cpu_to_le16(channel->lcid); msg.param2 = cpu_to_le32(granted); qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true); return 0; } /** * qcom_glink_advertise_intent - convert an rx intent cmd to wire format and * transmit * @glink: The transport to transmit on. * @channel: The local channel * @size: The intent to pass on to remote. * * Return: 0 on success or standard Linux error code. */ static int qcom_glink_advertise_intent(struct qcom_glink *glink, struct glink_channel *channel, struct glink_core_rx_intent *intent) { struct command { u16 id; u16 lcid; u32 count; u32 size; u32 liid; } __packed; struct command cmd; cmd.id = cpu_to_le16(RPM_CMD_INTENT); cmd.lcid = cpu_to_le16(channel->lcid); cmd.count = cpu_to_le32(1); cmd.size = cpu_to_le32(intent->size); cmd.liid = cpu_to_le32(intent->id); qcom_glink_tx(glink, &cmd, sizeof(cmd), NULL, 0, true); return 0; } static struct glink_core_rx_intent * qcom_glink_alloc_intent(struct qcom_glink *glink, struct glink_channel *channel, size_t size, bool reuseable) { struct glink_core_rx_intent *intent; int ret; unsigned long flags; intent = kzalloc(sizeof(*intent), GFP_KERNEL); if (!intent) return NULL; intent->data = kzalloc(size, GFP_KERNEL); if (!intent->data) return NULL; spin_lock_irqsave(&channel->intent_lock, flags); ret = idr_alloc_cyclic(&channel->liids, intent, 1, -1, GFP_ATOMIC); if (ret < 0) { spin_unlock_irqrestore(&channel->intent_lock, flags); return NULL; } spin_unlock_irqrestore(&channel->intent_lock, flags); intent->id = ret; intent->size = size; intent->reuse = reuseable; return intent; } /** * qcom_glink_handle_intent_req() - Receive a request for rx_intent * from remote side * if_ptr: Pointer to the transport interface * rcid: Remote channel ID * size: size of the intent * * The function searches for the local channel to which the request for * rx_intent has arrived and allocates and notifies the remote back */ static void qcom_glink_handle_intent_req(struct qcom_glink *glink, u32 cid, size_t size) { struct glink_core_rx_intent *intent; struct glink_channel *channel; unsigned long flags; spin_lock_irqsave(&glink->idr_lock, flags); channel = idr_find(&glink->rcids, cid); spin_unlock_irqrestore(&glink->idr_lock, flags); if (!channel) { pr_err("%s channel not found for cid %d\n", __func__, cid); return; } intent = qcom_glink_alloc_intent(glink, channel, size, false); if (intent) qcom_glink_advertise_intent(glink, channel, intent); qcom_glink_send_intent_req_ack(glink, channel, !!intent); } static int qcom_glink_rx_defer(struct qcom_glink *glink, size_t extra) { struct glink_defer_cmd *dcmd; Loading Loading @@ -455,6 +614,7 @@ static int qcom_glink_rx_defer(struct qcom_glink *glink, size_t extra) static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail) { struct glink_core_rx_intent *intent; struct glink_channel *channel; struct { struct glink_msg msg; Loading @@ -464,6 +624,8 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail) unsigned int chunk_size; unsigned int left_size; unsigned int rcid; unsigned int liid; int ret = 0; unsigned long flags; if (avail < sizeof(hdr)) { Loading Loading @@ -491,56 +653,78 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail) dev_dbg(glink->dev, "Data on non-existing channel\n"); /* Drop the message */ qcom_glink_rx_advance(glink, ALIGN(sizeof(hdr) + chunk_size, 8)); return 0; goto advance_rx; } if (glink->intentless) { /* Might have an ongoing, fragmented, message to append */ if (!channel->buf) { channel->buf = kmalloc(chunk_size + left_size, GFP_ATOMIC); if (!channel->buf) intent = kzalloc(sizeof(*intent), GFP_ATOMIC); if (!intent) return -ENOMEM; channel->buf_size = chunk_size + left_size; channel->buf_offset = 0; intent->data = kmalloc(chunk_size + left_size, GFP_ATOMIC); if (!intent->data) { kfree(intent); return -ENOMEM; } qcom_glink_rx_advance(glink, sizeof(hdr)); intent->id = 0xbabababa; intent->size = chunk_size + left_size; intent->offset = 0; if (channel->buf_size - channel->buf_offset < chunk_size) { dev_err(glink->dev, "Insufficient space in input buffer\n"); channel->buf = intent; } else { intent = channel->buf; } } else { liid = le32_to_cpu(hdr.msg.param2); spin_lock_irqsave(&channel->intent_lock, flags); intent = idr_find(&channel->liids, liid); spin_unlock_irqrestore(&channel->intent_lock, flags); if (!intent) { dev_err(glink->dev, "no intent found for channel %s intent %d", channel->name, liid); goto advance_rx; } } if (intent->size - intent->offset < chunk_size) { dev_err(glink->dev, "Insufficient space in intent\n"); /* The packet header lied, drop payload */ qcom_glink_rx_advance(glink, chunk_size); return -ENOMEM; goto advance_rx; } qcom_glink_rx_peak(glink, channel->buf + channel->buf_offset, qcom_glink_rx_advance(glink, ALIGN(sizeof(hdr), 8)); qcom_glink_rx_peak(glink, intent->data + intent->offset, chunk_size); channel->buf_offset += chunk_size; intent->offset += chunk_size; /* Handle message when no fragments remain to be received */ if (!left_size) { spin_lock(&channel->recv_lock); if (channel->ept.cb) { channel->ept.cb(channel->ept.rpdev, channel->buf, channel->buf_offset, intent->data, intent->offset, channel->ept.priv, RPMSG_ADDR_ANY); } spin_unlock(&channel->recv_lock); kfree(channel->buf); intent->offset = 0; channel->buf = NULL; channel->buf_size = 0; } /* Each message starts at 8 byte aligned address */ advance_rx: qcom_glink_rx_advance(glink, ALIGN(chunk_size, 8)); return 0; return ret; } static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid) Loading Loading @@ -586,6 +770,7 @@ static irqreturn_t qcom_glink_native_intr(int irq, void *data) case RPM_CMD_VERSION_ACK: case RPM_CMD_CLOSE: case RPM_CMD_CLOSE_ACK: case RPM_CMD_RX_INTENT_REQ: ret = qcom_glink_rx_defer(glink, 0); break; case RPM_CMD_OPEN_ACK: Loading Loading @@ -1003,6 +1188,9 @@ static void qcom_glink_work(struct work_struct *work) case RPM_CMD_CLOSE_ACK: qcom_glink_rx_close_ack(glink, param1); break; case RPM_CMD_RX_INTENT_REQ: qcom_glink_handle_intent_req(glink, param1, param2); break; default: WARN(1, "Unknown defer object %d\n", cmd); break; Loading @@ -1015,7 +1203,8 @@ static void qcom_glink_work(struct work_struct *work) struct qcom_glink *qcom_glink_native_probe(struct device *dev, unsigned long features, struct qcom_glink_pipe *rx, struct qcom_glink_pipe *tx) struct qcom_glink_pipe *tx, bool intentless) { int irq; int ret; Loading @@ -1030,6 +1219,7 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev, glink->rx_pipe = rx; glink->features = features; glink->intentless = intentless; mutex_init(&glink->tx_lock); spin_lock_init(&glink->rx_lock); Loading drivers/rpmsg/qcom_glink_native.h +2 −1 Original line number Diff line number Diff line Loading @@ -37,7 +37,8 @@ struct qcom_glink; struct qcom_glink *qcom_glink_native_probe(struct device *dev, unsigned long features, struct qcom_glink_pipe *rx, struct qcom_glink_pipe *tx); struct qcom_glink_pipe *tx, bool intentless); void qcom_glink_native_remove(struct qcom_glink *glink); void qcom_glink_native_unregister(struct qcom_glink *glink); Loading drivers/rpmsg/qcom_glink_rpm.c +2 −1 Original line number Diff line number Diff line Loading @@ -305,7 +305,8 @@ static int glink_rpm_probe(struct platform_device *pdev) glink = qcom_glink_native_probe(&pdev->dev, 0, &rx_pipe->native, &tx_pipe->native); &tx_pipe->native, true); if (IS_ERR(glink)) return PTR_ERR(glink); Loading drivers/rpmsg/qcom_glink_smem.c +3 −2 Original line number Diff line number Diff line Loading @@ -284,8 +284,9 @@ struct qcom_glink *qcom_glink_smem_register(struct device *parent, *tx_pipe->head = 0; glink = qcom_glink_native_probe(dev, GLINK_FEATURE_TRACER_PKT, &rx_pipe->native, &tx_pipe->native); GLINK_FEATURE_INTENT_REUSE, &rx_pipe->native, &tx_pipe->native, false); if (IS_ERR(glink)) { ret = PTR_ERR(glink); goto err_put_dev; Loading Loading
drivers/rpmsg/qcom_glink_native.c +216 −26 Original line number Diff line number Diff line Loading @@ -59,6 +59,26 @@ struct glink_defer_cmd { u8 data[]; }; /** * struct glink_core_rx_intent - RX intent * RX intent * * data: pointer to the data (may be NULL for zero-copy) * id: remote or local intent ID * size: size of the original intent (do not modify) * reuse: To mark if the intent can be reused after first use * in_use: To mark if intent is already in use for the channel * offset: next write offset (initially 0) */ struct glink_core_rx_intent { void *data; u32 id; size_t size; bool reuse; bool in_use; u32 offset; }; /** * struct qcom_glink - driver context, relates to one remote subsystem * @dev: reference to the associated struct device Loading Loading @@ -117,6 +137,8 @@ enum { * @name: unique channel name/identifier * @lcid: channel id, in local space * @rcid: channel id, in remote space * @intent_lock: lock for protection of @liids * @liids: idr of all local intents * @buf: receive buffer, for gathering fragments * @buf_offset: write offset in @buf * @buf_size: size of current @buf Loading @@ -137,7 +159,10 @@ struct glink_channel { unsigned int lcid; unsigned int rcid; void *buf; spinlock_t intent_lock; struct idr liids; struct glink_core_rx_intent *buf; int buf_offset; int buf_size; Loading @@ -154,6 +179,9 @@ static const struct rpmsg_endpoint_ops glink_endpoint_ops; #define RPM_CMD_OPEN 2 #define RPM_CMD_CLOSE 3 #define RPM_CMD_OPEN_ACK 4 #define RPM_CMD_INTENT 5 #define RPM_CMD_RX_INTENT_REQ 7 #define RPM_CMD_RX_INTENT_REQ_ACK 8 #define RPM_CMD_TX_DATA 9 #define RPM_CMD_CLOSE_ACK 11 #define RPM_CMD_TX_DATA_CONT 12 Loading @@ -172,12 +200,14 @@ static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink, /* Setup glink internal glink_channel data */ spin_lock_init(&channel->recv_lock); spin_lock_init(&channel->intent_lock); channel->glink = glink; channel->name = kstrdup(name, GFP_KERNEL); init_completion(&channel->open_req); init_completion(&channel->open_ack); idr_init(&channel->liids); kref_init(&channel->refcount); return channel; Loading @@ -187,6 +217,11 @@ static void qcom_glink_channel_release(struct kref *ref) { struct glink_channel *channel = container_of(ref, struct glink_channel, refcount); unsigned long flags; spin_lock_irqsave(&channel->intent_lock, flags); idr_destroy(&channel->liids); spin_unlock_irqrestore(&channel->intent_lock, flags); kfree(channel->name); kfree(channel); Loading Loading @@ -424,6 +459,130 @@ static void qcom_glink_receive_version_ack(struct qcom_glink *glink, } } /** * qcom_glink_send_intent_req_ack() - convert an rx intent request ack cmd to wire format and transmit * @glink: The transport to transmit on. * @channel: The glink channel * @granted: The request response to encode. * * Return: 0 on success or standard Linux error code. */ static int qcom_glink_send_intent_req_ack(struct qcom_glink *glink, struct glink_channel *channel, bool granted) { struct glink_msg msg; msg.cmd = cpu_to_le16(RPM_CMD_RX_INTENT_REQ_ACK); msg.param1 = cpu_to_le16(channel->lcid); msg.param2 = cpu_to_le32(granted); qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true); return 0; } /** * qcom_glink_advertise_intent - convert an rx intent cmd to wire format and * transmit * @glink: The transport to transmit on. * @channel: The local channel * @size: The intent to pass on to remote. * * Return: 0 on success or standard Linux error code. */ static int qcom_glink_advertise_intent(struct qcom_glink *glink, struct glink_channel *channel, struct glink_core_rx_intent *intent) { struct command { u16 id; u16 lcid; u32 count; u32 size; u32 liid; } __packed; struct command cmd; cmd.id = cpu_to_le16(RPM_CMD_INTENT); cmd.lcid = cpu_to_le16(channel->lcid); cmd.count = cpu_to_le32(1); cmd.size = cpu_to_le32(intent->size); cmd.liid = cpu_to_le32(intent->id); qcom_glink_tx(glink, &cmd, sizeof(cmd), NULL, 0, true); return 0; } static struct glink_core_rx_intent * qcom_glink_alloc_intent(struct qcom_glink *glink, struct glink_channel *channel, size_t size, bool reuseable) { struct glink_core_rx_intent *intent; int ret; unsigned long flags; intent = kzalloc(sizeof(*intent), GFP_KERNEL); if (!intent) return NULL; intent->data = kzalloc(size, GFP_KERNEL); if (!intent->data) return NULL; spin_lock_irqsave(&channel->intent_lock, flags); ret = idr_alloc_cyclic(&channel->liids, intent, 1, -1, GFP_ATOMIC); if (ret < 0) { spin_unlock_irqrestore(&channel->intent_lock, flags); return NULL; } spin_unlock_irqrestore(&channel->intent_lock, flags); intent->id = ret; intent->size = size; intent->reuse = reuseable; return intent; } /** * qcom_glink_handle_intent_req() - Receive a request for rx_intent * from remote side * if_ptr: Pointer to the transport interface * rcid: Remote channel ID * size: size of the intent * * The function searches for the local channel to which the request for * rx_intent has arrived and allocates and notifies the remote back */ static void qcom_glink_handle_intent_req(struct qcom_glink *glink, u32 cid, size_t size) { struct glink_core_rx_intent *intent; struct glink_channel *channel; unsigned long flags; spin_lock_irqsave(&glink->idr_lock, flags); channel = idr_find(&glink->rcids, cid); spin_unlock_irqrestore(&glink->idr_lock, flags); if (!channel) { pr_err("%s channel not found for cid %d\n", __func__, cid); return; } intent = qcom_glink_alloc_intent(glink, channel, size, false); if (intent) qcom_glink_advertise_intent(glink, channel, intent); qcom_glink_send_intent_req_ack(glink, channel, !!intent); } static int qcom_glink_rx_defer(struct qcom_glink *glink, size_t extra) { struct glink_defer_cmd *dcmd; Loading Loading @@ -455,6 +614,7 @@ static int qcom_glink_rx_defer(struct qcom_glink *glink, size_t extra) static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail) { struct glink_core_rx_intent *intent; struct glink_channel *channel; struct { struct glink_msg msg; Loading @@ -464,6 +624,8 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail) unsigned int chunk_size; unsigned int left_size; unsigned int rcid; unsigned int liid; int ret = 0; unsigned long flags; if (avail < sizeof(hdr)) { Loading Loading @@ -491,56 +653,78 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail) dev_dbg(glink->dev, "Data on non-existing channel\n"); /* Drop the message */ qcom_glink_rx_advance(glink, ALIGN(sizeof(hdr) + chunk_size, 8)); return 0; goto advance_rx; } if (glink->intentless) { /* Might have an ongoing, fragmented, message to append */ if (!channel->buf) { channel->buf = kmalloc(chunk_size + left_size, GFP_ATOMIC); if (!channel->buf) intent = kzalloc(sizeof(*intent), GFP_ATOMIC); if (!intent) return -ENOMEM; channel->buf_size = chunk_size + left_size; channel->buf_offset = 0; intent->data = kmalloc(chunk_size + left_size, GFP_ATOMIC); if (!intent->data) { kfree(intent); return -ENOMEM; } qcom_glink_rx_advance(glink, sizeof(hdr)); intent->id = 0xbabababa; intent->size = chunk_size + left_size; intent->offset = 0; if (channel->buf_size - channel->buf_offset < chunk_size) { dev_err(glink->dev, "Insufficient space in input buffer\n"); channel->buf = intent; } else { intent = channel->buf; } } else { liid = le32_to_cpu(hdr.msg.param2); spin_lock_irqsave(&channel->intent_lock, flags); intent = idr_find(&channel->liids, liid); spin_unlock_irqrestore(&channel->intent_lock, flags); if (!intent) { dev_err(glink->dev, "no intent found for channel %s intent %d", channel->name, liid); goto advance_rx; } } if (intent->size - intent->offset < chunk_size) { dev_err(glink->dev, "Insufficient space in intent\n"); /* The packet header lied, drop payload */ qcom_glink_rx_advance(glink, chunk_size); return -ENOMEM; goto advance_rx; } qcom_glink_rx_peak(glink, channel->buf + channel->buf_offset, qcom_glink_rx_advance(glink, ALIGN(sizeof(hdr), 8)); qcom_glink_rx_peak(glink, intent->data + intent->offset, chunk_size); channel->buf_offset += chunk_size; intent->offset += chunk_size; /* Handle message when no fragments remain to be received */ if (!left_size) { spin_lock(&channel->recv_lock); if (channel->ept.cb) { channel->ept.cb(channel->ept.rpdev, channel->buf, channel->buf_offset, intent->data, intent->offset, channel->ept.priv, RPMSG_ADDR_ANY); } spin_unlock(&channel->recv_lock); kfree(channel->buf); intent->offset = 0; channel->buf = NULL; channel->buf_size = 0; } /* Each message starts at 8 byte aligned address */ advance_rx: qcom_glink_rx_advance(glink, ALIGN(chunk_size, 8)); return 0; return ret; } static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid) Loading Loading @@ -586,6 +770,7 @@ static irqreturn_t qcom_glink_native_intr(int irq, void *data) case RPM_CMD_VERSION_ACK: case RPM_CMD_CLOSE: case RPM_CMD_CLOSE_ACK: case RPM_CMD_RX_INTENT_REQ: ret = qcom_glink_rx_defer(glink, 0); break; case RPM_CMD_OPEN_ACK: Loading Loading @@ -1003,6 +1188,9 @@ static void qcom_glink_work(struct work_struct *work) case RPM_CMD_CLOSE_ACK: qcom_glink_rx_close_ack(glink, param1); break; case RPM_CMD_RX_INTENT_REQ: qcom_glink_handle_intent_req(glink, param1, param2); break; default: WARN(1, "Unknown defer object %d\n", cmd); break; Loading @@ -1015,7 +1203,8 @@ static void qcom_glink_work(struct work_struct *work) struct qcom_glink *qcom_glink_native_probe(struct device *dev, unsigned long features, struct qcom_glink_pipe *rx, struct qcom_glink_pipe *tx) struct qcom_glink_pipe *tx, bool intentless) { int irq; int ret; Loading @@ -1030,6 +1219,7 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev, glink->rx_pipe = rx; glink->features = features; glink->intentless = intentless; mutex_init(&glink->tx_lock); spin_lock_init(&glink->rx_lock); Loading
drivers/rpmsg/qcom_glink_native.h +2 −1 Original line number Diff line number Diff line Loading @@ -37,7 +37,8 @@ struct qcom_glink; struct qcom_glink *qcom_glink_native_probe(struct device *dev, unsigned long features, struct qcom_glink_pipe *rx, struct qcom_glink_pipe *tx); struct qcom_glink_pipe *tx, bool intentless); void qcom_glink_native_remove(struct qcom_glink *glink); void qcom_glink_native_unregister(struct qcom_glink *glink); Loading
drivers/rpmsg/qcom_glink_rpm.c +2 −1 Original line number Diff line number Diff line Loading @@ -305,7 +305,8 @@ static int glink_rpm_probe(struct platform_device *pdev) glink = qcom_glink_native_probe(&pdev->dev, 0, &rx_pipe->native, &tx_pipe->native); &tx_pipe->native, true); if (IS_ERR(glink)) return PTR_ERR(glink); Loading
drivers/rpmsg/qcom_glink_smem.c +3 −2 Original line number Diff line number Diff line Loading @@ -284,8 +284,9 @@ struct qcom_glink *qcom_glink_smem_register(struct device *parent, *tx_pipe->head = 0; glink = qcom_glink_native_probe(dev, GLINK_FEATURE_TRACER_PKT, &rx_pipe->native, &tx_pipe->native); GLINK_FEATURE_INTENT_REUSE, &rx_pipe->native, &tx_pipe->native, false); if (IS_ERR(glink)) { ret = PTR_ERR(glink); goto err_put_dev; Loading