linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Dmitry Kalinkin <dmitry.kalinkin@gmail.com>
To: linux-kernel@vger.kernel.org, devel@driverdev.osuosl.org
Cc: Martyn Welch <martyn.welch@ge.com>,
	Manohar Vanga <manohar.vanga@gmail.com>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Igor Alekseev <igor.alekseev@itep.ru>,
	Dmitry Kalinkin <dmitry.kalinkin@gmail.com>
Subject: [PATCHv4 4/4] staging: vme_user: provide DMA functionality
Date: Sat, 13 Jun 2015 16:34:04 +0300	[thread overview]
Message-ID: <1434202444-25719-5-git-send-email-dmitry.kalinkin@gmail.com> (raw)
In-Reply-To: <1434202444-25719-1-git-send-email-dmitry.kalinkin@gmail.com>

This introduces a new dma device that provides a single ioctl call that
provides DMA read and write functionality to the user space.

Signed-off-by: Dmitry Kalinkin <dmitry.kalinkin@gmail.com>
Cc: Igor Alekseev <igor.alekseev@itep.ru>
---
 drivers/staging/vme/devices/vme_user.c | 201 ++++++++++++++++++++++++++++++++-
 drivers/staging/vme/devices/vme_user.h |  11 ++
 2 files changed, 209 insertions(+), 3 deletions(-)

diff --git a/drivers/staging/vme/devices/vme_user.c b/drivers/staging/vme/devices/vme_user.c
index 9cca97a..5cc782e 100644
--- a/drivers/staging/vme/devices/vme_user.c
+++ b/drivers/staging/vme/devices/vme_user.c
@@ -79,15 +79,18 @@ static unsigned int bus_num;
  * We shall support 4 masters and 4 slaves with this driver.
  */
 #define VME_MAJOR	221	/* VME Major Device Number */
-#define VME_DEVS	9	/* Number of dev entries */
+#define VME_DEVS	10	/* Number of dev entries */
 
 #define MASTER_MINOR	0
 #define MASTER_MAX	3
 #define SLAVE_MINOR	4
 #define SLAVE_MAX	7
 #define CONTROL_MINOR	8
+#define DMA_MINOR	9
 
