[RFC 4/4] firewire: add mem1394

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



While the previous patches were purely infrastructure, this patch
actually adds the code using it: mem1394.

There are some open questions on a few things, maybe someone can help
out there.

diff --git a/drivers/ieee1394/Kconfig b/drivers/ieee1394/Kconfig
index 39142e2..cd7b28c 100644
--- a/drivers/ieee1394/Kconfig
+++ b/drivers/ieee1394/Kconfig
@@ -169,4 +169,21 @@ config IEEE1394_RAWIO
 	  To compile this driver as a module, say M here: the
 	  module will be called raw1394.
 
+config IEEE1394_MEMDEV
+	tristate "IEEE1394 memory device support"
+	depends on IEEE1394 && EXPERIMENTAL
+	help
+	  Say Y here if you want support for the ieee1394 memory device.
+	  This is useful for debugging systems attached via firewire
+	  since it usually allows you to read from and write to their memory,
+	  depending on the controller and machine setup.
+	  
+	  It differs from raw access (which allows the same usage) in that
+	  it provides devices nodes (usually called /dev/fwmem-<guid>) that can
+	  be read and written with any tool, as opposed to specialised tools
+	  required for raw1394.
+	  
+	  To compile this driver as a module, say M here: the
+	  module will be called mem1394.
+
 endmenu
diff --git a/drivers/ieee1394/Makefile b/drivers/ieee1394/Makefile
index 180bf82..7da4e21 100644
--- a/drivers/ieee1394/Makefile
+++ b/drivers/ieee1394/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_IEEE1394_RAWIO) += raw1394.
 obj-$(CONFIG_IEEE1394_SBP2) += sbp2.o
 obj-$(CONFIG_IEEE1394_DV1394) += dv1394.o
 obj-$(CONFIG_IEEE1394_ETH1394) += eth1394.o
+obj-$(CONFIG_IEEE1394_MEMDEV) += mem1394.o
 
 quiet_cmd_oui2c = OUI2C   [email protected]
       cmd_oui2c = $(CONFIG_SHELL) $(src)/oui2c.sh < $< > [email protected]
