diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /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.c | 2958 |
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 |