-#define PCI_BUF_SIZE  0x20000	/* Size of one slave image buffer */
+#define PCI_BUF_SIZE	0x20000		/* Size of one slave image buffer */
+
+#define VME_MAX_DMA_LEN	0x4000000	/* Maximal DMA transfer length */
 
 /*
  * Structure to handle image related parameters.
@@ -112,7 +115,7 @@ static const int type[VME_DEVS] = {	MASTER_MINOR,	MASTER_MINOR,
 					MASTER_MINOR,	MASTER_MINOR,
 					SLAVE_MINOR,	SLAVE_MINOR,
 					SLAVE_MINOR,	SLAVE_MINOR,
-					CONTROL_MINOR
+					CONTROL_MINOR,	DMA_MINOR
 				};
 
 struct vme_user_vma_priv {
@@ -343,6 +346,168 @@ static loff_t vme_user_llseek(struct file *file, loff_t off, int whence)
 	return -EINVAL;
 }
 
+static int vme_user_sg_to_dma_list(const struct vme_dma_op *dma_op,
+				   struct sg_table *sgt,
+				   int sg_count, struct vme_dma_list *dma_list)
+{
+	ssize_t pos = 0;
+	struct scatterlist *sg;
+	int i, ret;
+
+	for_each_sg(sgt->sgl, sg, sg_count, i) {
+		struct vme_dma_attr *pci_attr, *vme_attr, *src, *dest;
+		dma_addr_t hw_address = sg_dma_address(sg);
+		unsigned int hw_len = sg_dma_len(sg);
+
+		vme_attr = vme_dma_vme_attribute(dma_op->vme_addr + pos,
+						 dma_op->aspace,
+						 dma_op->cycle,
+						 dma_op->dwidth);
+		if (!vme_attr)
+			return -ENOMEM;
+
+		pci_attr = vme_dma_pci_attribute(hw_address);
+		if (!pci_attr) {
+			vme_dma_free_attribute(vme_attr);
+			return -ENOMEM;
+		}
+
+		switch (dma_op->dir) {
+		case VME_DMA_MEM_TO_VME:
+			src = pci_attr;
+			dest = vme_attr;
+			break;
+		case VME_DMA_VME_TO_MEM:
+			src = vme_attr;
+			dest = pci_attr;
+			break;
+		}
+
+		ret = vme_dma_list_add(dma_list, src, dest, hw_len);
+
+		/*
+		 * XXX VME API doesn't mention whether we should keep
+		 * attributes around
+		 */
+		vme_dma_free_attribute(vme_attr);
+		vme_dma_free_attribute(pci_attr);
+
+		if (ret)
+			return ret;
+
+		pos += hw_len;
+	}
+
+	return 0;
+}
+
+static enum dma_data_direction vme_dir_to_dma_dir(unsigned vme_dir)
+{
+	switch (vme_dir) {
+	case VME_DMA_VME_TO_MEM:
+		return DMA_FROM_DEVICE;
+	case VME_DMA_MEM_TO_VME:
+		return DMA_TO_DEVICE;
+	}
+
+	return DMA_NONE;
+}
+
+static ssize_t vme_user_dma_ioctl(unsigned int minor,
+				  const struct vme_dma_op *dma_op)
+{
+	unsigned int offset = offset_in_page(dma_op->buf_vaddr);
+	unsigned long nr_pages;
+	enum dma_data_direction dir;
+	struct vme_dma_list *dma_list;
+	struct sg_table *sgt = NULL;
+	struct page **pages = NULL;
+	long got_pages;
+	ssize_t count;
+	int retval, sg_count;
+
+	/* Prevent WARN from dma_map_sg */
+	if (dma_op->count == 0)
+		return 0;
+
+	/*
+	 * This is a voluntary limit to prevent huge allocation for pages
+	 * array. VME_MAX_DMA_LEN is not a fundamental VME constraint.
+	 */
+	count = min_t(size_t, dma_op->count, VME_MAX_DMA_LEN);
+	nr_pages = (offset + count + PAGE_SIZE - 1) >> PAGE_SHIFT;
+
+	dir = vme_dir_to_dma_dir(dma_op->dir);
+	if (dir == DMA_NONE)
+		return -EINVAL;
+
+	pages = kmalloc_array(nr_pages, sizeof(pages[0]), GFP_KERNEL);
+	if (!pages) {
+		retval = -ENOMEM;
+		goto free;
+	}
+
+	sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
+	if (!sgt) {
+		retval = -ENOMEM;
+		goto free;
+	}
+
+	dma_list = vme_new_dma_list(image[minor].resource);
+	if (!dma_list) {
+		retval = -ENOMEM;
+		goto free;
+	}
+
+	got_pages = get_user_pages_fast(dma_op->buf_vaddr, nr_pages,
+					dir == DMA_FROM_DEVICE, pages);
+	if (got_pages != nr_pages) {
+		pr_debug("Not all pages were pinned\n");
+		retval = (got_pages < 0) ? got_pages : -EFAULT;
+		goto release_pages;
+	}
+
+	retval = sg_alloc_table_from_pages(sgt, pages, nr_pages,
+					   offset, count, GFP_KERNEL);
+	if (retval)
+		goto release_pages;
+
+	sg_count = dma_map_sg(vme_user_bridge->dev.parent,
+			      sgt->sgl, sgt->nents, dir);
+	if (!sg_count) {
+		pr_debug("DMA mapping error\n");
+		retval = -EFAULT;
+		goto free_sgt;
+	}
+
+	retval = vme_user_sg_to_dma_list(dma_op, sgt, sg_count, dma_list);
+	if (retval)
+		goto dma_unmap;
+
+	retval = vme_dma_list_exec(dma_list);
+
+dma_unmap:
+	dma_unmap_sg(vme_user_bridge->dev.parent, sgt->sgl, sgt->nents, dir);
+
+free_sgt:
+	sg_free_table(sgt);
+
+release_pages:
+	if (got_pages > 0)
+		release_pages(pages, got_pages, 0);
+
+	vme_dma_list_free(dma_list);
+
+free:
+	kfree(sgt);
+	kfree(pages);
+
+	if (retval)
+		return retval;
+
+	return count;
+}
+
 /*
  * The ioctls provided by the old VME access method (the one at vmelinux.org)
  * are most certainly wrong as the effectively push the registers layout
@@ -359,6 +524,7 @@ static int vme_user_ioctl(struct inode *inode, struct file *file,
 	struct vme_master master;
 	struct vme_slave slave;
 	struct vme_irq_id irq_req;
+	struct vme_dma_op dma_op;
 	unsigned long copied;
 	unsigned int minor = MINOR(inode->i_rdev);
 	int retval;
@@ -467,6 +633,19 @@ static int vme_user_ioctl(struct inode *inode, struct file *file,
 			break;
 		}
 		break;
+	case DMA_MINOR:
+		switch (cmd) {
+		case VME_DO_DMA:
+			copied = copy_from_user(&dma_op, argp,
+						sizeof(dma_op));
+			if (copied != 0) {
+				pr_warn("Partial copy from userspace\n");
+				return -EFAULT;
+			}
+
+			return vme_user_dma_ioctl(minor, &dma_op);
+		}
+		break;
 	}
 
 	return -EINVAL;
@@ -678,6 +857,15 @@ static int vme_user_probe(struct vme_dev *vdev)
 		}
 	}
 
+	image[DMA_MINOR].resource = vme_dma_request(vme_user_bridge,
+		VME_DMA_VME_TO_MEM | VME_DMA_MEM_TO_VME);
+	if (!image[DMA_MINOR].resource) {
+		dev_warn(&vdev->dev,
+			 "Unable to allocate dma resource\n");
+		err = -ENOMEM;
+		goto err_master;
+	}
+
 	/* Create sysfs entries - on udev systems this creates the dev files */
 	vme_user_sysfs_class = class_create(THIS_MODULE, driver_name);
 	if (IS_ERR(vme_user_sysfs_class)) {
@@ -700,6 +888,9 @@ static int vme_user_probe(struct vme_dev *vdev)
 		case SLAVE_MINOR:
 			name = "bus/vme/s%d";
 			break;
+		case DMA_MINOR:
+			name = "bus/vme/dma0";
+			break;
 		default:
 			err = -EINVAL;
 			goto err_sysfs;
@@ -724,6 +915,8 @@ err_sysfs:
 	}
 	class_destroy(vme_user_sysfs_class);
 
