Loading net/qrtr/Kconfig +9 −0 Original line number Diff line number Diff line Loading @@ -38,4 +38,13 @@ config QRTR_MHI transport will wait for the uplink callback notification before returning to qrtr. config QRTR_USB tristate "USB IPC Router channels" depends on USB || (COMPILE_TEST && USB=n) help Say Y here to support USB based ipcrouter channels. USB transport is used for connections with other sytems for IPC Router. The USB transport provides bulk endpoints to facilitate sending and receiving IPC Router data packets. endif # QRTR net/qrtr/Makefile +3 −0 Original line number Diff line number Diff line Loading @@ -6,3 +6,6 @@ obj-$(CONFIG_QRTR_TUN) += qrtr-tun.o qrtr-tun-y := tun.o obj-$(CONFIG_QRTR_MHI) += qrtr-mhi.o qrtr-mhi-y := mhi.o obj-$(CONFIG_QRTR_USB) += qrtr-usb.o qrtr-usb-y := usb.o net/qrtr/qrtr.c +3 −1 Original line number Diff line number Diff line Loading @@ -959,7 +959,9 @@ static int qrtr_port_assign(struct qrtr_sock *ipc, int *port) if (rc >= 0) *port = rc; } else if (*port < QRTR_MIN_EPH_SOCKET && !(capable(CAP_NET_ADMIN) || in_egroup_p(AID_VENDOR_QRTR))) { !(capable(CAP_NET_ADMIN) || in_egroup_p(AID_VENDOR_QRTR) || in_egroup_p(GLOBAL_ROOT_GID))) { rc = -EACCES; } else if (*port == QRTR_PORT_CTRL) { rc = idr_alloc(&qrtr_ports, ipc, 0, 1, GFP_ATOMIC); Loading net/qrtr/usb.c 0 → 100644 +311 −0 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2018, The Linux Foundation. All rights reserved. */ #include <linux/kthread.h> #include <linux/module.h> #include <linux/skbuff.h> #include <linux/usb.h> #include "qrtr.h" #define QRTR_VENDOR_ID 0x05c6 #define MAX_DATA_SIZE 16384 enum qrtr_usb_rx_state { QRTR_USB_RX_SUSPEND = 0, QRTR_USB_RX_RUN, QRTR_USB_RX_STOP }; struct qrtr_usb_dev { struct qrtr_endpoint ep; struct usb_device *udev; struct usb_interface *iface; struct usb_anchor submitted; struct completion in_compl; struct urb *in_urb; struct task_struct *rx_thread; enum qrtr_usb_rx_state thread_state; wait_queue_head_t qrtr_wq; unsigned int in_pipe; unsigned int out_pipe; }; static void qcom_usb_qrtr_txn_cb(struct urb *urb) { struct completion *compl = urb->context; complete(compl); } /* from usb to qrtr */ static int qcom_usb_qrtr_rx_thread_fn(void *data) { struct qrtr_usb_dev *qdev = data; struct urb *in_urb = qdev->in_urb; void *buf; int rc = 0; buf = kmalloc(MAX_DATA_SIZE, GFP_KERNEL); if (!buf) return -ENOMEM; usb_anchor_urb(in_urb, &qdev->submitted); usb_fill_bulk_urb(in_urb, qdev->udev, qdev->in_pipe, buf, MAX_DATA_SIZE, qcom_usb_qrtr_txn_cb, &qdev->in_compl); while (!kthread_should_stop()) { if (qdev->thread_state == QRTR_USB_RX_SUSPEND || qdev->thread_state == QRTR_USB_RX_STOP) { dev_dbg(&qdev->udev->dev, "pausing or stopping thread, state=%d\n", qdev->thread_state); wait_event_interruptible(qdev->qrtr_wq, qdev->thread_state == QRTR_USB_RX_RUN || kthread_should_stop()); continue; } rc = usb_autopm_get_interface(qdev->iface); if (rc) { dev_err(&qdev->udev->dev, "failed to get autopm, rc=%d\n", rc); continue; } reinit_completion(&qdev->in_compl); rc = usb_submit_urb(in_urb, GFP_KERNEL); if (rc) { usb_autopm_put_interface(qdev->iface); dev_dbg(&qdev->udev->dev, "could not submit in urb, rc=%d\n", rc); /* Give up if the device is disconnected */ if (rc == -ENODEV) break; continue; } wait_for_completion(&qdev->in_compl); if (in_urb->status) { usb_autopm_put_interface(qdev->iface); dev_dbg(&qdev->udev->dev, "URB status %d", in_urb->status); continue; } usb_autopm_put_interface(qdev->iface); dev_dbg(&qdev->udev->dev, "received message with len=%d\n", in_urb->actual_length); rc = qrtr_endpoint_post(&qdev->ep, buf, in_urb->actual_length); if (rc == -EINVAL) dev_err(&qdev->udev->dev, "invalid ipcrouter packet\n"); } kfree(buf); wait_event_interruptible(qdev->qrtr_wq, kthread_should_stop()); dev_dbg(&qdev->udev->dev, "leaving rx_thread\n"); return rc; } /* from qrtr to usb */ static int qcom_usb_qrtr_send(struct qrtr_endpoint *ep, struct sk_buff *skb) { struct qrtr_usb_dev *qdev = container_of(ep, struct qrtr_usb_dev, ep); struct urb *out_urb = NULL; struct completion compl; int rc; rc = skb_linearize(skb); if (rc) goto exit_free_skb; out_urb = usb_alloc_urb(0, GFP_KERNEL); if (!out_urb) { rc = -ENOMEM; goto exit_free_skb; } rc = usb_autopm_get_interface(qdev->iface); if (rc) goto exit_free_urb; init_completion(&compl); usb_fill_bulk_urb(out_urb, qdev->udev, qdev->out_pipe, skb->data, skb->len, qcom_usb_qrtr_txn_cb, &compl); usb_anchor_urb(out_urb, &qdev->submitted); rc = usb_submit_urb(out_urb, GFP_KERNEL); if (rc) { dev_err(&qdev->udev->dev, "could not submit out urb\n"); usb_unanchor_urb(out_urb); goto exit_autopm_put_intf; } wait_for_completion(&compl); rc = out_urb->status; dev_dbg(&qdev->udev->dev, "sent message with len=%d, rc=%d\n", skb->len, rc); exit_autopm_put_intf: usb_autopm_put_interface(qdev->iface); exit_free_urb: usb_free_urb(out_urb); exit_free_skb: if (rc) kfree_skb(skb); else consume_skb(skb); return rc; } static int qcom_usb_qrtr_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct qrtr_usb_dev *qdev; struct usb_device *udev; struct usb_host_interface *intf_desc; struct usb_endpoint_descriptor *ep_desc; int rc, i; udev = usb_get_dev(interface_to_usbdev(interface)); if (!udev) return -ENODEV; qdev = devm_kzalloc(&udev->dev, sizeof(*qdev), GFP_KERNEL); if (!qdev) return -ENOMEM; qdev->udev = udev; qdev->iface = interface; qdev->ep.xmit = qcom_usb_qrtr_send; intf_desc = interface->cur_altsetting; for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) { ep_desc = &intf_desc->endpoint[i].desc; if (!qdev->in_pipe && usb_endpoint_is_bulk_in(ep_desc)) qdev->in_pipe = usb_rcvbulkpipe(qdev->udev, ep_desc->bEndpointAddress); if (!qdev->out_pipe && usb_endpoint_is_bulk_out(ep_desc)) qdev->out_pipe = usb_sndbulkpipe(qdev->udev, ep_desc->bEndpointAddress); } if (!qdev->in_pipe || !qdev->out_pipe) { dev_err(&qdev->udev->dev, "could not find endpoints\n"); return -ENODEV; } qdev->in_urb = usb_alloc_urb(0, GFP_KERNEL); if (!qdev->in_urb) { dev_err(&qdev->udev->dev, "could not allocate in urb\n"); return -ENOMEM; } init_usb_anchor(&qdev->submitted); rc = qrtr_endpoint_register(&qdev->ep, QRTR_EP_NID_AUTO); if (rc) return rc; usb_set_intfdata(interface, qdev); init_waitqueue_head(&qdev->qrtr_wq); init_completion(&qdev->in_compl); qdev->thread_state = QRTR_USB_RX_RUN; qdev->rx_thread = kthread_run(qcom_usb_qrtr_rx_thread_fn, qdev, "qrtr-usb-rx"); if (IS_ERR(qdev->rx_thread)) { dev_err(&qdev->udev->dev, "could not create rx_thread\n"); qrtr_endpoint_unregister(&qdev->ep); return PTR_ERR(qdev->rx_thread); } dev_dbg(&qdev->udev->dev, "QTI USB QRTR driver probed\n"); return 0; } static int qcom_usb_qrtr_suspend(struct usb_interface *intf, pm_message_t message) { struct qrtr_usb_dev *qdev = usb_get_intfdata(intf); /* Suspend the thread */ qdev->thread_state = QRTR_USB_RX_SUSPEND; usb_kill_anchored_urbs(&qdev->submitted); return 0; } static int qcom_usb_qrtr_resume(struct usb_interface *intf) { struct qrtr_usb_dev *qdev = usb_get_intfdata(intf); qdev->thread_state = QRTR_USB_RX_RUN; wake_up(&qdev->qrtr_wq); return 0; } static int qcom_usb_qrtr_reset_resume(struct usb_interface *intf) { struct qrtr_usb_dev *qdev = usb_get_intfdata(intf); int rc = 0; qrtr_endpoint_unregister(&qdev->ep); rc = qrtr_endpoint_register(&qdev->ep, QRTR_EP_NID_AUTO); if (rc) return rc; qdev->thread_state = QRTR_USB_RX_RUN; wake_up(&qdev->qrtr_wq); return rc; } static void qcom_usb_qrtr_disconnect(struct usb_interface *interface) { struct qrtr_usb_dev *qdev = usb_get_intfdata(interface); qdev->thread_state = QRTR_USB_RX_STOP; usb_kill_anchored_urbs(&qdev->submitted); kthread_stop(qdev->rx_thread); usb_free_urb(qdev->in_urb); qrtr_endpoint_unregister(&qdev->ep); usb_set_intfdata(interface, NULL); usb_put_dev(qdev->udev); } static const struct usb_device_id qcom_usb_qrtr_ids[] = { { USB_DEVICE_INTERFACE_NUMBER(QRTR_VENDOR_ID, 0x90ef, 3) }, { USB_DEVICE_INTERFACE_NUMBER(QRTR_VENDOR_ID, 0x90f0, 3) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, qcom_usb_qrtr_ids); static struct usb_driver qcom_usb_qrtr_driver = { .name = "qcom_usb_qrtr", .probe = qcom_usb_qrtr_probe, .disconnect = qcom_usb_qrtr_disconnect, .suspend = qcom_usb_qrtr_suspend, .resume = qcom_usb_qrtr_resume, .reset_resume = qcom_usb_qrtr_reset_resume, .id_table = qcom_usb_qrtr_ids, .supports_autosuspend = 1, }; module_usb_driver(qcom_usb_qrtr_driver); MODULE_DESCRIPTION("QTI IPC-Router USB interface driver"); MODULE_LICENSE("GPL v2"); Loading
net/qrtr/Kconfig +9 −0 Original line number Diff line number Diff line Loading @@ -38,4 +38,13 @@ config QRTR_MHI transport will wait for the uplink callback notification before returning to qrtr. config QRTR_USB tristate "USB IPC Router channels" depends on USB || (COMPILE_TEST && USB=n) help Say Y here to support USB based ipcrouter channels. USB transport is used for connections with other sytems for IPC Router. The USB transport provides bulk endpoints to facilitate sending and receiving IPC Router data packets. endif # QRTR
net/qrtr/Makefile +3 −0 Original line number Diff line number Diff line Loading @@ -6,3 +6,6 @@ obj-$(CONFIG_QRTR_TUN) += qrtr-tun.o qrtr-tun-y := tun.o obj-$(CONFIG_QRTR_MHI) += qrtr-mhi.o qrtr-mhi-y := mhi.o obj-$(CONFIG_QRTR_USB) += qrtr-usb.o qrtr-usb-y := usb.o
net/qrtr/qrtr.c +3 −1 Original line number Diff line number Diff line Loading @@ -959,7 +959,9 @@ static int qrtr_port_assign(struct qrtr_sock *ipc, int *port) if (rc >= 0) *port = rc; } else if (*port < QRTR_MIN_EPH_SOCKET && !(capable(CAP_NET_ADMIN) || in_egroup_p(AID_VENDOR_QRTR))) { !(capable(CAP_NET_ADMIN) || in_egroup_p(AID_VENDOR_QRTR) || in_egroup_p(GLOBAL_ROOT_GID))) { rc = -EACCES; } else if (*port == QRTR_PORT_CTRL) { rc = idr_alloc(&qrtr_ports, ipc, 0, 1, GFP_ATOMIC); Loading
net/qrtr/usb.c 0 → 100644 +311 −0 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2018, The Linux Foundation. All rights reserved. */ #include <linux/kthread.h> #include <linux/module.h> #include <linux/skbuff.h> #include <linux/usb.h> #include "qrtr.h" #define QRTR_VENDOR_ID 0x05c6 #define MAX_DATA_SIZE 16384 enum qrtr_usb_rx_state { QRTR_USB_RX_SUSPEND = 0, QRTR_USB_RX_RUN, QRTR_USB_RX_STOP }; struct qrtr_usb_dev { struct qrtr_endpoint ep; struct usb_device *udev; struct usb_interface *iface; struct usb_anchor submitted; struct completion in_compl; struct urb *in_urb; struct task_struct *rx_thread; enum qrtr_usb_rx_state thread_state; wait_queue_head_t qrtr_wq; unsigned int in_pipe; unsigned int out_pipe; }; static void qcom_usb_qrtr_txn_cb(struct urb *urb) { struct completion *compl = urb->context; complete(compl); } /* from usb to qrtr */ static int qcom_usb_qrtr_rx_thread_fn(void *data) { struct qrtr_usb_dev *qdev = data; struct urb *in_urb = qdev->in_urb; void *buf; int rc = 0; buf = kmalloc(MAX_DATA_SIZE, GFP_KERNEL); if (!buf) return -ENOMEM; usb_anchor_urb(in_urb, &qdev->submitted); usb_fill_bulk_urb(in_urb, qdev->udev, qdev->in_pipe, buf, MAX_DATA_SIZE, qcom_usb_qrtr_txn_cb, &qdev->in_compl); while (!kthread_should_stop()) { if (qdev->thread_state == QRTR_USB_RX_SUSPEND || qdev->thread_state == QRTR_USB_RX_STOP) { dev_dbg(&qdev->udev->dev, "pausing or stopping thread, state=%d\n", qdev->thread_state); wait_event_interruptible(qdev->qrtr_wq, qdev->thread_state == QRTR_USB_RX_RUN || kthread_should_stop()); continue; } rc = usb_autopm_get_interface(qdev->iface); if (rc) { dev_err(&qdev->udev->dev, "failed to get autopm, rc=%d\n", rc); continue; } reinit_completion(&qdev->in_compl); rc = usb_submit_urb(in_urb, GFP_KERNEL); if (rc) { usb_autopm_put_interface(qdev->iface); dev_dbg(&qdev->udev->dev, "could not submit in urb, rc=%d\n", rc); /* Give up if the device is disconnected */ if (rc == -ENODEV) break; continue; } wait_for_completion(&qdev->in_compl); if (in_urb->status) { usb_autopm_put_interface(qdev->iface); dev_dbg(&qdev->udev->dev, "URB status %d", in_urb->status); continue; } usb_autopm_put_interface(qdev->iface); dev_dbg(&qdev->udev->dev, "received message with len=%d\n", in_urb->actual_length); rc = qrtr_endpoint_post(&qdev->ep, buf, in_urb->actual_length); if (rc == -EINVAL) dev_err(&qdev->udev->dev, "invalid ipcrouter packet\n"); } kfree(buf); wait_event_interruptible(qdev->qrtr_wq, kthread_should_stop()); dev_dbg(&qdev->udev->dev, "leaving rx_thread\n"); return rc; } /* from qrtr to usb */ static int qcom_usb_qrtr_send(struct qrtr_endpoint *ep, struct sk_buff *skb) { struct qrtr_usb_dev *qdev = container_of(ep, struct qrtr_usb_dev, ep); struct urb *out_urb = NULL; struct completion compl; int rc; rc = skb_linearize(skb); if (rc) goto exit_free_skb; out_urb = usb_alloc_urb(0, GFP_KERNEL); if (!out_urb) { rc = -ENOMEM; goto exit_free_skb; } rc = usb_autopm_get_interface(qdev->iface); if (rc) goto exit_free_urb; init_completion(&compl); usb_fill_bulk_urb(out_urb, qdev->udev, qdev->out_pipe, skb->data, skb->len, qcom_usb_qrtr_txn_cb, &compl); usb_anchor_urb(out_urb, &qdev->submitted); rc = usb_submit_urb(out_urb, GFP_KERNEL); if (rc) { dev_err(&qdev->udev->dev, "could not submit out urb\n"); usb_unanchor_urb(out_urb); goto exit_autopm_put_intf; } wait_for_completion(&compl); rc = out_urb->status; dev_dbg(&qdev->udev->dev, "sent message with len=%d, rc=%d\n", skb->len, rc); exit_autopm_put_intf: usb_autopm_put_interface(qdev->iface); exit_free_urb: usb_free_urb(out_urb); exit_free_skb: if (rc) kfree_skb(skb); else consume_skb(skb); return rc; } static int qcom_usb_qrtr_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct qrtr_usb_dev *qdev; struct usb_device *udev; struct usb_host_interface *intf_desc; struct usb_endpoint_descriptor *ep_desc; int rc, i; udev = usb_get_dev(interface_to_usbdev(interface)); if (!udev) return -ENODEV; qdev = devm_kzalloc(&udev->dev, sizeof(*qdev), GFP_KERNEL); if (!qdev) return -ENOMEM; qdev->udev = udev; qdev->iface = interface; qdev->ep.xmit = qcom_usb_qrtr_send; intf_desc = interface->cur_altsetting; for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) { ep_desc = &intf_desc->endpoint[i].desc; if (!qdev->in_pipe && usb_endpoint_is_bulk_in(ep_desc)) qdev->in_pipe = usb_rcvbulkpipe(qdev->udev, ep_desc->bEndpointAddress); if (!qdev->out_pipe && usb_endpoint_is_bulk_out(ep_desc)) qdev->out_pipe = usb_sndbulkpipe(qdev->udev, ep_desc->bEndpointAddress); } if (!qdev->in_pipe || !qdev->out_pipe) { dev_err(&qdev->udev->dev, "could not find endpoints\n"); return -ENODEV; } qdev->in_urb = usb_alloc_urb(0, GFP_KERNEL); if (!qdev->in_urb) { dev_err(&qdev->udev->dev, "could not allocate in urb\n"); return -ENOMEM; } init_usb_anchor(&qdev->submitted); rc = qrtr_endpoint_register(&qdev->ep, QRTR_EP_NID_AUTO); if (rc) return rc; usb_set_intfdata(interface, qdev); init_waitqueue_head(&qdev->qrtr_wq); init_completion(&qdev->in_compl); qdev->thread_state = QRTR_USB_RX_RUN; qdev->rx_thread = kthread_run(qcom_usb_qrtr_rx_thread_fn, qdev, "qrtr-usb-rx"); if (IS_ERR(qdev->rx_thread)) { dev_err(&qdev->udev->dev, "could not create rx_thread\n"); qrtr_endpoint_unregister(&qdev->ep); return PTR_ERR(qdev->rx_thread); } dev_dbg(&qdev->udev->dev, "QTI USB QRTR driver probed\n"); return 0; } static int qcom_usb_qrtr_suspend(struct usb_interface *intf, pm_message_t message) { struct qrtr_usb_dev *qdev = usb_get_intfdata(intf); /* Suspend the thread */ qdev->thread_state = QRTR_USB_RX_SUSPEND; usb_kill_anchored_urbs(&qdev->submitted); return 0; } static int qcom_usb_qrtr_resume(struct usb_interface *intf) { struct qrtr_usb_dev *qdev = usb_get_intfdata(intf); qdev->thread_state = QRTR_USB_RX_RUN; wake_up(&qdev->qrtr_wq); return 0; } static int qcom_usb_qrtr_reset_resume(struct usb_interface *intf) { struct qrtr_usb_dev *qdev = usb_get_intfdata(intf); int rc = 0; qrtr_endpoint_unregister(&qdev->ep); rc = qrtr_endpoint_register(&qdev->ep, QRTR_EP_NID_AUTO); if (rc) return rc; qdev->thread_state = QRTR_USB_RX_RUN; wake_up(&qdev->qrtr_wq); return rc; } static void qcom_usb_qrtr_disconnect(struct usb_interface *interface) { struct qrtr_usb_dev *qdev = usb_get_intfdata(interface); qdev->thread_state = QRTR_USB_RX_STOP; usb_kill_anchored_urbs(&qdev->submitted); kthread_stop(qdev->rx_thread); usb_free_urb(qdev->in_urb); qrtr_endpoint_unregister(&qdev->ep); usb_set_intfdata(interface, NULL); usb_put_dev(qdev->udev); } static const struct usb_device_id qcom_usb_qrtr_ids[] = { { USB_DEVICE_INTERFACE_NUMBER(QRTR_VENDOR_ID, 0x90ef, 3) }, { USB_DEVICE_INTERFACE_NUMBER(QRTR_VENDOR_ID, 0x90f0, 3) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, qcom_usb_qrtr_ids); static struct usb_driver qcom_usb_qrtr_driver = { .name = "qcom_usb_qrtr", .probe = qcom_usb_qrtr_probe, .disconnect = qcom_usb_qrtr_disconnect, .suspend = qcom_usb_qrtr_suspend, .resume = qcom_usb_qrtr_resume, .reset_resume = qcom_usb_qrtr_reset_resume, .id_table = qcom_usb_qrtr_ids, .supports_autosuspend = 1, }; module_usb_driver(qcom_usb_qrtr_driver); MODULE_DESCRIPTION("QTI IPC-Router USB interface driver"); MODULE_LICENSE("GPL v2");