1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345 |
- // SPDX-License-Identifier: GPL-2.0-only
- /* Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved. */
- #include <linux/platform_device.h>
- #include <linux/ipc_logging.h>
- #include <linux/refcount.h>
- #include <linux/device.h>
- #include <linux/module.h>
- #include <linux/mm.h>
- #include <linux/skbuff.h>
- #include <linux/rpmsg.h>
- #include <linux/cdev.h>
- #include <linux/slab.h>
- #include <linux/poll.h>
- #include <linux/idr.h>
- #include <linux/of.h>
- #include <linux/fs.h>
- #include <linux/uaccess.h>
- #include <linux/termios.h>
- #include <linux/string.h>
- #include "qcom_glink_native.h"
- /* Define IPC Logging Macros */
- #define GLINK_PKT_IPC_LOG_PAGE_CNT 32
- static void *glink_pkt_ilctxt;
- #define GLINK_PKT_INFO(x, ...) \
- ipc_log_string(glink_pkt_ilctxt, "[%s]: "x, __func__, ##__VA_ARGS__)
- #define GLINK_PKT_ERR(x, ...) \
- do { \
- printk_ratelimited("%s[%s]: " x, KERN_ERR, __func__, ##__VA_ARGS__); \
- ipc_log_string(glink_pkt_ilctxt, "%s[%s]: " x, "", __func__, \
- ##__VA_ARGS__);\
- } while (0)
- #define GLINK_PKT_IOCTL_MAGIC (0xC3)
- #define GLINK_PKT_IOCTL_QUEUE_RX_INTENT \
- _IOW(GLINK_PKT_IOCTL_MAGIC, 0, unsigned int)
- struct glink_pkt_zerocopy_receive {
- __u64 address; /* in: address of mapping */
- __u32 length; /* out: number of bytes to map/mapped */
- __u32 offset; /* out: amount of bytes to skip */
- };
- #define GLINK_PKT_IOCTL_ZC_RECV \
- _IOWR(GLINK_PKT_IOCTL_MAGIC, 1, struct glink_pkt_zerocopy_receive)
- #define GLINK_PKT_IOCTL_ZC_DONE \
- _IOWR(GLINK_PKT_IOCTL_MAGIC, 2, struct glink_pkt_zerocopy_receive)
- #define MODULE_NAME "glink_pkt"
- static dev_t glink_pkt_major;
- static struct class *glink_pkt_class;
- static int num_glink_pkt_devs;
- static DEFINE_IDA(glink_pkt_minor_ida);
- /**
- * struct glink_pkt - driver context, relates rpdev to cdev
- * @dev: glink pkt device
- * @cdev: cdev for the glink pkt device
- * @lock: synchronization of @rpdev and @open_tout modifications
- * @ch_open: wait object for opening the glink channel
- * @refcount: count how many userspace clients have handles
- * @rpdev: underlaying rpmsg device
- * @rx_done: cache whether rpdev can support external rx done
- * @queue_lock: synchronization of @queue operations
- * @queue: incoming message queue
- * @readq: wait object for incoming queue
- * @sig_change: flag to indicate serial signal change
- * @fragmented_read: set from dt node for partial read
- * @enable_ch_close: set from dt node for unregister driver on close syscall
- * @drv_lock: lock to protect rpmsg driver variable
- * @drv: rpmsg driver for registering to rpmsg bus
- * @drv_registered: status of rpmsg driver
- * @dev_name: /dev/@dev_name for glink_pkt device
- * @ch_name: glink channel to match to
- * @edge: glink edge to match to
- * @open_tout: timeout for open syscall, configurable in sysfs
- * @rskb_read_lock: Lock to protect rskb during read syscalls
- * @rskb: current skb being read
- * @rdata: data pointer in current skb
- * @rdata_len: remaining data to be read from skb
- */
- struct glink_pkt_device {
- struct device dev;
- struct cdev cdev;
- struct mutex lock;
- struct completion ch_open;
- refcount_t refcount;
- struct rpmsg_device *rpdev;
- bool rx_done;
- spinlock_t queue_lock;
- struct sk_buff_head queue;
- struct sk_buff_head pending;
- wait_queue_head_t readq;
- int sig_change;
- bool fragmented_read;
- bool enable_ch_close;
- struct mutex drv_lock;
- struct rpmsg_driver drv;
- bool drv_registered;
- const char *dev_name;
- const char *ch_name;
- const char *edge;
- int open_tout;
- struct mutex rskb_read_lock;
- struct sk_buff *rskb;
- unsigned char *rdata;
- size_t rdata_len;
- };
- #define dev_to_gpdev(_dev) container_of(_dev, struct glink_pkt_device, dev)
- #define cdev_to_gpdev(_cdev) container_of(_cdev, struct glink_pkt_device, cdev)
- #define drv_to_rpdrv(_drv) container_of(_drv, struct rpmsg_driver, drv)
- #define rpdrv_to_gpdev(_rdrv) container_of(_rdrv, struct glink_pkt_device, drv)
- static ssize_t open_timeout_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t n)
- {
- struct glink_pkt_device *gpdev = dev_to_gpdev(dev);
- long tmp;
- mutex_lock(&gpdev->lock);
- if (kstrtol(buf, 0, &tmp)) {
- mutex_unlock(&gpdev->lock);
- GLINK_PKT_ERR("unable to convert string to int for /dev/%s\n",
- gpdev->dev_name);
- return -EINVAL;
- }
- gpdev->open_tout = tmp;
- mutex_unlock(&gpdev->lock);
- return n;
- }
- static ssize_t open_timeout_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
- {
- struct glink_pkt_device *gpdev = dev_to_gpdev(dev);
- ssize_t ret;
- mutex_lock(&gpdev->lock);
- ret = scnprintf(buf, PAGE_SIZE, "%d\n", gpdev->open_tout);
- mutex_unlock(&gpdev->lock);
- return ret;
- }
- static DEVICE_ATTR_RW(open_timeout);
- static void glink_pkt_kfree_skb(struct glink_pkt_device *gpdev, struct sk_buff *skb)
- {
- int ret;
- if (gpdev->rx_done) {
- GLINK_PKT_INFO("channel:%s\n", gpdev->ch_name);
- ret = qcom_glink_rx_done(gpdev->rpdev->ept, skb->data);
- if (ret < 0)
- GLINK_PKT_INFO("Failed channel:%s ret:%d\n", gpdev->ch_name, ret);
- /*
- * Data memory is freed by qcom_glink_rx_done(), reset the
- * skb data pointers so kfree_skb() does not try to free
- * a second time and originally allocated buffer is freed
- * correctly.
- */
- skb->data = skb->head;
- }
- kfree_skb(skb);
- }
- static void glink_pkt_clear_queues(struct glink_pkt_device *gpdev)
- {
- struct sk_buff *skb;
- unsigned long flags;
- mutex_lock(&gpdev->rskb_read_lock);
- spin_lock_irqsave(&gpdev->queue_lock, flags);
- if (gpdev->rskb) {
- glink_pkt_kfree_skb(gpdev, gpdev->rskb);
- gpdev->rskb = NULL;
- gpdev->rdata = NULL;
- gpdev->rdata_len = 0;
- }
- while ((skb = skb_dequeue(&gpdev->queue)))
- glink_pkt_kfree_skb(gpdev, skb);
- while ((skb = skb_dequeue(&gpdev->pending)))
- glink_pkt_kfree_skb(gpdev, skb);
- spin_unlock_irqrestore(&gpdev->queue_lock, flags);
- mutex_unlock(&gpdev->rskb_read_lock);
- }
- static int glink_pkt_rpdev_no_copy_cb(struct rpmsg_device *rpdev, void *buf,
- int len, void *priv, u32 addr)
- {
- struct glink_pkt_device *gpdev = dev_get_drvdata(&rpdev->dev);
- unsigned long flags;
- struct sk_buff *skb;
- GLINK_PKT_INFO("Data received on:%s len:%d\n", gpdev->ch_name, len);
- skb = alloc_skb(0, GFP_ATOMIC);
- if (!skb) {
- GLINK_PKT_ERR("Failed to allocate skb\n");
- return -ENOMEM;
- }
- skb->data = buf;
- skb_reset_tail_pointer(skb);
- /* For external buffer, skb->tail and skb->len calculation does not match */
- skb->len += len;
- spin_lock_irqsave(&gpdev->queue_lock, flags);
- skb_queue_tail(&gpdev->queue, skb);
- spin_unlock_irqrestore(&gpdev->queue_lock, flags);
- /* wake up any blocking processes, waiting for new data */
- wake_up_interruptible(&gpdev->readq);
- GLINK_PKT_INFO("Data queued on:%s len:%d\n", gpdev->ch_name, len);
- return RPMSG_DEFER;
- }
- static int glink_pkt_rpdev_copy_cb(struct rpmsg_device *rpdev, void *buf,
- int len, void *priv, u32 addr)
- {
- struct glink_pkt_device *gpdev = dev_get_drvdata(&rpdev->dev);
- unsigned long flags;
- struct sk_buff *skb;
- if (!gpdev) {
- GLINK_PKT_ERR("channel is in reset\n");
- return -ENETRESET;
- }
- GLINK_PKT_INFO("Data received on:%s len:%d\n", gpdev->ch_name, len);
- skb = alloc_skb(len, GFP_ATOMIC);
- if (!skb) {
- GLINK_PKT_ERR("Failed to allocate skb\n");
- return -ENOMEM;
- }
- skb_put_data(skb, buf, len);
- spin_lock_irqsave(&gpdev->queue_lock, flags);
- skb_queue_tail(&gpdev->queue, skb);
- spin_unlock_irqrestore(&gpdev->queue_lock, flags);
- /* wake up any blocking processes, waiting for new data */
- wake_up_interruptible(&gpdev->readq);
- GLINK_PKT_INFO("Data queued on:%s len:%d\n", gpdev->ch_name, len);
- return 0;
- }
- static int glink_pkt_rpdev_cb(struct rpmsg_device *rpdev, void *buf, int len,
- void *priv, u32 addr)
- {
- struct glink_pkt_device *gpdev = dev_get_drvdata(&rpdev->dev);
- rpmsg_rx_cb_t cb = (gpdev->rx_done) ? glink_pkt_rpdev_no_copy_cb : glink_pkt_rpdev_copy_cb;
- return cb(rpdev, buf, len, priv, addr);
- }
- static int glink_pkt_rpdev_sigs(struct rpmsg_device *rpdev, void *priv,
- u32 old, u32 new)
- {
- struct device_driver *drv = rpdev->dev.driver;
- struct rpmsg_driver *rpdrv = drv_to_rpdrv(drv);
- struct glink_pkt_device *gpdev = rpdrv_to_gpdev(rpdrv);
- unsigned long flags;
- GLINK_PKT_INFO("Received signal new:0x%x old:0x%x on channel:%s\n",
- new, old, gpdev->ch_name);
- spin_lock_irqsave(&gpdev->queue_lock, flags);
- gpdev->sig_change = true;
- spin_unlock_irqrestore(&gpdev->queue_lock, flags);
- /* wake up any blocking processes, waiting for new data */
- wake_up_interruptible(&gpdev->readq);
- return 0;
- }
- static int glink_pkt_rpdev_probe(struct rpmsg_device *rpdev)
- {
- struct device_driver *drv = rpdev->dev.driver;
- struct rpmsg_driver *rpdrv = drv_to_rpdrv(drv);
- struct glink_pkt_device *gpdev = rpdrv_to_gpdev(rpdrv);
- mutex_lock(&gpdev->lock);
- gpdev->rpdev = rpdev;
- gpdev->rx_done = (qcom_glink_rx_done_supported(rpdev->ept) > 0) ? true : false;
- qcom_glink_register_signals_cb(rpdev->ept, glink_pkt_rpdev_sigs);
- mutex_unlock(&gpdev->lock);
- dev_set_drvdata(&rpdev->dev, gpdev);
- complete_all(&gpdev->ch_open);
- return 0;
- }
- static void glink_pkt_rpdev_remove(struct rpmsg_device *rpdev)
- {
- struct device_driver *drv = rpdev->dev.driver;
- struct rpmsg_driver *rpdrv = drv_to_rpdrv(drv);
- struct glink_pkt_device *gpdev = rpdrv_to_gpdev(rpdrv);
- mutex_lock(&gpdev->lock);
- glink_pkt_clear_queues(gpdev);
- gpdev->rpdev = NULL;
- mutex_unlock(&gpdev->lock);
- dev_set_drvdata(&rpdev->dev, NULL);
- /* wake up any blocked readers */
- reinit_completion(&gpdev->ch_open);
- wake_up_interruptible(&gpdev->readq);
- }
- static int glink_pkt_drv_try_register(struct glink_pkt_device *gpdev)
- {
- int ret = 0;
- mutex_lock(&gpdev->drv_lock);
- if (!gpdev->drv_registered) {
- ret = register_rpmsg_driver(&gpdev->drv);
- if (!ret)
- gpdev->drv_registered = true;
- }
- mutex_unlock(&gpdev->drv_lock);
- return ret;
- }
- static void glink_pkt_drv_try_unregister(struct glink_pkt_device *gpdev)
- {
- mutex_lock(&gpdev->drv_lock);
- if (gpdev->drv_registered) {
- unregister_rpmsg_driver(&gpdev->drv);
- gpdev->drv_registered = false;
- }
- mutex_unlock(&gpdev->drv_lock);
- }
- /**
- * glink_pkt_open() - open() syscall for the glink_pkt device
- * inode: Pointer to the inode structure.
- * file: Pointer to the file structure.
- *
- * This function is used to open the glink pkt device when
- * userspace client do a open() system call. All input arguments are
- * validated by the virtual file system before calling this function.
- */
- static int glink_pkt_open(struct inode *inode, struct file *file)
- {
- struct glink_pkt_device *gpdev = cdev_to_gpdev(inode->i_cdev);
- int tout = msecs_to_jiffies(gpdev->open_tout * 1000);
- struct device *dev = &gpdev->dev;
- int ret;
- refcount_inc(&gpdev->refcount);
- get_device(dev);
- GLINK_PKT_INFO("begin for %s by %s:%d ref_cnt[%d]\n",
- gpdev->ch_name, current->comm,
- task_pid_nr(current), refcount_read(&gpdev->refcount));
- if (gpdev->enable_ch_close)
- glink_pkt_drv_try_register(gpdev);
- ret = wait_for_completion_interruptible_timeout(&gpdev->ch_open, tout);
- if (ret <= 0) {
- if (gpdev->enable_ch_close)
- glink_pkt_drv_try_unregister(gpdev);
- refcount_dec(&gpdev->refcount);
- put_device(dev);
- GLINK_PKT_INFO("timeout for %s by %s:%d\n", gpdev->ch_name,
- current->comm, task_pid_nr(current));
- return -ETIMEDOUT;
- }
- file->private_data = gpdev;
- GLINK_PKT_INFO("end for %s by %s:%d ref_cnt[%d]\n",
- gpdev->ch_name, current->comm,
- task_pid_nr(current), refcount_read(&gpdev->refcount));
- return 0;
- }
- /**
- * glink_pkt_release() - release operation on glink_pkt device
- * inode: Pointer to the inode structure.
- * file: Pointer to the file structure.
- *
- * This function is used to release the glink pkt device when
- * userspace client do a close() system call. All input arguments are
- * validated by the virtual file system before calling this function.
- */
- static int glink_pkt_release(struct inode *inode, struct file *file)
- {
- struct glink_pkt_device *gpdev = cdev_to_gpdev(inode->i_cdev);
- struct device *dev = &gpdev->dev;
- unsigned long flags;
- GLINK_PKT_INFO("for %s by %s:%d ref_cnt[%d]\n",
- gpdev->ch_name, current->comm,
- task_pid_nr(current), refcount_read(&gpdev->refcount));
- refcount_dec(&gpdev->refcount);
- if (refcount_read(&gpdev->refcount) == 1) {
- glink_pkt_clear_queues(gpdev);
- spin_lock_irqsave(&gpdev->queue_lock, flags);
- gpdev->sig_change = false;
- wake_up_interruptible(&gpdev->readq);
- spin_unlock_irqrestore(&gpdev->queue_lock, flags);
- if (gpdev->enable_ch_close)
- glink_pkt_drv_try_unregister(gpdev);
- }
- put_device(dev);
- return 0;
- }
- /**
- * glink_pkt_read() - read() syscall for the glink_pkt device
- * file: Pointer to the file structure.
- * buf: Pointer to the userspace buffer.
- * count: Number bytes to read from the file.
- * ppos: Pointer to the position into the file.
- *
- * This function is used to Read the data from glink pkt device when
- * userspace client do a read() system call. All input arguments are
- * validated by the virtual file system before calling this function.
- */
- static ssize_t glink_pkt_read(struct file *file,
- char __user *buf, size_t count, loff_t *ppos)
- {
- struct glink_pkt_device *gpdev = file->private_data;
- struct sk_buff *skb = NULL;
- int ret = 0;
- int use;
- if (!gpdev || refcount_read(&gpdev->refcount) == 1) {
- GLINK_PKT_ERR("invalid device handle\n");
- return -EINVAL;
- }
- if (!completion_done(&gpdev->ch_open)) {
- GLINK_PKT_ERR("%s channel in reset\n", gpdev->ch_name);
- return -ENETRESET;
- }
- GLINK_PKT_INFO("begin for %s by %s:%d ref_cnt[%d], remaining[%d], count[%d]\n",
- gpdev->ch_name, current->comm,
- task_pid_nr(current), refcount_read(&gpdev->refcount),
- gpdev->rdata_len, count);
- /* Wait for data in the queue */
- spin_lock_irq(&gpdev->queue_lock);
- if (skb_queue_empty(&gpdev->queue) && !gpdev->rskb) {
- if (file->f_flags & O_NONBLOCK) {
- spin_unlock_irq(&gpdev->queue_lock);
- return -EAGAIN;
- }
- /* Wait until we get data or the endpoint goes away */
- ret = wait_event_interruptible_lock_irq(gpdev->readq,
- !skb_queue_empty(&gpdev->queue) ||
- !completion_done(&gpdev->ch_open),
- gpdev->queue_lock);
- }
- spin_unlock_irq(&gpdev->queue_lock);
- if (ret)
- return -ERESTARTSYS;
- if (!completion_done(&gpdev->ch_open))
- return -ENETRESET;
- mutex_lock(&gpdev->rskb_read_lock);
- spin_lock_irq(&gpdev->queue_lock);
- if (!gpdev->rskb) {
- gpdev->rskb = skb_dequeue(&gpdev->queue);
- if (!gpdev->rskb) {
- spin_unlock_irq(&gpdev->queue_lock);
- mutex_unlock(&gpdev->rskb_read_lock);
- return 0;
- }
- gpdev->rdata = gpdev->rskb->data;
- gpdev->rdata_len = gpdev->rskb->len;
- }
- spin_unlock_irq(&gpdev->queue_lock);
- use = min_t(size_t, count, gpdev->rdata_len);
- if (copy_to_user(buf, gpdev->rdata, use))
- ret = -EFAULT;
- spin_lock_irq(&gpdev->queue_lock);
- gpdev->rdata += use;
- gpdev->rdata_len -= use;
- if (!gpdev->fragmented_read || !gpdev->rdata_len) {
- skb = gpdev->rskb;
- gpdev->rskb = NULL;
- gpdev->rdata = NULL;
- gpdev->rdata_len = 0;
- }
- spin_unlock_irq(&gpdev->queue_lock);
- if (skb)
- glink_pkt_kfree_skb(gpdev, skb);
- mutex_unlock(&gpdev->rskb_read_lock);
- ret = (ret < 0) ? ret : use;
- GLINK_PKT_INFO("end for %s by %s:%d ret[%d], remaining[%d]\n", gpdev->ch_name,
- current->comm, task_pid_nr(current), ret, gpdev->rdata_len);
- return ret;
- }
- /**
- * glink_pkt_write() - write() syscall for the glink_pkt device
- * file: Pointer to the file structure.
- * buf: Pointer to the userspace buffer.
- * count: Number bytes to read from the file.
- * ppos: Pointer to the position into the file.
- *
- * This function is used to write the data to glink pkt device when
- * userspace client do a write() system call. All input arguments are
- * validated by the virtual file system before calling this function.
- */
- static ssize_t glink_pkt_write(struct file *file,
- const char __user *buf, size_t count, loff_t *ppos)
- {
- struct glink_pkt_device *gpdev = file->private_data;
- void *kbuf;
- int ret;
- gpdev = file->private_data;
- if (!gpdev || refcount_read(&gpdev->refcount) == 1) {
- GLINK_PKT_ERR("invalid device handle\n");
- return -EINVAL;
- }
- GLINK_PKT_INFO("begin to %s buffer_size %zu\n", gpdev->ch_name, count);
- kbuf = vmemdup_user(buf, count);
- if (IS_ERR(kbuf))
- return PTR_ERR(kbuf);
- if (mutex_lock_interruptible(&gpdev->lock)) {
- ret = -ERESTARTSYS;
- goto free_kbuf;
- }
- if (!completion_done(&gpdev->ch_open) || !gpdev->rpdev) {
- GLINK_PKT_ERR("%s channel in reset\n", gpdev->ch_name);
- ret = -ENETRESET;
- goto unlock_ch;
- }
- if (file->f_flags & O_NONBLOCK)
- ret = rpmsg_trysend(gpdev->rpdev->ept, kbuf, count);
- else
- ret = rpmsg_send(gpdev->rpdev->ept, kbuf, count);
- unlock_ch:
- mutex_unlock(&gpdev->lock);
- free_kbuf:
- kvfree(kbuf);
- GLINK_PKT_INFO("finish to %s ret %d\n", gpdev->ch_name, ret);
- return ret < 0 ? ret : count;
- }
- /**
- * glink_pkt_poll() - poll() syscall for the glink_pkt device
- * file: Pointer to the file structure.
- * wait: pointer to Poll table.
- *
- * This function is used to poll on the glink pkt device when
- * userspace client do a poll() system call. All input arguments are
- * validated by the virtual file system before calling this function.
- */
- static __poll_t glink_pkt_poll(struct file *file, poll_table *wait)
- {
- struct glink_pkt_device *gpdev = file->private_data;
- __poll_t mask = 0;
- unsigned long flags;
- gpdev = file->private_data;
- if (!gpdev || refcount_read(&gpdev->refcount) == 1) {
- GLINK_PKT_ERR("invalid device handle\n");
- return POLLERR;
- }
- if (!completion_done(&gpdev->ch_open)) {
- GLINK_PKT_ERR("%s channel in reset\n", gpdev->ch_name);
- return POLLHUP | POLLPRI;
- }
- GLINK_PKT_INFO("Wait for pkt on channel:%s\n", gpdev->ch_name);
- poll_wait(file, &gpdev->readq, wait);
- mutex_lock(&gpdev->lock);
- if (!completion_done(&gpdev->ch_open) || !gpdev->rpdev) {
- GLINK_PKT_ERR("%s channel reset after wait\n", gpdev->ch_name);
- mutex_unlock(&gpdev->lock);
- return POLLHUP;
- }
- spin_lock_irqsave(&gpdev->queue_lock, flags);
- if (!skb_queue_empty(&gpdev->queue) || gpdev->rskb)
- mask |= POLLIN | POLLRDNORM;
- if (gpdev->sig_change)
- mask |= POLLPRI;
- spin_unlock_irqrestore(&gpdev->queue_lock, flags);
- mask |= rpmsg_poll(gpdev->rpdev->ept, file, wait);
- mutex_unlock(&gpdev->lock);
- GLINK_PKT_INFO("Exit channel:%s\n", gpdev->ch_name);
- return mask;
- }
- /**
- * glink_pkt_tiocmset() - set the signals for glink_pkt device
- * devp: Pointer to the glink_pkt device structure.
- * cmd: IOCTL command.
- * arg: Arguments to the ioctl call.
- *
- * This function is used to set the signals on the glink pkt device
- * when userspace client do a ioctl() system call with TIOCMBIS,
- * TIOCMBIC and TICOMSET.
- */
- static int glink_pkt_tiocmset(struct glink_pkt_device *gpdev, unsigned int cmd,
- int __user *arg)
- {
- u32 set, clear, val;
- int ret;
- ret = get_user(val, arg);
- if (ret)
- return ret;
- set = clear = 0;
- switch (cmd) {
- case TIOCMBIS:
- set = val;
- break;
- case TIOCMBIC:
- clear = val;
- break;
- case TIOCMSET:
- set = val;
- clear = ~val;
- break;
- }
- set &= TIOCM_DTR | TIOCM_RTS | TIOCM_CD | TIOCM_RI;
- clear &= TIOCM_DTR | TIOCM_RTS | TIOCM_CD | TIOCM_RI;
- GLINK_PKT_INFO("set[0x%x] clear[0x%x]\n", set, clear);
- return qcom_glink_set_signals(gpdev->rpdev->ept, set, clear);
- }
- static const struct vm_operations_struct glink_pkt_vm_ops = {
- };
- static int glink_pkt_mmap(struct file *file, struct vm_area_struct *vma)
- {
- if (vma->vm_flags & (VM_WRITE | VM_EXEC))
- return -EPERM;
- vm_flags_clear(vma, VM_MAYWRITE | VM_MAYEXEC);
- /* Instruct vm_insert_page() to not mmap_read_lock(mm) */
- vm_flags_set(vma, VM_MIXEDMAP);
- vma->vm_ops = &glink_pkt_vm_ops;
- return 0;
- }
- struct glink_pkt_zerocopy_cb {
- unsigned long leading_page;
- unsigned long trailing_page;
- unsigned long address;
- unsigned long length;
- };
- static int glink_pkt_zerocopy_done(struct glink_pkt_device *gpdev,
- struct glink_pkt_zerocopy_receive *zc)
- {
- unsigned long address = (unsigned long)zc->address;
- struct glink_pkt_zerocopy_cb *cb = NULL;
- struct vm_area_struct *vma;
- struct sk_buff *skb;
- unsigned long flags;
- if (!PAGE_ALIGNED(address) || address != zc->address)
- return -EINVAL;
- if (!gpdev->rx_done)
- return -EINVAL;
- mmap_read_lock(current->mm);
- vma = vma_lookup(current->mm, address);
- if (!vma || vma->vm_ops != &glink_pkt_vm_ops) {
- mmap_read_unlock(current->mm);
- return -EINVAL;
- }
- spin_lock_irqsave(&gpdev->queue_lock, flags);
- skb = skb_peek(&gpdev->pending);
- if (skb) {
- do {
- cb = (struct glink_pkt_zerocopy_cb *)skb->cb;
- if (address == cb->address) {
- skb_unlink(skb, &gpdev->pending);
- break;
- }
- } while ((skb = skb_peek_next(skb, &gpdev->pending)));
- }
- spin_unlock_irqrestore(&gpdev->queue_lock, flags);
- if (cb && cb->address == address)
- zap_vma_ptes(vma, address, cb->length);
- mmap_read_unlock(current->mm);
- if (!skb)
- return -EINVAL;
- if (cb->trailing_page)
- free_page(cb->trailing_page);
- if (cb->leading_page)
- free_page(cb->leading_page);
- glink_pkt_kfree_skb(gpdev, skb);
- return 0;
- }
- static struct page *glink_pkt_vaddr_to_page(void *cpu_addr)
- {
- if (is_vmalloc_addr(cpu_addr))
- return vmalloc_to_page(cpu_addr);
- return virt_to_page(cpu_addr);
- }
- static int glink_pkt_zerocopy_receive(struct glink_pkt_device *gpdev,
- struct glink_pkt_zerocopy_receive *zc)
- {
- unsigned long address = (unsigned long)zc->address;
- struct glink_pkt_zerocopy_cb *cb;
- unsigned long trailing_page = 0;
- unsigned long leading_page = 0;
- unsigned long data_address;
- struct vm_area_struct *vma;
- unsigned int pages_to_map;
- u32 total_bytes_to_map;
- struct sk_buff *skb;
- unsigned long flags;
- u32 data_len;
- u32 vma_len;
- int rc;
- if (!PAGE_ALIGNED(address) || address != zc->address)
- return -EINVAL;
- if (!gpdev->rx_done)
- return -EINVAL;
- zc->offset = 0;
- zc->length = 0;
- /* Check if address is being used in any of the pending mappings */
- spin_lock_irqsave(&gpdev->queue_lock, flags);
- skb = skb_peek(&gpdev->pending);
- if (skb) {
- do {
- cb = (struct glink_pkt_zerocopy_cb *)skb->cb;
- if (address >= cb->address && address <= (cb->address + cb->length)) {
- spin_unlock_irqrestore(&gpdev->queue_lock, flags);
- return -EINVAL;
- }
- } while ((skb = skb_peek_next(skb, &gpdev->pending)));
- }
- spin_unlock_irqrestore(&gpdev->queue_lock, flags);
- mmap_read_lock(current->mm);
- vma = vma_lookup(current->mm, address);
- if (!vma || vma->vm_ops != &glink_pkt_vm_ops) {
- rc = -EINVAL;
- goto error_out;
- }
- vma_len = vma->vm_end - address;
- spin_lock_irqsave(&gpdev->queue_lock, flags);
- skb = skb_dequeue(&gpdev->queue);
- spin_unlock_irqrestore(&gpdev->queue_lock, flags);
- if (!skb) {
- rc = -EIO;
- goto error_out;
- }
- data_address = (unsigned long)skb->data;
- data_len = skb->len;
- /* Pass sanity checks, start actual mapping procedure */
- total_bytes_to_map = data_len;
- /*
- * If the skb data is not page aligned, then a blank page needs to be
- * allocated, zeroed out and the data copied to prevent information
- * leaks
- */
- if (!PAGE_ALIGNED(data_address)) {
- u32 copy_size;
- u32 offset;
- void *buf;
- leading_page = get_zeroed_page(GFP_KERNEL);
- if (!leading_page) {
- rc = -ENOMEM;
- goto skb_repush;
- }
- offset = data_address - ALIGN_DOWN(data_address, PAGE_SIZE);
- copy_size = PAGE_SIZE - offset;
- buf = (void *)leading_page;
- memcpy(buf + offset, (void *)data_address, copy_size);
- total_bytes_to_map = total_bytes_to_map - copy_size + PAGE_SIZE;
- zc->offset = offset;
- }
- /*
- * If the data does not end of the page boundary, then we need to copy
- * the trailing data into a zeroed out page, similar to the first page
- */
- if (!PAGE_ALIGNED(data_address + data_len)) {
- u32 copy_size;
- void *dst;
- unsigned long end;
- trailing_page = get_zeroed_page(GFP_KERNEL);
- if (!trailing_page) {
- rc = -ENOMEM;
- goto free_leading;
- }
- end = data_address + data_len;
- copy_size = end - ALIGN_DOWN(end, PAGE_SIZE);
- dst = (void *)trailing_page;
- memcpy(dst, (void *)ALIGN_DOWN(end, PAGE_SIZE), copy_size);
- total_bytes_to_map = total_bytes_to_map - copy_size + PAGE_SIZE;
- }
- if (vma_len < total_bytes_to_map) {
- rc = -ENOSPC;
- goto free_trailing;
- }
- if (!PAGE_ALIGNED(total_bytes_to_map)) {
- rc = -EINVAL;
- goto free_trailing;
- }
- zap_vma_ptes(vma, address, total_bytes_to_map);
- pages_to_map = total_bytes_to_map / PAGE_SIZE;
- if (leading_page) {
- rc = vm_insert_page(vma, address, virt_to_page(leading_page));
- if (rc)
- goto zap_pages;
- address += PAGE_SIZE;
- }
- data_address = ALIGN(data_address, PAGE_SIZE);
- while (pages_to_map) {
- struct page *page;
- page = glink_pkt_vaddr_to_page((void *)data_address);
- prefetchw(page);
- rc = vm_insert_page(vma, address, page);
- if (rc)
- goto zap_pages;
- address += PAGE_SIZE;
- data_address += PAGE_SIZE;
- pages_to_map--;
- }
- if (trailing_page) {
- rc = vm_insert_page(vma, address, virt_to_page(trailing_page));
- if (rc)
- goto zap_pages;
- address += PAGE_SIZE;
- }
- zc->length = data_len;
- spin_lock_irqsave(&gpdev->queue_lock, flags);
- cb = (struct glink_pkt_zerocopy_cb *)skb->cb;
- cb->leading_page = leading_page;
- cb->trailing_page = trailing_page;
- cb->address = zc->address;
- cb->length = total_bytes_to_map;
- skb_queue_tail(&gpdev->pending, skb);
- spin_unlock_irqrestore(&gpdev->queue_lock, flags);
- mmap_read_unlock(current->mm);
- return 0;
- zap_pages:
- zap_vma_ptes(vma, zc->address, total_bytes_to_map);
- free_trailing:
- if (trailing_page)
- free_page(trailing_page);
- free_leading:
- if (leading_page)
- free_page(leading_page);
- skb_repush:
- spin_lock_irqsave(&gpdev->queue_lock, flags);
- skb_queue_head(&gpdev->queue, skb);
- spin_unlock_irqrestore(&gpdev->queue_lock, flags);
- error_out:
- mmap_read_unlock(current->mm);
- return rc;
- }
- /**
- * glink_pkt_ioctl() - ioctl() syscall for the glink_pkt device
- * file: Pointer to the file structure.
- * cmd: IOCTL command.
- * arg: Arguments to the ioctl call.
- *
- * This function is used to ioctl on the glink pkt device when
- * userspace client do a ioctl() system call. All input arguments are
- * validated by the virtual file system before calling this function.
- */
- static long glink_pkt_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
- {
- struct glink_pkt_zerocopy_receive zc;
- struct glink_pkt_device *gpdev;
- unsigned long flags;
- int ret;
- gpdev = file->private_data;
- if (!gpdev || refcount_read(&gpdev->refcount) == 1) {
- GLINK_PKT_ERR("invalid device handle\n");
- return -EINVAL;
- }
- if (mutex_lock_interruptible(&gpdev->lock))
- return -ERESTARTSYS;
- if (!completion_done(&gpdev->ch_open)) {
- GLINK_PKT_ERR("%s channel in reset\n", gpdev->ch_name);
- mutex_unlock(&gpdev->lock);
- return -ENETRESET;
- }
- switch (cmd) {
- case TIOCMGET:
- spin_lock_irqsave(&gpdev->queue_lock, flags);
- gpdev->sig_change = false;
- spin_unlock_irqrestore(&gpdev->queue_lock, flags);
- ret = qcom_glink_get_signals(gpdev->rpdev->ept);
- if (ret >= 0)
- ret = put_user(ret, (int __user *)arg);
- break;
- case TIOCMSET:
- case TIOCMBIS:
- case TIOCMBIC:
- ret = glink_pkt_tiocmset(gpdev, cmd, (int __user *)arg);
- break;
- case GLINK_PKT_IOCTL_QUEUE_RX_INTENT:
- /* Return success to not break userspace client logic */
- ret = 0;
- break;
- case GLINK_PKT_IOCTL_ZC_RECV:
- if (copy_from_user(&zc, (void __user *)arg, sizeof(zc))) {
- ret = -EFAULT;
- break;
- }
- ret = glink_pkt_zerocopy_receive(gpdev, &zc);
- if (copy_to_user((void __user *)arg, &zc, sizeof(zc)))
- ret = -EFAULT;
- break;
- case GLINK_PKT_IOCTL_ZC_DONE:
- if (copy_from_user(&zc, (void __user *)arg, sizeof(zc))) {
- ret = -EFAULT;
- break;
- }
- ret = glink_pkt_zerocopy_done(gpdev, &zc);
- break;
- default:
- GLINK_PKT_ERR("unrecognized ioctl command 0x%x\n", cmd);
- ret = -ENOIOCTLCMD;
- }
- mutex_unlock(&gpdev->lock);
- return ret;
- }
- static const struct file_operations glink_pkt_fops = {
- .owner = THIS_MODULE,
- .open = glink_pkt_open,
- .release = glink_pkt_release,
- .read = glink_pkt_read,
- .write = glink_pkt_write,
- .poll = glink_pkt_poll,
- .unlocked_ioctl = glink_pkt_ioctl,
- .mmap = glink_pkt_mmap,
- .compat_ioctl = glink_pkt_ioctl,
- };
- static ssize_t name_show(struct device *dev, struct device_attribute *attr,
- char *buf)
- {
- struct glink_pkt_device *gpdev = dev_to_gpdev(dev);
- return scnprintf(buf, RPMSG_NAME_SIZE, "%s\n", gpdev->ch_name);
- }
- static DEVICE_ATTR_RO(name);
- static struct attribute *glink_pkt_device_attrs[] = {
- &dev_attr_name.attr,
- NULL,
- };
- ATTRIBUTE_GROUPS(glink_pkt_device);
- /**
- * parse_glinkpkt_devicetree() - parse device tree binding for a subnode
- *
- * np: pointer to a device tree node
- * gpdev: pointer to GLINK PACKET device
- *
- * Return: 0 on success, standard Linux error codes on error.
- */
- static int glink_pkt_parse_devicetree(struct device_node *np,
- struct glink_pkt_device *gpdev)
- {
- char *key;
- int ret;
- key = "qcom,glinkpkt-edge";
- ret = of_property_read_string(np, key, &gpdev->edge);
- if (ret < 0)
- goto error;
- key = "qcom,glinkpkt-ch-name";
- ret = of_property_read_string(np, key, &gpdev->ch_name);
- if (ret < 0)
- goto error;
- key = "qcom,glinkpkt-dev-name";
- ret = of_property_read_string(np, key, &gpdev->dev_name);
- if (ret < 0)
- goto error;
- key = "qcom,glinkpkt-enable-ch-close";
- gpdev->enable_ch_close = of_property_read_bool(np, key);
- key = "qcom,glinkpkt-fragmented-read";
- gpdev->fragmented_read = of_property_read_bool(np, key);
- GLINK_PKT_INFO(
- "Parsed %s:%s /dev/%s enable channel close:%d fragmented-read:%d\n",
- gpdev->edge, gpdev->ch_name, gpdev->dev_name,
- gpdev->enable_ch_close, gpdev->fragmented_read);
- return 0;
- error:
- GLINK_PKT_ERR("%s: missing key: %s\n", __func__, key);
- return ret;
- }
- static void glink_pkt_release_device(struct device *dev)
- {
- struct glink_pkt_device *gpdev = dev_to_gpdev(dev);
- GLINK_PKT_INFO("for %s by %s:%d ref_cnt[%d]\n",
- gpdev->ch_name, current->comm,
- task_pid_nr(current), refcount_read(&gpdev->refcount));
- ida_simple_remove(&glink_pkt_minor_ida, MINOR(gpdev->dev.devt));
- cdev_del(&gpdev->cdev);
- kfree(gpdev);
- }
- static int glink_pkt_init_rpmsg(struct glink_pkt_device *gpdev)
- {
- struct rpmsg_driver *rpdrv = &gpdev->drv;
- struct device *dev = &gpdev->dev;
- struct rpmsg_device_id *match;
- char *drv_name;
- match = devm_kzalloc(dev, sizeof(*match) * 2, GFP_KERNEL);
- if (!match)
- return -ENOMEM;
- strscpy(match->name, gpdev->ch_name, RPMSG_NAME_SIZE);
- drv_name = devm_kasprintf(dev, GFP_KERNEL,
- "%s_%s", "glink_pkt", gpdev->dev_name);
- if (!drv_name)
- return -ENOMEM;
- rpdrv->probe = glink_pkt_rpdev_probe;
- rpdrv->remove = glink_pkt_rpdev_remove;
- rpdrv->callback = glink_pkt_rpdev_cb;
- rpdrv->id_table = match;
- rpdrv->drv.name = drv_name;
- return glink_pkt_drv_try_register(gpdev);
- }
- /**
- * glink_pkt_add_device() - Create glink packet device and add cdev
- * parent: pointer to the parent device of this glink packet device
- * np: pointer to device node this glink packet device represents
- *
- * return: 0 for success, Standard Linux errors
- */
- static int glink_pkt_create_device(struct device *parent,
- struct device_node *np)
- {
- struct glink_pkt_device *gpdev;
- struct device *dev;
- int ret, minor;
- gpdev = kzalloc(sizeof(*gpdev), GFP_KERNEL);
- if (!gpdev)
- return -ENOMEM;
- minor = ida_simple_get(&glink_pkt_minor_ida, 0, num_glink_pkt_devs,
- GFP_KERNEL);
- if (minor < 0) {
- kfree(gpdev);
- return minor;
- }
- dev = &gpdev->dev;
- ret = glink_pkt_parse_devicetree(np, gpdev);
- if (ret < 0) {
- GLINK_PKT_ERR("failed to parse dt ret:%d\n", ret);
- ida_simple_remove(&glink_pkt_minor_ida, MINOR(dev->devt));
- kfree(gpdev);
- return ret;
- }
- mutex_init(&gpdev->lock);
- mutex_init(&gpdev->drv_lock);
- mutex_init(&gpdev->rskb_read_lock);
- refcount_set(&gpdev->refcount, 1);
- init_completion(&gpdev->ch_open);
- /* Default open timeout for open is 120 sec */
- gpdev->open_tout = 120;
- gpdev->sig_change = false;
- gpdev->rx_done = false;
- spin_lock_init(&gpdev->queue_lock);
- gpdev->rskb = NULL;
- gpdev->rdata = NULL;
- gpdev->rdata_len = 0;
- skb_queue_head_init(&gpdev->queue);
- skb_queue_head_init(&gpdev->pending);
- init_waitqueue_head(&gpdev->readq);
- device_initialize(dev);
- dev->class = glink_pkt_class;
- dev->parent = parent;
- dev->groups = glink_pkt_device_groups;
- dev_set_drvdata(dev, gpdev);
- cdev_init(&gpdev->cdev, &glink_pkt_fops);
- gpdev->cdev.owner = THIS_MODULE;
- dev->devt = MKDEV(MAJOR(glink_pkt_major), minor);
- dev_set_name(dev, gpdev->dev_name, minor);
- ret = cdev_add(&gpdev->cdev, dev->devt, 1);
- if (ret) {
- GLINK_PKT_ERR("cdev_add failed for %s ret:%d\n",
- gpdev->dev_name, ret);
- ida_simple_remove(&glink_pkt_minor_ida, MINOR(dev->devt));
- kfree(gpdev);
- return ret;
- }
- dev->release = glink_pkt_release_device;
- ret = device_add(dev);
- if (ret) {
- GLINK_PKT_ERR("device_create failed for %s ret:%d\n",
- gpdev->dev_name, ret);
- goto free_dev;
- }
- if (device_create_file(dev, &dev_attr_open_timeout))
- GLINK_PKT_ERR("device_create_file failed for %s\n",
- gpdev->dev_name);
- ret = glink_pkt_init_rpmsg(gpdev);
- if (ret)
- goto free_dev;
- return 0;
- free_dev:
- put_device(dev);
- return ret;
- }
- /**
- * glink_pkt_deinit() - De-initialize this module
- *
- * This function frees all the memory and unregisters the char device region.
- */
- static void glink_pkt_deinit(void)
- {
- class_destroy(glink_pkt_class);
- unregister_chrdev_region(MAJOR(glink_pkt_major), num_glink_pkt_devs);
- }
- /**
- * glink_pkt_probe() - Probe a GLINK packet device
- *
- * pdev: Pointer to platform device.
- *
- * return: 0 on success, standard Linux error codes on error.
- *
- * This function is called when the underlying device tree driver registers
- * a platform device, mapped to a G-Link packet device.
- */
- static int glink_pkt_probe(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
- struct device_node *cn;
- int ret;
- num_glink_pkt_devs = of_get_child_count(dev->of_node);
- ret = alloc_chrdev_region(&glink_pkt_major, 0, num_glink_pkt_devs,
- "glinkpkt");
- if (ret < 0) {
- GLINK_PKT_ERR("alloc_chrdev_region failed ret:%d\n", ret);
- return ret;
- }
- glink_pkt_class = class_create(THIS_MODULE, "glinkpkt");
- if (IS_ERR(glink_pkt_class)) {
- ret = PTR_ERR(glink_pkt_class);
- GLINK_PKT_ERR("class_create failed ret:%d\n", ret);
- goto error_deinit;
- }
- for_each_child_of_node(dev->of_node, cn) {
- glink_pkt_create_device(dev, cn);
- }
- GLINK_PKT_INFO("G-Link Packet Port Driver Initialized\n");
- return 0;
- error_deinit:
- glink_pkt_deinit();
- return ret;
- }
- static const struct of_device_id glink_pkt_match_table[] = {
- { .compatible = "qcom,glinkpkt" },
- {},
- };
- static struct platform_driver glink_pkt_driver = {
- .probe = glink_pkt_probe,
- .driver = {
- .name = MODULE_NAME,
- .of_match_table = glink_pkt_match_table,
- },
- };
- /**
- * glink_pkt_init() - Initialization function for this module
- *
- * returns: 0 on success, standard Linux error code otherwise.
- */
- static int __init glink_pkt_init(void)
- {
- int ret;
- ret = platform_driver_register(&glink_pkt_driver);
- if (ret) {
- GLINK_PKT_ERR("%s: glink_pkt register failed %d\n",
- __func__, ret);
- return ret;
- }
- glink_pkt_ilctxt = ipc_log_context_create(GLINK_PKT_IPC_LOG_PAGE_CNT,
- "glink_pkt", 0);
- return 0;
- }
- /**
- * glink_pkt_exit() - Exit function for this module
- *
- * This function is used to cleanup the module during the exit.
- */
- static void __exit glink_pkt_exit(void)
- {
- glink_pkt_deinit();
- platform_driver_unregister(&glink_pkt_driver);
- }
- module_init(glink_pkt_init);
- module_exit(glink_pkt_exit);
- MODULE_DESCRIPTION("MSM G-Link Packet Port");
- MODULE_LICENSE("GPL");
|