+	vme_dma_free(image[DMA_MINOR].resource);
+
 	/* Ensure counter set correcty to unalloc all master windows */
 	i = MASTER_MAX + 1;
 err_master:
@@ -764,6 +957,8 @@ static int vme_user_remove(struct vme_dev *dev)
 	}
 	class_destroy(vme_user_sysfs_class);
 
+	vme_dma_free(image[DMA_MINOR].resource);
+
 	for (i = MASTER_MINOR; i < (MASTER_MAX + 1); i++) {
 		kfree(image[i].kern_buf);
 		vme_master_free(image[i].resource);
diff --git a/drivers/staging/vme/devices/vme_user.h b/drivers/staging/vme/devices/vme_user.h
index b8cc7bc..252b1c9 100644
--- a/drivers/staging/vme/devices/vme_user.h
+++ b/drivers/staging/vme/devices/vme_user.h
@@ -48,11 +48,22 @@ struct vme_irq_id {
 	__u8 statid;
 };
 
+struct vme_dma_op {
+	__u64 vme_addr;		/* Starting Address on the VMEbus */
+	__u64 buf_vaddr;	/* Pointer to userspace memory */
+	__u32 count;		/* Count of bytes to copy */
+	__u32 aspace;		/* Address Space */
+	__u32 cycle;		/* Cycle properties */
+	__u32 dwidth;		/* Data transfer width */
+	__u32 dir;		/* Transfer Direction */
+};
+
 #define VME_GET_SLAVE _IOR(VME_IOC_MAGIC, 1, struct vme_slave)
 #define VME_SET_SLAVE _IOW(VME_IOC_MAGIC, 2, struct vme_slave)
 #define VME_GET_MASTER _IOR(VME_IOC_MAGIC, 3, struct vme_master)
 #define VME_SET_MASTER _IOW(VME_IOC_MAGIC, 4, struct vme_master)
 #define VME_IRQ_GEN _IOW(VME_IOC_MAGIC, 5, struct vme_irq_id)
+#define VME_DO_DMA _IOW(VME_IOC_MAGIC, 7, struct vme_dma_op)
 
 #endif /* _VME_USER_H_ */
 
-- 
1.8.3.1


  parent reply	other threads:[~2015-06-13 13:35 UTC|newest]

Thread overview: 49+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-05-28 12:06 [PATCHv3 00/16] vme DMA and user space driver improvements Dmitry Kalinkin
2015-05-28 12:06 ` [PATCHv3 01/16] Documentation: mention vme_master_mmap() in VME API Dmitry Kalinkin
2015-05-28 12:06 ` [PATCHv3 02/16] vme: tsi148: fix DMA lists longer that one item Dmitry Kalinkin
2015-05-28 12:07 ` [PATCHv3 03/16] vme: tsi148: fix first DMA item mapping Dmitry Kalinkin
2015-05-28 12:07 ` [PATCHv3 04/16] vme: stop DMA transfer on interruption Dmitry Kalinkin
2015-05-28 12:07 ` [PATCHv3 05/16] staging: vme_user: refactor llseek to switch(){} Dmitry Kalinkin
2015-05-28 12:07 ` [PATCHv3 06/16] vme: check for A64 overflow in vme_check_window() Dmitry Kalinkin
2015-05-28 12:07 ` [PATCHv3 07/16] vme: export vme_check_window() Dmitry Kalinkin
2015-05-28 12:07 ` [PATCHv3 08/16] staging: vme_user: provide DMA functionality Dmitry Kalinkin
2015-06-13  0:28   ` Greg Kroah-Hartman
2015-07-06 13:22     ` Martyn Welch
2015-07-06 13:50       ` Dmitry Kalinkin
2015-07-06 14:48         ` Martyn Welch
2015-07-06 17:24           ` Dmitry Kalinkin
2015-07-07  7:13             ` Alessio Igor Bogani
2015-07-08 13:22             ` Martyn Welch
2015-07-08 15:02               ` Generic VME UIO driver Dmitry Kalinkin
2015-07-20  8:09                 ` Martyn Welch
     [not found]             ` <CAPk1OjEX7YX5J=yMPOGyGg7ZT6P-iKtaGRDDv2oARPFUcdnKnQ@mail.gmail.com>
2015-07-07 10:52               ` [PATCHv3 08/16] staging: vme_user: provide DMA functionality Dmitry Kalinkin
2015-07-08 13:57                 ` Martyn Welch
2015-07-08 14:47                   ` Dmitry Kalinkin
     [not found]               ` <78FC1849-FFE4-49E5-8421-25D27324F790@gmail.com>
2015-07-07 12:51                 ` Alessio Igor Bogani
2015-07-07 13:04                   ` Dmitry Kalinkin
2015-07-08 13:41                   ` Martyn Welch
2015-07-08 14:39                     ` Dmitry Kalinkin
2015-07-08 14:42                     ` [PATCH] vme: lower alignment requirement in pci bridge drivers Dmitry Kalinkin
2015-07-08 13:28               ` [PATCHv3 08/16] staging: vme_user: provide DMA functionality Martyn Welch
2015-05-28 12:07 ` [PATCHv3 09/16] vme: ca91cx42: return error code on DMA error Dmitry Kalinkin
2015-05-28 12:07 ` [PATCHv3 10/16] vme: ca91cx42: fix LM_CTL address mask Dmitry Kalinkin
2015-05-28 12:07 ` [PATCHv3 11/16] staging: vme_user: remove unused counters Dmitry Kalinkin
2015-05-28 12:07 ` [PATCHv3 12/16] staging: vme_user: remove forward declarations Dmitry Kalinkin
2015-05-28 12:07 ` [PATCHv3 13/16] staging: vme_user: remove open/release Dmitry Kalinkin
2015-05-28 12:07 ` [PATCHv3 14/16] staging: vme_user: remove buf_unalloc helper Dmitry Kalinkin
2015-05-28 12:07 ` [PATCHv3 15/16] vme: tsi148: depend on HAS_DMA for Kconfig Dmitry Kalinkin
2015-05-28 12:07 ` [PATCHv3 16/16] vme: provide uapi header Dmitry Kalinkin
2015-06-13  0:30   ` Greg Kroah-Hartman
2015-05-31  3:06 ` [PATCHv3 00/16] vme DMA and user space driver improvements Greg Kroah-Hartman
2015-06-10 13:09   ` Dmitry Kalinkin
2015-06-13  0:31     ` Greg Kroah-Hartman
2015-06-13  2:04       ` Dmitry Kalinkin
2015-06-13  2:24         ` Greg Kroah-Hartman
2015-06-13  2:30           ` Dmitry Kalinkin
2015-06-13  4:40             ` Greg Kroah-Hartman
2015-06-13 13:34               ` [PATCHv4 0/4] " Dmitry Kalinkin
2015-06-13 13:34                 ` [PATCHv4 1/4] staging: vme_user: remove forward declarations Dmitry Kalinkin
2015-06-13 13:34                 ` [PATCHv4 2/4] staging: vme_user: remove open/release Dmitry Kalinkin
2015-06-13 13:34                 ` [PATCHv4 3/4] staging: vme_user: remove buf_unalloc helper Dmitry Kalinkin
2015-06-13 13:34                 ` Dmitry Kalinkin [this message]
2015-06-13 21:47                 ` [PATCHv4 0/4] vme DMA and user space driver improvements Greg Kroah-Hartman

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1434202444-25719-5-git-send-email-dmitry.kalinkin@gmail.com \
    --to=dmitry.kalinkin@gmail.com \
    --cc=devel@driverdev.osuosl.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=igor.alekseev@itep.ru \
    --cc=linux-kernel@vger.kernel.org \
    --cc=manohar.vanga@gmail.com \
    --cc=martyn.welch@ge.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).