aboutsummaryrefslogtreecommitdiff
path: root/drivers/ieee1394/raw1394.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/ieee1394/raw1394.c
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/ieee1394/raw1394.c')
-rw-r--r--drivers/ieee1394/raw1394.c2958
1 files changed, 2958 insertions, 0 deletions
diff --git a/drivers/ieee1394/raw1394.c b/drivers/ieee1394/raw1394.c
new file mode 100644
index 00000000000..6a08a8982ea
--- /dev/null
+++ b/drivers/ieee1394/raw1394.c
@@ -0,0 +1,2958 @@
+/*
+ * IEEE 1394 for Linux
+ *
+ * Raw interface to the bus
+ *
+ * Copyright (C) 1999, 2000 Andreas E. Bombe
+ * 2001, 2002 Manfred Weihs <weihs@ict.tuwien.ac.at>
+ * 2002 Christian Toegel <christian.toegel@gmx.at>
+ *
+ * This code is licensed under the GPL. See the file COPYING in the root
+ * directory of the kernel sources for details.
+ *
+ *
+ * Contributions:
+ *
+ * Manfred Weihs <weihs@ict.tuwien.ac.at>
+ * configuration ROM manipulation
+ * address range mapping
+ * adaptation for new (transparent) loopback mechanism
+ * sending of arbitrary async packets
+ * Christian Toegel <christian.toegel@gmx.at>
+ * address range mapping
+ * lock64 request
+ * transmit physical packet
+ * busreset notification control (switch on/off)
+ * busreset with selection of type (short/long)
+ * request_reply
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/vmalloc.h>
+#include <linux/cdev.h>
+#include <asm/uaccess.h>
+#include <asm/atomic.h>
+#include <linux/devfs_fs_kernel.h>
+
+#include "csr1212.h"
+#include "ieee1394.h"
+#include "ieee1394_types.h"
+#include "ieee1394_core.h"
+#include "nodemgr.h"
+#include "hosts.h"
+#include "highlevel.h"
+#include "iso.h"
+#include "ieee1394_transactions.h"
+#include "raw1394.h"
+#include "raw1394-private.h"
+
+#define int2ptr(x) ((void __user *)(unsigned long)x)
+#define ptr2int(x) ((u64)(unsigned long)(void __user *)x)
+
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
+#define RAW1394_DEBUG
+#endif
+
+#ifdef RAW1394_DEBUG
+#define DBGMSG(fmt, args...) \
+printk(KERN_INFO "raw1394:" fmt "\n" , ## args)
+#else
+#define DBGMSG(fmt, args...)
+#endif
+
+static LIST_HEAD(host_info_list);
+static int host_count;
+static DEFINE_SPINLOCK(host_info_lock);
+static atomic_t internal_generation = ATOMIC_INIT(0);
+
+static atomic_t iso_buffer_size;
+static const int iso_buffer_max = 4 * 1024 * 1024; /* 4 MB */
+
+static struct hpsb_highlevel raw1394_highlevel;
+
+static int arm_read(struct hpsb_host *host, int nodeid, quadlet_t * buffer,
+ u64 addr, size_t length, u16 flags);
+static int arm_write(struct hpsb_host *host, int nodeid, int destid,
+ quadlet_t * data, u64 addr, size_t length, u16 flags);
+static int arm_lock(struct hpsb_host *host, int nodeid, quadlet_t * store,
+ u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode,
+ u16 flags);
+static int arm_lock64(struct hpsb_host *host, int nodeid, octlet_t * store,
+ u64 addr, octlet_t data, octlet_t arg, int ext_tcode,
+ u16 flags);
+static struct hpsb_address_ops arm_ops = {
+ .read = arm_read,
+ .write = arm_write,
+ .lock = arm_lock,
+ .lock64 = arm_lock64,
+};
+
+static void queue_complete_cb(struct pending_request *req);
+
+static struct pending_request *__alloc_pending_request(int flags)
+{
+ struct pending_request *req;
+
+ req = (struct pending_request *)kmalloc(sizeof(struct pending_request),
+ flags);
+ if (req != NULL) {
+ memset(req, 0, sizeof(struct pending_request));
+ INIT_LIST_HEAD(&req->list);
+ }
+
+ return req;
+}
+
+static inline struct pending_request *alloc_pending_request(void)
+{
+ return __alloc_pending_request(SLAB_KERNEL);
+}
+
+static void free_pending_request(struct pending_request *req)
+{
+ if (req->ibs) {
+ if (atomic_dec_and_test(&req->ibs->refcount)) {
+ atomic_sub(req->ibs->data_size, &iso_buffer_size);
+ kfree(req->ibs);
+ }
+ } else if (req->free_data) {
+ kfree(req->data);
+ }
+ hpsb_free_packet(req->packet);
+ kfree(req);
+}
+
+/* fi->reqlists_lock must be taken */
+static void __queue_complete_req(struct pending_request *req)
+{
+ struct file_info *fi = req->file_info;
+ list_del(&req->list);
+ list_add_tail(&req->list, &fi->req_complete);
+
+ up(&fi->complete_sem);
+ wake_up_interruptible(&fi->poll_wait_complete);
+}
+
+static void queue_complete_req(struct pending_request *req)
+{
+ unsigned long flags;
+ struct file_info *fi = req->file_info;
+
+ spin_lock_irqsave(&fi->reqlists_lock, flags);
+ __queue_complete_req(req);
+ spin_unlock_irqrestore(&fi->reqlists_lock, flags);
+}
+
+static void queue_complete_cb(struct pending_request *req)
+{
+ struct hpsb_packet *packet = req->packet;
+ int rcode = (packet->header[1] >> 12) & 0xf;
+
+ switch (packet->ack_code) {
+ case ACKX_NONE:
+ case ACKX_SEND_ERROR:
+ req->req.error = RAW1394_ERROR_SEND_ERROR;
+ break;
+ case ACKX_ABORTED:
+ req->req.error = RAW1394_ERROR_ABORTED;
+ break;
+ case ACKX_TIMEOUT:
+ req->req.error = RAW1394_ERROR_TIMEOUT;
+ break;
+ default:
+ req->req.error = (packet->ack_code << 16) | rcode;
+ break;
+ }
+
+ if (!((packet->ack_code == ACK_PENDING) && (rcode == RCODE_COMPLETE))) {
+ req->req.length = 0;
+ }
+
+ if ((req->req.type == RAW1394_REQ_ASYNC_READ) ||
+ (req->req.type == RAW1394_REQ_ASYNC_WRITE) ||
+ (req->req.type == RAW1394_REQ_ASYNC_STREAM) ||
+ (req->req.type == RAW1394_REQ_LOCK) ||
+ (req->req.type == RAW1394_REQ_LOCK64))
+ hpsb_free_tlabel(packet);
+
+ queue_complete_req(req);
+}
+
+static void add_host(struct hpsb_host *host)
+{
+ struct host_info *hi;
+ unsigned long flags;
+
+ hi = (struct host_info *)kmalloc(sizeof(struct host_info), GFP_KERNEL);
+
+ if (hi != NULL) {
+ INIT_LIST_HEAD(&hi->list);
+ hi->host = host;
+ INIT_LIST_HEAD(&hi->file_info_list);
+
+ spin_lock_irqsave(&host_info_lock, flags);
+ list_add_tail(&hi->list, &host_info_list);
+ host_count++;
+ spin_unlock_irqrestore(&host_info_lock, flags);
+ }
+
+ atomic_inc(&internal_generation);
+}
+
+static struct host_info *find_host_info(struct hpsb_host *host)
+{
+ struct host_info *hi;
+
+ list_for_each_entry(hi, &host_info_list, list)
+ if (hi->host == host)
+ return hi;
+
+ return NULL;
+}
+
+static void remove_host(struct hpsb_host *host)
+{
+ struct host_info *hi;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host_info_lock, flags);
+ hi = find_host_info(host);
+
+ if (hi != NULL) {
+ list_del(&hi->list);
+ host_count--;
+ /*
+ FIXME: address ranges should be removed
+ and fileinfo states should be initialized
+ (including setting generation to
+ internal-generation ...)
+ */
+ }
+ spin_unlock_irqrestore(&host_info_lock, flags);
+
+ if (hi == NULL) {
+ printk(KERN_ERR "raw1394: attempt to remove unknown host "
+ "0x%p\n", host);
+ return;
+ }
+
+ kfree(hi);
+
+ atomic_inc(&internal_generation);
+}
+
+static void host_reset(struct hpsb_host *host)
+{
+ unsigned long flags;
+ struct host_info *hi;
+ struct file_info *fi;
+ struct pending_request *req;
+
+ spin_lock_irqsave(&host_info_lock, flags);
+ hi = find_host_info(host);
+
+ if (hi != NULL) {
+ list_for_each_entry(fi, &hi->file_info_list, list) {
+ if (fi->notification == RAW1394_NOTIFY_ON) {
+ req = __alloc_pending_request(SLAB_ATOMIC);
+
+ if (req != NULL) {
+ req->file_info = fi;
+ req->req.type = RAW1394_REQ_BUS_RESET;
+ req->req.generation =
+ get_hpsb_generation(host);
+ req->req.misc = (host->node_id << 16)
+ | host->node_count;
+ if (fi->protocol_version > 3) {
+ req->req.misc |=
+ (NODEID_TO_NODE
+ (host->irm_id)
+ << 8);
+ }
+
+ queue_complete_req(req);
+ }
+ }
+ }
+ }
+ spin_unlock_irqrestore(&host_info_lock, flags);
+}
+
+static void iso_receive(struct hpsb_host *host, int channel, quadlet_t * data,
+ size_t length)
+{
+ unsigned long flags;
+ struct host_info *hi;
+ struct file_info *fi;
+ struct pending_request *req, *req_next;
+ struct iso_block_store *ibs = NULL;
+ LIST_HEAD(reqs);
+
+ if ((atomic_read(&iso_buffer_size) + length) > iso_buffer_max) {
+ HPSB_INFO("dropped iso packet");
+ return;
+ }
+
+ spin_lock_irqsave(&host_info_lock, flags);
+ hi = find_host_info(host);
+
+ if (hi != NULL) {
+ list_for_each_entry(fi, &hi->file_info_list, list) {
+ if (!(fi->listen_channels & (1ULL << channel)))
+ continue;
+
+ req = __alloc_pending_request(SLAB_ATOMIC);
+ if (!req)
+ break;
+
+ if (!ibs) {
+ ibs = kmalloc(sizeof(struct iso_block_store)
+ + length, SLAB_ATOMIC);
+ if (!ibs) {
+ kfree(req);
+ break;
+ }
+
+ atomic_add(length, &iso_buffer_size);
+ atomic_set(&ibs->refcount, 0);
+ ibs->data_size = length;
+ memcpy(ibs->data, data, length);
+ }
+
+ atomic_inc(&ibs->refcount);
+
+ req->file_info = fi;
+ req->ibs = ibs;
+ req->data = ibs->data;
+ req->req.type = RAW1394_REQ_ISO_RECEIVE;
+ req->req.generation = get_hpsb_generation(host);
+ req->req.misc = 0;
+ req->req.recvb = ptr2int(fi->iso_buffer);
+ req->req.length = min(length, fi->iso_buffer_length);
+
+ list_add_tail(&req->list, &reqs);
+ }
+ }
+ spin_unlock_irqrestore(&host_info_lock, flags);
+
+ list_for_each_entry_safe(req, req_next, &reqs, list)
+ queue_complete_req(req);
+}
+
+static void fcp_request(struct hpsb_host *host, int nodeid, int direction,
+ int cts, u8 * data, size_t length)
+{
+ unsigned long flags;
+ struct host_info *hi;
+ struct file_info *fi;
+ struct pending_request *req, *req_next;
+ struct iso_block_store *ibs = NULL;
+ LIST_HEAD(reqs);
+
+ if ((atomic_read(&iso_buffer_size) + length) > iso_buffer_max) {
+ HPSB_INFO("dropped fcp request");
+ return;
+ }
+
+ spin_lock_irqsave(&host_info_lock, flags);
+ hi = find_host_info(host);
+
+ if (hi != NULL) {
+ list_for_each_entry(fi, &hi->file_info_list, list) {
+ if (!fi->fcp_buffer)
+ continue;
+
+ req = __alloc_pending_request(SLAB_ATOMIC);
+ if (!req)
+ break;
+
+ if (!ibs) {
+ ibs = kmalloc(sizeof(struct iso_block_store)
+ + length, SLAB_ATOMIC);
+ if (!ibs) {
+ kfree(req);
+ break;
+ }
+
+ atomic_add(length, &iso_buffer_size);
+ atomic_set(&ibs->refcount, 0);
+ ibs->data_size = length;
+ memcpy(ibs->data, data, length);
+ }
+
+ atomic_inc(&ibs->refcount);
+
+ req->file_info = fi;
+ req->ibs = ibs;
+ req->data = ibs->data;
+ req->req.type = RAW1394_REQ_FCP_REQUEST;
+ req->req.generation = get_hpsb_generation(host);
+ req->req.misc = nodeid | (direction << 16);
+ req->req.recvb = ptr2int(fi->fcp_buffer);
+ req->req.length = length;
+
+ list_add_tail(&req->list, &reqs);
+ }
+ }
+ spin_unlock_irqrestore(&host_info_lock, flags);
+
+ list_for_each_entry_safe(req, req_next, &reqs, list)
+ queue_complete_req(req);
+}
+
+static ssize_t raw1394_read(struct file *file, char __user * buffer,
+ size_t count, loff_t * offset_is_ignored)
+{
+ struct file_info *fi = (struct file_info *)file->private_data;
+ struct list_head *lh;
+ struct pending_request *req;
+ ssize_t ret;
+
+ if (count != sizeof(struct raw1394_request)) {
+ return -EINVAL;
+ }
+
+ if (!access_ok(VERIFY_WRITE, buffer, count)) {
+ return -EFAULT;
+ }
+
+ if (file->f_flags & O_NONBLOCK) {
+ if (down_trylock(&fi->complete_sem)) {
+ return -EAGAIN;
+ }
+ } else {
+ if (down_interruptible(&fi->complete_sem)) {
+ return -ERESTARTSYS;
+ }
+ }
+
+ spin_lock_irq(&fi->reqlists_lock);
+ lh = fi->req_complete.next;
+ list_del(lh);
+ spin_unlock_irq(&fi->reqlists_lock);
+
+ req = list_entry(lh, struct pending_request, list);
+
+ if (req->req.length) {
+ if (copy_to_user(int2ptr(req->req.recvb), req->data,
+ req->req.length)) {
+ req->req.error = RAW1394_ERROR_MEMFAULT;
+ }
+ }
+ if (copy_to_user(buffer, &req->req, sizeof(req->req))) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ ret = (ssize_t) sizeof(struct raw1394_request);
+ out:
+ free_pending_request(req);
+ return ret;
+}
+
+static int state_opened(struct file_info *fi, struct pending_request *req)
+{
+ if (req->req.type == RAW1394_REQ_INITIALIZE) {
+ switch (req->req.misc) {
+ case RAW1394_KERNELAPI_VERSION:
+ case 3:
+ fi->state = initialized;
+ fi->protocol_version = req->req.misc;
+ req->req.error = RAW1394_ERROR_NONE;
+ req->req.generation = atomic_read(&internal_generation);
+ break;
+
+ default:
+ req->req.error = RAW1394_ERROR_COMPAT;
+ req->req.misc = RAW1394_KERNELAPI_VERSION;
+ }
+ } else {
+ req->req.error = RAW1394_ERROR_STATE_ORDER;
+ }
+
+ req->req.length = 0;
+ queue_complete_req(req);
+ return sizeof(struct raw1394_request);
+}
+
+static int state_initialized(struct file_info *fi, struct pending_request *req)
+{
+ struct host_info *hi;
+ struct raw1394_khost_list *khl;
+
+ if (req->req.generation != atomic_read(&internal_generation)) {
+ req->req.error = RAW1394_ERROR_GENERATION;
+ req->req.generation = atomic_read(&internal_generation);
+ req->req.length = 0;
+ queue_complete_req(req);
+ return sizeof(struct raw1394_request);
+ }
+
+ switch (req->req.type) {
+ case RAW1394_REQ_LIST_CARDS:
+ spin_lock_irq(&host_info_lock);
+ khl = kmalloc(sizeof(struct raw1394_khost_list) * host_count,
+ SLAB_ATOMIC);
+
+ if (khl != NULL) {
+ req->req.misc = host_count;
+ req->data = (quadlet_t *) khl;
+
+ list_for_each_entry(hi, &host_info_list, list) {
+ khl->nodes = hi->host->node_count;
+ strcpy(khl->name, hi->host->driver->name);
+ khl++;
+ }
+ }
+ spin_unlock_irq(&host_info_lock);
+
+ if (khl != NULL) {
+ req->req.error = RAW1394_ERROR_NONE;
+ req->req.length = min(req->req.length,
+ (u32) (sizeof
+ (struct raw1394_khost_list)
+ * req->req.misc));
+ req->free_data = 1;
+ } else {
+ return -ENOMEM;
+ }
+ break;
+
+ case RAW1394_REQ_SET_CARD:
+ spin_lock_irq(&host_info_lock);
+ if (req->req.misc < host_count) {
+ list_for_each_entry(hi, &host_info_list, list) {
+ if (!req->req.misc--)
+ break;
+ }
+ get_device(&hi->host->device); // XXX Need to handle failure case
+ list_add_tail(&fi->list, &hi->file_info_list);
+ fi->host = hi->host;
+ fi->state = connected;
+
+ req->req.error = RAW1394_ERROR_NONE;
+ req->req.generation = get_hpsb_generation(fi->host);
+ req->req.misc = (fi->host->node_id << 16)
+ | fi->host->node_count;
+ if (fi->protocol_version > 3) {
+ req->req.misc |=
+ NODEID_TO_NODE(fi->host->irm_id) << 8;
+ }
+ } else {
+ req->req.error = RAW1394_ERROR_INVALID_ARG;
+ }
+ spin_unlock_irq(&host_info_lock);
+
+ req->req.length = 0;
+ break;
+
+ default:
+ req->req.error = RAW1394_ERROR_STATE_ORDER;
+ req->req.length = 0;
+ break;
+ }
+
+ queue_complete_req(req);
+ return sizeof(struct raw1394_request);
+}
+
+static void handle_iso_listen(struct file_info *fi, struct pending_request *req)
+{
+ int channel = req->req.misc;
+
+ spin_lock_irq(&host_info_lock);
+ if ((channel > 63) || (channel < -64)) {
+ req->req.error = RAW1394_ERROR_INVALID_ARG;
+ } else if (channel >= 0) {
+ /* allocate channel req.misc */
+ if (fi->listen_channels & (1ULL << channel)) {
+ req->req.error = RAW1394_ERROR_ALREADY;
+ } else {
+ if (hpsb_listen_channel
+ (&raw1394_highlevel, fi->host, channel)) {
+ req->req.error = RAW1394_ERROR_ALREADY;
+ } else {
+ fi->listen_channels |= 1ULL << channel;
+ fi->iso_buffer = int2ptr(req->req.recvb);
+ fi->iso_buffer_length = req->req.length;
+ }
+ }
+ } else {
+ /* deallocate channel (one's complement neg) req.misc */
+ channel = ~channel;
+
+ if (fi->listen_channels & (1ULL << channel)) {
+ hpsb_unlisten_channel(&raw1394_highlevel, fi->host,
+ channel);
+ fi->listen_channels &= ~(1ULL << channel);
+ } else {
+ req->req.error = RAW1394_ERROR_INVALID_ARG;
+ }
+ }
+
+ req->req.length = 0;
+ queue_complete_req(req);
+ spin_unlock_irq(&host_info_lock);
+}
+
+static void handle_fcp_listen(struct file_info *fi, struct pending_request *req)
+{
+ if (req->req.misc) {
+ if (fi->fcp_buffer) {
+ req->req.error = RAW1394_ERROR_ALREADY;
+ } else {
+ fi->fcp_buffer = int2ptr(req->req.recvb);
+ }
+ } else {
+ if (!fi->fcp_buffer) {
+ req->req.error = RAW1394_ERROR_ALREADY;
+ } else {
+ fi->fcp_buffer = NULL;
+ }
+ }
+
+ req->req.length = 0;
+ queue_complete_req(req);
+}
+
+static int handle_async_request(struct file_info *fi,
+ struct pending_request *req, int node)
+{
+ struct hpsb_packet *packet = NULL;
+ u64 addr = req->req.address & 0xffffffffffffULL;
+
+ switch (req->req.type) {
+ case RAW1394_REQ_ASYNC_READ:
+ DBGMSG("read_request called");
+ packet =
+ hpsb_make_readpacket(fi->host, node, addr, req->req.length);
+
+ if (!packet)
+ return -ENOMEM;
+
+ if (req->req.length == 4)
+ req->data = &packet->header[3];
+ else
+ req->data = packet->data;
+
+ break;
+
+ case RAW1394_REQ_ASYNC_WRITE:
+ DBGMSG("write_request called");
+
+ packet = hpsb_make_writepacket(fi->host, node, addr, NULL,
+ req->req.length);
+ if (!packet)
+ return -ENOMEM;
+
+ if (req->req.length == 4) {
+ if (copy_from_user
+ (&packet->header[3], int2ptr(req->req.sendb),
+ req->req.length))
+ req->req.error = RAW1394_ERROR_MEMFAULT;
+ } else {
+ if (copy_from_user
+ (packet->data, int2ptr(req->req.sendb),
+ req->req.length))
+ req->req.error = RAW1394_ERROR_MEMFAULT;
+ }
+
+ req->req.length = 0;
+ break;
+
+ case RAW1394_REQ_ASYNC_STREAM:
+ DBGMSG("stream_request called");
+
+ packet =
+ hpsb_make_streampacket(fi->host, NULL, req->req.length,
+ node & 0x3f /*channel */ ,
+ (req->req.misc >> 16) & 0x3,
+ req->req.misc & 0xf);
+ if (!packet)
+ return -ENOMEM;
+
+ if (copy_from_user(packet->data, int2ptr(req->req.sendb),
+ req->req.length))
+ req->req.error = RAW1394_ERROR_MEMFAULT;
+
+ req->req.length = 0;
+ break;
+
+ case RAW1394_REQ_LOCK:
+ DBGMSG("lock_request called");
+ if ((req->req.misc == EXTCODE_FETCH_ADD)
+ || (req->req.misc == EXTCODE_LITTLE_ADD)) {
+ if (req->req.length != 4) {
+ req->req.error = RAW1394_ERROR_INVALID_ARG;
+ break;
+ }
+ } else {
+ if (req->req.length != 8) {
+ req->req.error = RAW1394_ERROR_INVALID_ARG;
+ break;
+ }
+ }
+
+ packet = hpsb_make_lockpacket(fi->host, node, addr,
+ req->req.misc, NULL, 0);
+ if (!packet)
+ return -ENOMEM;
+
+ if (copy_from_user(packet->data, int2ptr(req->req.sendb),
+ req->req.length)) {
+ req->req.error = RAW1394_ERROR_MEMFAULT;
+ break;
+ }
+
+ req->data = packet->data;
+ req->req.length = 4;
+ break;
+
+ case RAW1394_REQ_LOCK64:
+ DBGMSG("lock64_request called");
+ if ((req->req.misc == EXTCODE_FETCH_ADD)
+ || (req->req.misc == EXTCODE_LITTLE_ADD)) {
+ if (req->req.length != 8) {
+ req->req.error = RAW1394_ERROR_INVALID_ARG;
+ break;
+ }
+ } else {
+ if (req->req.length != 16) {
+ req->req.error = RAW1394_ERROR_INVALID_ARG;
+ break;
+ }
+ }
+ packet = hpsb_make_lock64packet(fi->host, node, addr,
+ req->req.misc, NULL, 0);
+ if (!packet)
+ return -ENOMEM;
+
+ if (copy_from_user(packet->data, int2ptr(req->req.sendb),
+ req->req.length)) {
+ req->req.error = RAW1394_ERROR_MEMFAULT;
+ break;
+ }
+
+ req->data = packet->data;
+ req->req.length = 8;
+ break;
+
+ default:
+ req->req.error = RAW1394_ERROR_STATE_ORDER;
+ }
+
+ req->packet = packet;
+
+ if (req->req.error) {
+ req->req.length = 0;
+ queue_complete_req(req);
+ return sizeof(struct raw1394_request);
+ }
+
+ hpsb_set_packet_complete_task(packet,
+ (void (*)(void *))queue_complete_cb, req);
+
+ spin_lock_irq(&fi->reqlists_lock);
+ list_add_tail(&req->list, &fi->req_pending);
+ spin_unlock_irq(&fi->reqlists_lock);
+
+ packet->generation = req->req.generation;
+
+ if (hpsb_send_packet(packet) < 0) {
+ req->req.error = RAW1394_ERROR_SEND_ERROR;
+ req->req.length = 0;
+ hpsb_free_tlabel(packet);
+ queue_complete_req(req);
+ }
+ return sizeof(struct raw1394_request);
+}
+
+static int handle_iso_send(struct file_info *fi, struct pending_request *req,
+ int channel)
+{
+ struct hpsb_packet *packet;
+
+ packet = hpsb_make_isopacket(fi->host, req->req.length, channel & 0x3f,
+ (req->req.misc >> 16) & 0x3,
+ req->req.misc & 0xf);
+ if (!packet)
+ return -ENOMEM;
+
+ packet->speed_code = req->req.address & 0x3;
+
+ req->packet = packet;
+
+ if (copy_from_user(packet->data, int2ptr(req->req.sendb),
+ req->req.length)) {
+ req->req.error = RAW1394_ERROR_MEMFAULT;
+ req->req.length = 0;
+ queue_complete_req(req);
+ return sizeof(struct raw1394_request);
+ }
+
+ req->req.length = 0;
+ hpsb_set_packet_complete_task(packet,
+ (void (*)(void *))queue_complete_req,
+ req);
+
+ spin_lock_irq(&fi->reqlists_lock);
+ list_add_tail(&req->list, &fi->req_pending);
+ spin_unlock_irq(&fi->reqlists_lock);
+
+ /* Update the generation of the packet just before sending. */
+ packet->generation = req->req.generation;
+
+ if (hpsb_send_packet(packet) < 0) {
+ req->req.error = RAW1394_ERROR_SEND_ERROR;
+ queue_complete_req(req);
+ }
+
+ return sizeof(struct raw1394_request);
+}
+
+static int handle_async_send(struct file_info *fi, struct pending_request *req)
+{
+ struct hpsb_packet *packet;
+ int header_length = req->req.misc & 0xffff;
+ int expect_response = req->req.misc >> 16;
+
+ if ((header_length > req->req.length) || (header_length < 12)) {
+ req->req.error = RAW1394_ERROR_INVALID_ARG;
+ req->req.length = 0;
+ queue_complete_req(req);
+ return sizeof(struct raw1394_request);
+ }
+
+ packet = hpsb_alloc_packet(req->req.length - header_length);
+ req->packet = packet;
+ if (!packet)
+ return -ENOMEM;
+
+ if (copy_from_user(packet->header, int2ptr(req->req.sendb),
+ header_length)) {
+ req->req.error = RAW1394_ERROR_MEMFAULT;
+ req->req.length = 0;
+ queue_complete_req(req);
+ return sizeof(struct raw1394_request);
+ }
+
+ if (copy_from_user
+ (packet->data, int2ptr(req->req.sendb) + header_length,
+ packet->data_size)) {
+ req->req.error = RAW1394_ERROR_MEMFAULT;
+ req->req.length = 0;
+ queue_complete_req(req);
+ return sizeof(struct raw1394_request);
+ }
+
+ packet->type = hpsb_async;
+ packet->node_id = packet->header[0] >> 16;
+ packet->tcode = (packet->header[0] >> 4) & 0xf;
+ packet->tlabel = (packet->header[0] >> 10) & 0x3f;
+ packet->host = fi->host;
+ packet->expect_response = expect_response;
+ packet->header_size = header_length;
+ packet->data_size = req->req.length - header_length;
+
+ req->req.length = 0;
+ hpsb_set_packet_complete_task(packet,
+ (void (*)(void *))queue_complete_cb, req);
+
+ spin_lock_irq(&fi->reqlists_lock);
+ list_add_tail(&req->list, &fi->req_pending);
+ spin_unlock_irq(&fi->reqlists_lock);
+
+ /* Update the generation of the packet just before sending. */
+ packet->generation = req->req.generation;
+
+ if (hpsb_send_packet(packet) < 0) {
+ req->req.error = RAW1394_ERROR_SEND_ERROR;
+ queue_complete_req(req);
+ }
+
+ return sizeof(struct raw1394_request);
+}
+
+static int arm_read(struct hpsb_host *host, int nodeid, quadlet_t * buffer,
+ u64 addr, size_t length, u16 flags)
+{
+ struct pending_request *req;
+ struct host_info *hi;
+ struct file_info *fi = NULL;
+ struct list_head *entry;
+ struct arm_addr *arm_addr = NULL;
+ struct arm_request *arm_req = NULL;
+ struct arm_response *arm_resp = NULL;
+ int found = 0, size = 0, rcode = -1;
+ struct arm_request_response *arm_req_resp = NULL;
+
+ DBGMSG("arm_read called by node: %X"
+ "addr: %4.4x %8.8x length: %Zu", nodeid,
+ (u16) ((addr >> 32) & 0xFFFF), (u32) (addr & 0xFFFFFFFF),
+ length);
+ spin_lock(&host_info_lock);
+ hi = find_host_info(host); /* search address-entry */
+ if (hi != NULL) {
+ list_for_each_entry(fi, &hi->file_info_list, list) {
+ entry = fi->addr_list.next;
+ while (entry != &(fi->addr_list)) {
+ arm_addr =
+ list_entry(entry, struct arm_addr,
+ addr_list);
+ if (((arm_addr->start) <= (addr))
+ && ((arm_addr->end) >= (addr + length))) {
+ found = 1;
+ break;
+ }
+ entry = entry->next;
+ }
+ if (found) {
+ break;
+ }
+ }
+ }
+ rcode = -1;
+ if (!found) {
+ printk(KERN_ERR "raw1394: arm_read FAILED addr_entry not found"
+ " -> rcode_address_error\n");
+ spin_unlock(&host_info_lock);
+ return (RCODE_ADDRESS_ERROR);
+ } else {
+ DBGMSG("arm_read addr_entry FOUND");
+ }
+ if (arm_addr->rec_length < length) {
+ DBGMSG("arm_read blocklength too big -> rcode_data_error");
+ rcode = RCODE_DATA_ERROR; /* hardware error, data is unavailable */
+ }
+ if (rcode == -1) {
+ if (arm_addr->access_rights & ARM_READ) {
+ if (!(arm_addr->client_transactions & ARM_READ)) {
+ memcpy(buffer,
+ (arm_addr->addr_space_buffer) + (addr -
+ (arm_addr->
+ start)),
+ length);
+ DBGMSG("arm_read -> (rcode_complete)");
+ rcode = RCODE_COMPLETE;
+ }
+ } else {
+ rcode = RCODE_TYPE_ERROR; /* function not allowed */
+ DBGMSG("arm_read -> rcode_type_error (access denied)");
+ }
+ }
+ if (arm_addr->notification_options & ARM_READ) {
+ DBGMSG("arm_read -> entering notification-section");
+ req = __alloc_pending_request(SLAB_ATOMIC);
+ if (!req) {
+ DBGMSG("arm_read -> rcode_conflict_error");
+ spin_unlock(&host_info_lock);
+ return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected.
+ The request may be retried */
+ }
+ if (rcode == RCODE_COMPLETE) {
+ size =
+ sizeof(struct arm_request) +
+ sizeof(struct arm_response) +
+ length * sizeof(byte_t) +
+ sizeof(struct arm_request_response);
+ } else {
+ size =
+ sizeof(struct arm_request) +
+ sizeof(struct arm_response) +
+ sizeof(struct arm_request_response);
+ }
+ req->data = kmalloc(size, SLAB_ATOMIC);
+ if (!(req->data)) {
+ free_pending_request(req);
+ DBGMSG("arm_read -> rcode_conflict_error");
+ spin_unlock(&host_info_lock);
+ return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected.
+ The request may be retried */
+ }
+ req->free_data = 1;
+ req->file_info = fi;
+ req->req.type = RAW1394_REQ_ARM;
+ req->req.generation = get_hpsb_generation(host);
+ req->req.misc =
+ (((length << 16) & (0xFFFF0000)) | (ARM_READ & 0xFF));
+ req->req.tag = arm_addr->arm_tag;
+ req->req.recvb = arm_addr->recvb;
+ req->req.length = size;
+ arm_req_resp = (struct arm_request_response *)(req->data);
+ arm_req = (struct arm_request *)((byte_t *) (req->data) +
+ (sizeof
+ (struct
+ arm_request_response)));
+ arm_resp =
+ (struct arm_response *)((byte_t *) (arm_req) +
+ (sizeof(struct arm_request)));
+ arm_req->buffer = NULL;
+ arm_resp->buffer = NULL;
+ if (rcode == RCODE_COMPLETE) {
+ byte_t *buf =
+ (byte_t *) arm_resp + sizeof(struct arm_response);
+ memcpy(buf,
+ (arm_addr->addr_space_buffer) + (addr -
+ (arm_addr->
+ start)),
+ length);
+ arm_resp->buffer =
+ int2ptr((arm_addr->recvb) +
+ sizeof(struct arm_request_response) +
+ sizeof(struct arm_request) +
+ sizeof(struct arm_response));
+ }
+ arm_resp->buffer_length =
+ (rcode == RCODE_COMPLETE) ? length : 0;
+ arm_resp->response_code = rcode;
+ arm_req->buffer_length = 0;
+ arm_req->generation = req->req.generation;
+ arm_req->extended_transaction_code = 0;
+ arm_req->destination_offset = addr;
+ arm_req->source_nodeid = nodeid;
+ arm_req->destination_nodeid = host->node_id;
+ arm_req->tlabel = (flags >> 10) & 0x3f;
+ arm_req->tcode = (flags >> 4) & 0x0f;
+ arm_req_resp->request = int2ptr((arm_addr->recvb) +
+ sizeof(struct
+ arm_request_response));
+ arm_req_resp->response =
+ int2ptr((arm_addr->recvb) +
+ sizeof(struct arm_request_response) +
+ sizeof(struct arm_request));
+ queue_complete_req(req);
+ }
+ spin_unlock(&host_info_lock);
+ return (rcode);
+}
+
+static int arm_write(struct hpsb_host *host, int nodeid, int destid,
+ quadlet_t * data, u64 addr, size_t length, u16 flags)
+{
+ struct pending_request *req;
+ struct host_info *hi;
+ struct file_info *fi = NULL;
+ struct list_head *entry;
+ struct arm_addr *arm_addr = NULL;
+ struct arm_request *arm_req = NULL;
+ struct arm_response *arm_resp = NULL;
+ int found = 0, size = 0, rcode = -1, length_conflict = 0;
+ struct arm_request_response *arm_req_resp = NULL;
+
+ DBGMSG("arm_write called by node: %X"
+ "addr: %4.4x %8.8x length: %Zu", nodeid,
+ (u16) ((addr >> 32) & 0xFFFF), (u32) (addr & 0xFFFFFFFF),
+ length);
+ spin_lock(&host_info_lock);
+ hi = find_host_info(host); /* search address-entry */
+ if (hi != NULL) {
+ list_for_each_entry(fi, &hi->file_info_list, list) {
+ entry = fi->addr_list.next;
+ while (entry != &(fi->addr_list)) {
+ arm_addr =
+ list_entry(entry, struct arm_addr,
+ addr_list);
+ if (((arm_addr->start) <= (addr))
+ && ((arm_addr->end) >= (addr + length))) {
+ found = 1;
+ break;
+ }
+ entry = entry->next;
+ }
+ if (found) {
+ break;
+ }
+ }
+ }
+ rcode = -1;
+ if (!found) {
+ printk(KERN_ERR "raw1394: arm_write FAILED addr_entry not found"
+ " -> rcode_address_error\n");
+ spin_unlock(&host_info_lock);
+ return (RCODE_ADDRESS_ERROR);
+ } else {
+ DBGMSG("arm_write addr_entry FOUND");
+ }
+ if (arm_addr->rec_length < length) {
+ DBGMSG("arm_write blocklength too big -> rcode_data_error");
+ length_conflict = 1;
+ rcode = RCODE_DATA_ERROR; /* hardware error, data is unavailable */
+ }
+ if (rcode == -1) {
+ if (arm_addr->access_rights & ARM_WRITE) {
+ if (!(arm_addr->client_transactions & ARM_WRITE)) {
+ memcpy((arm_addr->addr_space_buffer) +
+ (addr - (arm_addr->start)), data,
+ length);
+ DBGMSG("arm_write -> (rcode_complete)");
+ rcode = RCODE_COMPLETE;
+ }
+ } else {
+ rcode = RCODE_TYPE_ERROR; /* function not allowed */
+ DBGMSG("arm_write -> rcode_type_error (access denied)");
+ }
+ }
+ if (arm_addr->notification_options & ARM_WRITE) {
+ DBGMSG("arm_write -> entering notification-section");
+ req = __alloc_pending_request(SLAB_ATOMIC);
+ if (!req) {
+ DBGMSG("arm_write -> rcode_conflict_error");
+ spin_unlock(&host_info_lock);
+ return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected.
+ The request my be retried */
+ }
+ size =
+ sizeof(struct arm_request) + sizeof(struct arm_response) +
+ (length) * sizeof(byte_t) +
+ sizeof(struct arm_request_response);
+ req->data = kmalloc(size, SLAB_ATOMIC);
+ if (!(req->data)) {
+ free_pending_request(req);
+ DBGMSG("arm_write -> rcode_conflict_error");
+ spin_unlock(&host_info_lock);
+ return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected.
+ The request may be retried */
+ }
+ req->free_data = 1;
+ req->file_info = fi;
+ req->req.type = RAW1394_REQ_ARM;
+ req->req.generation = get_hpsb_generation(host);
+ req->req.misc =
+ (((length << 16) & (0xFFFF0000)) | (ARM_WRITE & 0xFF));
+ req->req.tag = arm_addr->arm_tag;
+ req->req.recvb = arm_addr->recvb;
+ req->req.length = size;
+ arm_req_resp = (struct arm_request_response *)(req->data);
+ arm_req = (struct arm_request *)((byte_t *) (req->data) +
+ (sizeof
+ (struct
+ arm_request_response)));
+ arm_resp =
+ (struct arm_response *)((byte_t *) (arm_req) +
+ (sizeof(struct arm_r