diff --git a/drivers/ieee1394/mem1394.c b/drivers/ieee1394/mem1394.c
new file mode 100644
index 0000000..e9d44a2
--- /dev/null
+++ b/drivers/ieee1394/mem1394.c
@@ -0,0 +1,277 @@
+/*
+ * IEEE 1394 for Linux
+ *
+ * Copyright (C) 2006 Johannes Berg <[email protected]>
+ *
+ * This code is licensed under the GPL v2. See the file COPYING in the root
+ * directory of the kernel sources for details.
+ *
+ * This module provides a character device for each node attached
+ * to the bus and allows direct memory access on them.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/cdev.h>
+
+#include "ieee1394.h"
+#include "ieee1394_core.h"
+#include "ieee1394_transactions.h"
+#include "mem1394.h"
+#include "nodemgr.h"
+#include "highlevel.h"
+
+static int mem1394_mmap(struct file * file, struct vm_area_struct * vma)
+{
+	return -ENOSYS;
+}
+
+static int mem1394_open(struct inode *inode, struct file *file)
+{
+	struct mem1394_file_info *fi;
+
+	fi = kzalloc(sizeof(*fi), GFP_KERNEL);
+	if (!fi)
+		return -ENOMEM;
+	file->private_data = fi;
+	fi->memdev = container_of(inode->i_cdev, struct mem1394_dev, cdev);
+	return 0;
+}
+
+/* This function essentially clones hpsb_read. Might be better
+ * to create a new hpsb_read_user function instead... */
+static int mem1394_read(struct file *file, char __user * buffer,
+			size_t count, loff_t *offset)
+{
+	struct mem1394_file_info *fi = (struct mem1394_file_info*)file->private_data;
+	/* lower levels only support count as a multiple of 4 */
+	size_t submitcount = (count + (4-1)) & ~(4-1);
+	int retval = 0;
+	struct hpsb_packet *packet;
+
+	/* this is a bit icky. I think I'll want to create a
+	 * "struct hpsb_node_class_interface" that you register
+	 * with nodemgr.c instead of registering the "struct class_interface"
+	 * directly. It would wrap around the "struct class_interface"
+	 * and handle things like this.
+	 *
+	 * This means it would call the node_class_interface's
+	 *  - "add" method whenever the device is fully there, and an
+	 *  - "update" method when it survived a bus reset, and the
+	 *  - "remove" method when it went away, also taking care of
+	 * debouncing, which the mem1394 interface currently doesn't handle.
+	 *
+	 * But I need advice on this. It'll probably works this way
+	 * but most likely not once this interface stuff gets more
+	 * use; I can imagine using it for scanners instead of raw1394
+	 * so that the kernel can validate that a user can only
+	 * access a certain scanner and not all 1394 devices on the bus.
+	 * In other words some 'raw1394intf' instead of 'raw1394' which
+	 * creates one character device per ieee1394 node for finer
+	 * grained access control.
+	 * That would definitely want to have debouncing etc.
+	 *
+	 * However, I don't fully understand the states node_entries go
+	 * through yet, so I'm not sure this should even be here!
+	 * Maybe it should be in open? But then the device could go
+	 * into limbo when it is already opened...
+	 *
+	 * Similarly, what happens if a node is suspended?
+	 */
+	if (fi->memdev->ne->in_limbo)
+		return -ENODEV;
+
+	if (count == 0)
+		return 0;
+
+	/* I wonder if it is possible to do DMA directly to the userspace buffer... */
+	packet = hpsb_make_readpacket(fi->memdev->ne->host, fi->memdev->ne->nodeid, *offset, submitcount);
+	if (!packet)
+		return -ENOENT;
+	
+	packet->generation = fi->memdev->ne->generation;
+	retval = hpsb_send_packet_and_wait(packet);
+	if (retval < 0)
+		goto out_free;
+
+	retval = hpsb_packet_success(packet);
+	
+	if (retval == 0) {
+		if (submitcount == 4) {
+			if (copy_to_user(buffer, &packet->header[3], count))
+				retval = -EFAULT;
+		} else {
+			if (copy_to_user(buffer, packet->data, count))
+				retval = -EFAULT;
+		}
+	}
+
+	if (retval == 0) {
+		retval = count;
+		*offset += count;
+	}
+
+ out_free:
+	hpsb_free_tlabel(packet);
+	hpsb_free_packet(packet);
+
+	return retval;
+}
+
+static int mem1394_release(struct inode *inode, struct file *file)
+{
+	struct mem1394_file_info *fi = (struct mem1394_file_info*)file->private_data;
+	
+	kfree(fi);
+
+	return 0;
+}
+
+static struct class *mem1394_sysfs_class;
+
+static struct file_operations mem1394_fops = {
+	.owner = THIS_MODULE,
+	.mmap = mem1394_mmap,
+	.open = mem1394_open,
+	.read = mem1394_read,
+	.release = mem1394_release,
+};
+
+static atomic_t mem1394_dev_ctr;
+
+static struct mem1394_dev * alloc_mem1394_dev(struct device *dev)
+{
+	struct mem1394_dev *result;
+	struct node_entry *ne = container_of(dev, struct node_entry, device);
+	int ret;
+	struct class_device * mem1394_class_member;
+
+	result = kzalloc(sizeof(*result), GFP_KERNEL);
+	if (!result)
+		return NULL;
+
+	cdev_init(&result->cdev, &mem1394_fops);
+	result->cdev.owner = THIS_MODULE;
+	result->ne = ne;
+
+	ret = hpsb_cdev_add(&result->cdev);
+	if (ret) {
+		printk(KERN_ERR "mem1394: failed to register character device!\n");
+		goto out_free;
+	}
+
+	atomic_inc(&mem1394_dev_ctr);
+	mem1394_class_member = class_device_create(mem1394_sysfs_class, NULL, result->cdev.dev,
+						dev, "fwmem-%d", atomic_read(&mem1394_dev_ctr));
+	if (IS_ERR(mem1394_class_member)) {
+		printk(KERN_WARNING "mem1394: class_device_create failed\n");
+	} else {
+		class_set_devdata(mem1394_class_member, result);
+	}
+	dev->driver_data = result;
+
+	return result;
+ out_free:
+	kfree(result);
+	return NULL;
+}
+
+static DEFINE_SPINLOCK(dev_list_lock);
+static LIST_HEAD(dev_list);
+
+static int mem1394_add(struct class_device *cl_dev, struct class_interface *cl_intf)
+{
+	struct mem1394_dev *memdev;
+
+	if (!cl_dev) {
+		printk("cl_dev not assigned\n");
+		return -ENOENT;
+	}
+	if (!cl_dev->dev) {
+		printk("cl_dev->dev not assigned\n");
+		return -ENONET;
+	}
+
+	memdev = alloc_mem1394_dev(cl_dev->dev);
+	if (!memdev)
+		return -ENOMEM;
+
+	spin_lock(&dev_list_lock);
+	list_add_tail(&memdev->list, &dev_list);
+	spin_unlock(&dev_list_lock);
+
+	/* need we do anything else? */
+	return 0;
+}
+
+static void mem1394_del(struct mem1394_dev *memdev)
+{
+	class_device_destroy(mem1394_sysfs_class, memdev->cdev.dev);
+
+	/* kill off everything that might be in progress */
+	/* TODO */
+
+	/* remove character device */
+	hpsb_cdev_del(&memdev->cdev);
+	kfree(memdev);
+}
+
+static void mem1394_remove(struct class_device *cl_dev, struct class_interface *cl_intf)
+{
+	struct mem1394_dev *memdev, *tmp, *found = NULL;
+	
+	/* find our memdev corresponding to the class device */
+	spin_lock(&dev_list_lock);
+	list_for_each_entry_safe(memdev, tmp, &dev_list, list) {
+		if (cl_dev->dev == memdev->dev) {
+			list_del(&memdev->list);
+			found = memdev;
+			break;
+		}
+	}
+	spin_unlock(&dev_list_lock);
+	if (!found)
+		return;
+
+	mem1394_del(found);
+}
+
+static struct class_interface mem1394_interface = {
+	.add		= mem1394_add,
+	.remove		= mem1394_remove,
+};
+                
+static int __init init_mem1394(void)
+{
+	int ret;
+	
+	spin_lock_init(&dev_list_lock);
+	
+	mem1394_sysfs_class = class_create(THIS_MODULE, "mem1394");
+	if (IS_ERR(mem1394_sysfs_class)) {
+		return PTR_ERR(mem1394_sysfs_class);
+	}
+
+	ret = hpsb_register_node_interface(&mem1394_interface);
+	return ret;
+}
+
+static void __exit cleanup_mem1394(void)
+{
+	struct mem1394_dev *memdev, *tmp;
+
+	hpsb_unregister_node_interface(&mem1394_interface);
+	list_for_each_entry_safe(memdev, tmp, &dev_list, list) {
+		list_del(&memdev->list);
+		mem1394_del(memdev);
+	}
+	class_destroy(mem1394_sysfs_class);
+}
+
+module_init(init_mem1394);
+module_exit(cleanup_mem1394);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Johannes Berg <[email protected]>");
diff --git a/drivers/ieee1394/mem1394.h b/drivers/ieee1394/mem1394.h
new file mode 100644
index 0000000..e7cc8ab
--- /dev/null
+++ b/drivers/ieee1394/mem1394.h
@@ -0,0 +1,20 @@
+#ifndef IEEE1394_MEM1394_H
+#define IEEE1394_MEM1394_H
+
+#include <asm/types.h>
+#include <linux/cdev.h>
+#include <linux/list.h>
+#include "nodemgr.h"
+
+struct mem1394_dev {
+	struct device *dev;
+	struct node_entry *ne;
+	struct cdev cdev;
+	struct list_head list;
+};
+
+struct mem1394_file_info {
+	struct mem1394_dev *memdev;
+};
+
+#endif /* IEEE1394_MEM1394_H */


-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux