/* * IEEE 1394 for Linux * * Raw interface to the bus * * Copyright (C) 1999, 2000 Andreas E. Bombe * 2001, 2002 Manfred Weihs * 2002 Christian Toegel * * This code is licensed under the GPL. See the file COPYING in the root * directory of the kernel sources for details. * * * Contributions: * * Manfred Weihs * configuration ROM manipulation * address range mapping * adaptation for new (transparent) loopback mechanism * sending of arbitrary async packets * Christian Toegel * address range mapping * lock64 request * transmit physical packet * busreset notification control (switch on/off) * busreset with selection of type (short/long) * request_reply */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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" #if BITS_PER_LONG == 64 #define int2ptr(x) ((void *)x) #define ptr2int(x) ((u64)x) #else #define int2ptr(x) ((void *)(u32)x) #define ptr2int(x) ((u64)(u32)x) #endif #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 devfs_handle_t devfs_handle; static LIST_HEAD(host_info_list); static int host_count; static spinlock_t host_info_lock = SPIN_LOCK_UNLOCKED; 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); } free_hpsb_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 list_head *lh; struct host_info *hi; list_for_each(lh, &host_info_list) { hi = list_entry(lh, struct host_info, 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 list_head *lh; 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(lh, &hi->file_info_list) { fi = list_entry(lh, struct file_info, 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 list_head *lh; struct host_info *hi; struct file_info *fi; struct pending_request *req; 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(lh, &hi->file_info_list) { fi = list_entry(lh, struct file_info, 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); lh = reqs.next; while (lh != &reqs) { req = list_entry(lh, struct pending_request, list); lh = lh->next; 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 list_head *lh; struct host_info *hi; struct file_info *fi; struct pending_request *req; 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(lh, &hi->file_info_list) { fi = list_entry(lh, struct file_info, 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); lh = reqs.next; while (lh != &reqs) { req = list_entry(lh, struct pending_request, list); lh = lh->next; queue_complete_req(req); } } static ssize_t raw1394_read(struct file *file, char *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; 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; } } __copy_to_user(buffer, &req->req, sizeof(req->req)); free_pending_request(req); return sizeof(struct raw1394_request); } 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 list_head *lh; 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(lh, &host_info_list) { hi = list_entry(lh, struct host_info, 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: lh = NULL; spin_lock_irq(&host_info_lock); if (req->req.misc < host_count) { lh = host_info_list.next; while (req->req.misc--) { lh = lh->next; } hi = list_entry(lh, struct host_info, list); hpsb_ref_host(hi->host); // XXX Need to handle failure case list_add_tail(&fi->list, &hi->file_info_list); fi->host = hi->host; fi->state = connected; } spin_unlock_irq(&host_info_lock); if (lh != NULL) { 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; } 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 = (u8 *)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)) { 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)) { 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 = alloc_hpsb_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, ((u8*) 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)) { 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 list_head *lh; 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(lh, &hi->file_info_list) { fi = list_entry(lh, struct file_info, 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) { arm_resp->buffer = ((byte_t *)(arm_resp) + (sizeof(struct arm_response))); memcpy (arm_resp->buffer, (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 list_head *lh; 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(lh, &hi->file_info_list) { fi = list_entry(lh, struct file_info, 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_request))); arm_req->buffer = ((byte_t *)(arm_resp) + (sizeof(struct arm_response))); arm_resp->buffer = NULL; memcpy (arm_req->buffer, data, length); arm_req->buffer = int2ptr((arm_addr->recvb) + sizeof (struct arm_request_response) + sizeof (struct arm_request) + sizeof (struct arm_response)); arm_req->buffer_length = length; 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 = destid; arm_req->tlabel = (flags >> 10) & 0x3f; arm_req->tcode = (flags >> 4) & 0x0f; arm_resp->buffer_length = 0; arm_resp->response_code = rcode; 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_lock (struct hpsb_host *host, int nodeid, quadlet_t *store, u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, u16 flags) { struct pending_request *req; struct list_head *lh; 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; quadlet_t old, new; struct arm_request_response *arm_req_resp = NULL; if (((ext_tcode & 0xFF) == EXTCODE_FETCH_ADD) || ((ext_tcode & 0xFF) == EXTCODE_LITTLE_ADD)) { DBGMSG("arm_lock called by node: %X " "addr: %4.4x %8.8x extcode: %2.2X data: %8.8X", nodeid, (u16) ((addr >>32) & 0xFFFF), (u32) (addr & 0xFFFFFFFF), ext_tcode & 0xFF , be32_to_cpu(data)); } else { DBGMSG("arm_lock called by node: %X " "addr: %4.4x %8.8x extcode: %2.2X data: %8.8X arg: %8.8X", nodeid, (u16) ((addr >>32) & 0xFFFF), (u32) (addr & 0xFFFFFFFF), ext_tcode & 0xFF , be32_to_cpu(data), be32_to_cpu(arg)); } spin_lock(&host_info_lock); hi = find_host_info(host); /* search address-entry */ if (hi != NULL) { list_for_each(lh, &hi->file_info_list) { fi = list_entry(lh, struct file_info, 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+sizeof(*store)))) { found = 1; break; } entry = entry->next; } if (found) { break; } } } rcode = -1; if (!found) { printk(KERN_ERR "raw1394: arm_lock FAILED addr_entry not found" " -> rcode_address_error\n"); spin_unlock(&host_info_lock); return (RCODE_ADDRESS_ERROR); } else { DBGMSG("arm_lock addr_entry FOUND"); } if (rcode == -1) { if (arm_addr->access_rights & ARM_LOCK) { if (!(arm_addr->client_transactions & ARM_LOCK)) { memcpy(&old,(arm_addr->addr_space_buffer)+(addr-(arm_addr->start)), sizeof(old)); switch (ext_tcode) { case (EXTCODE_MASK_SWAP): new = data | (old & ~arg); break; case (EXTCODE_COMPARE_SWAP): if (old == arg) { new = data; } else { new = old; } break; case (EXTCODE_FETCH_ADD): new = cpu_to_be32(be32_to_cpu(data) + be32_to_cpu(old)); break; case (EXTCODE_LITTLE_ADD): new = cpu_to_le32(le32_to_cpu(data) + le32_to_cpu(old)); break; case (EXTCODE_BOUNDED_ADD): if (old != arg) { new = cpu_to_be32(be32_to_cpu(data) + be32_to_cpu(old)); } else { new = old; } break; case (EXTCODE_WRAP_ADD): if (old != arg) { new = cpu_to_be32(be32_to_cpu(data) + be32_to_cpu(old)); } else { new = data; } break; default: rcode = RCODE_TYPE_ERROR; /* function not allowed */ printk(KERN_ERR "raw1394: arm_lock FAILED " "ext_tcode not allowed -> rcode_type_error\n"); break; } /*switch*/ if (rcode == -1) { DBGMSG("arm_lock -> (rcode_complete)"); rcode = RCODE_COMPLETE; memcpy (store, &old, sizeof(*store)); memcpy ((arm_addr->addr_space_buffer)+ (addr-(arm_addr->start)), &new, sizeof(*store)); } } } else { rcode = RCODE_TYPE_ERROR; /* function not allowed */ DBGMSG("arm_lock -> rcode_type_error (access denied)"); } } if (arm_addr->notification_options & ARM_LOCK) { DBGMSG("arm_lock -> entering notification-section"); req = __alloc_pending_request(SLAB_ATOMIC); if (!req) { DBGMSG("arm_lock -> rcode_conflict_error"); spin_unlock(&host_info_lock); return(RCODE_CONFLICT_ERROR); /* A resource conflict was detected. The request may be retried */ } size = sizeof(struct arm_request)+sizeof(struct arm_response) + 3 * sizeof(*store) + sizeof (struct arm_request_response); /* maximum */ req->data = kmalloc(size, SLAB_ATOMIC); if (!(req->data)) { free_pending_request(req); DBGMSG("arm_lock -> 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; 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 = ((byte_t *)(arm_resp) + (sizeof(struct arm_response))); arm_resp->buffer = ((byte_t *)(arm_req->buffer) + (2* sizeof(*store))); if ((ext_tcode == EXTCODE_FETCH_ADD) || (ext_tcode == EXTCODE_LITTLE_ADD)) { arm_req->buffer_length = sizeof(*store); memcpy (arm_req->buffer, &data, sizeof(*store)); } else { arm_req->buffer_length = 2 * sizeof(*store); memcpy (arm_req->buffer, &arg, sizeof(*store)); memcpy (((arm_req->buffer) + sizeof(*store)), &data, sizeof(*store)); } if (rcode == RCODE_COMPLETE) { arm_resp->buffer_length = sizeof(*store); memcpy (arm_resp->buffer, &old, sizeof(*store)); } else { arm_resp->buffer = NULL; arm_resp->buffer_length = 0; } req->file_info = fi; req->req.type = RAW1394_REQ_ARM; req->req.generation = get_hpsb_generation(host); req->req.misc = ( (((sizeof(*store)) << 16) & (0xFFFF0000)) | (ARM_LOCK & 0xFF)); req->req.tag = arm_addr->arm_tag; req->req.recvb = arm_addr->recvb; req->req.length = size; arm_req->generation = req->req.generation; arm_req->extended_transaction_code = ext_tcode; 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_resp->response_code = rcode; 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)); arm_req->buffer = int2ptr((arm_addr->recvb) + sizeof (struct arm_request_response) + sizeof (struct arm_request) + sizeof (struct arm_response)); arm_resp->buffer = int2ptr((arm_addr->recvb) + sizeof (struct arm_request_response) + sizeof (struct arm_request) + sizeof (struct arm_response) + 2* sizeof (*store)); queue_complete_req(req); } spin_unlock(&host_info_lock); return(rcode); } 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) { struct pending_request *req; struct list_head *lh; 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; octlet_t old, new; struct arm_request_response *arm_req_resp = NULL; if (((ext_tcode & 0xFF) == EXTCODE_FETCH_ADD) || ((ext_tcode & 0xFF) == EXTCODE_LITTLE_ADD)) { DBGMSG("arm_lock64 called by node: %X " "addr: %4.4x %8.8x extcode: %2.2X data: %8.8X %8.8X ", nodeid, (u16) ((addr >>32) & 0xFFFF), (u32) (addr & 0xFFFFFFFF), ext_tcode & 0xFF , (u32) ((be64_to_cpu(data) >> 32) & 0xFFFFFFFF), (u32) (be64_to_cpu(data) & 0xFFFFFFFF)); } else { DBGMSG("arm_lock64 called by node: %X " "addr: %4.4x %8.8x extcode: %2.2X data: %8.8X %8.8X arg: " "%8.8X %8.8X ", nodeid, (u16) ((addr >>32) & 0xFFFF), (u32) (addr & 0xFFFFFFFF), ext_tcode & 0xFF , (u32) ((be64_to_cpu(data) >> 32) & 0xFFFFFFFF), (u32) (be64_to_cpu(data) & 0xFFFFFFFF), (u32) ((be64_to_cpu(arg) >> 32) & 0xFFFFFFFF), (u32) (be64_to_cpu(arg) & 0xFFFFFFFF)); } spin_lock(&host_info_lock); hi = find_host_info(host); /* search addressentry in file_info's for host */ if (hi != NULL) { list_for_each(lh, &hi->file_info_list) { fi = list_entry(lh, struct file_info, 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+sizeof(*store)))) { found = 1; break; } entry = entry->next; } if (found) { break; } } } rcode = -1; if (!found) { printk(KERN_ERR "raw1394: arm_lock64 FAILED addr_entry not found" " -> rcode_address_error\n"); spin_unlock(&host_info_lock); return (RCODE_ADDRESS_ERROR); } else { DBGMSG("arm_lock64 addr_entry FOUND"); } if (rcode == -1) { if (arm_addr->access_rights & ARM_LOCK) { if (!(arm_addr->client_transactions & ARM_LOCK)) { memcpy(&old,(arm_addr->addr_space_buffer)+(addr-(arm_addr->start)), sizeof(old)); switch (ext_tcode) { case (EXTCODE_MASK_SWAP): new = data | (old & ~arg); break; case (EXTCODE_COMPARE_SWAP): if (old == arg) { new = data; } else { new = old; } break; case (EXTCODE_FETCH_ADD): new = cpu_to_be64(be64_to_cpu(data) + be64_to_cpu(old)); break; case (EXTCODE_LITTLE_ADD): new = cpu_to_le64(le64_to_cpu(data) + le64_to_cpu(old)); break; case (EXTCODE_BOUNDED_ADD): if (old != arg) { new = cpu_to_be64(be64_to_cpu(data) + be64_to_cpu(old)); } else { new = old; } break; case (EXTCODE_WRAP_ADD): if (old != arg) { new = cpu_to_be64(be64_to_cpu(data) + be64_to_cpu(old)); } else { new = data; } break; default: printk(KERN_ERR "raw1394: arm_lock64 FAILED " "ext_tcode not allowed -> rcode_type_error\n"); rcode = RCODE_TYPE_ERROR; /* function not allowed */ break; } /*switch*/ if (rcode == -1) { DBGMSG("arm_lock64 -> (rcode_complete)"); rcode = RCODE_COMPLETE; memcpy (store, &old, sizeof(*store)); memcpy ((arm_addr->addr_space_buffer)+ (addr-(arm_addr->start)), &new, sizeof(*store)); } } } else { rcode = RCODE_TYPE_ERROR; /* function not allowed */ DBGMSG("arm_lock64 -> rcode_type_error (access denied)"); } } if (arm_addr->notification_options & ARM_LOCK) { DBGMSG("arm_lock64 -> entering notification-section"); req = __alloc_pending_request(SLAB_ATOMIC); if (!req) { spin_unlock(&host_info_lock); DBGMSG("arm_lock64 -> rcode_conflict_error"); return(RCODE_CONFLICT_ERROR); /* A resource conflict was detected. The request may be retried */ } size = sizeof(struct arm_request)+sizeof(struct arm_response) + 3 * sizeof(*store) + sizeof (struct arm_request_response); /* maximum */ req->data = kmalloc(size, SLAB_ATOMIC); if (!(req->data)) { free_pending_request(req); spin_unlock(&host_info_lock); DBGMSG("arm_lock64 -> rcode_conflict_error"); return(RCODE_CONFLICT_ERROR); /* A resource conflict was detected. The request may be retried */ } req->free_data=1; 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 = ((byte_t *)(arm_resp) + (sizeof(struct arm_response))); arm_resp->buffer = ((byte_t *)(arm_req->buffer) + (2* sizeof(*store))); if ((ext_tcode == EXTCODE_FETCH_ADD) || (ext_tcode == EXTCODE_LITTLE_ADD)) { arm_req->buffer_length = sizeof(*store); memcpy (arm_req->buffer, &data, sizeof(*store)); } else { arm_req->buffer_length = 2 * sizeof(*store); memcpy (arm_req->buffer, &arg, sizeof(*store)); memcpy (((arm_req->buffer) + sizeof(*store)), &data, sizeof(*store)); } if (rcode == RCODE_COMPLETE) { arm_resp->buffer_length = sizeof(*store); memcpy (arm_resp->buffer, &old, sizeof(*store)); } else { arm_resp->buffer = NULL; arm_resp->buffer_length = 0; } req->file_info = fi; req->req.type = RAW1394_REQ_ARM; req->req.generation = get_hpsb_generation(host); req->req.misc = ( (((sizeof(*store)) << 16) & (0xFFFF0000)) | (ARM_LOCK & 0xFF)); req->req.tag = arm_addr->arm_tag; req->req.recvb = arm_addr->recvb; req->req.length = size; arm_req->generation = req->req.generation; arm_req->extended_transaction_code = ext_tcode; 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_resp->response_code = rcode; 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)); arm_req->buffer = int2ptr((arm_addr->recvb) + sizeof (struct arm_request_response) + sizeof (struct arm_request) + sizeof (struct arm_response)); arm_resp->buffer = int2ptr((arm_addr->recvb) + sizeof (struct arm_request_response) + sizeof (struct arm_request) + sizeof (struct arm_response) + 2* sizeof (*store)); queue_complete_req(req); } spin_unlock(&host_info_lock); return(rcode); } static int arm_register(struct file_info *fi, struct pending_request *req) { int retval; struct arm_addr *addr; struct list_head *lh, *lh_1, *lh_2; struct host_info *hi; struct file_info *fi_hlp = NULL; struct list_head *entry; struct arm_addr *arm_addr = NULL; int same_host, another_host; unsigned long flags; DBGMSG("arm_register called " "addr(Offset): %8.8x %8.8x length: %u " "rights: %2.2X notify: %2.2X " "max_blk_len: %4.4X", (u32) ((req->req.address >>32) & 0xFFFF), (u32) (req->req.address & 0xFFFFFFFF), req->req.length, ((req->req.misc >> 8) & 0xFF), (req->req.misc & 0xFF),((req->req.misc >> 16) & 0xFFFF)); /* check addressrange */ if ((((req->req.address) & ~(0xFFFFFFFFFFFFULL)) != 0) || (((req->req.address + req->req.length) & ~(0xFFFFFFFFFFFFULL)) != 0)) { req->req.length = 0; return (-EINVAL); } /* addr-list-entry for fileinfo */ addr = (struct arm_addr *)kmalloc(sizeof(struct arm_addr), SLAB_KERNEL); if (!addr) { req->req.length = 0; return (-ENOMEM); } /* allocation of addr_space_buffer */ addr->addr_space_buffer = (u8 *)vmalloc(req->req.length); if (!(addr->addr_space_buffer)) { kfree(addr); req->req.length = 0; return (-ENOMEM); } /* initialization of addr_space_buffer */ if ((req->req.sendb)== (unsigned long)NULL) { /* init: set 0 */ memset(addr->addr_space_buffer, 0,req->req.length); } else { /* init: user -> kernel */ if (copy_from_user(addr->addr_space_buffer,int2ptr(req->req.sendb), req->req.length)) { vfree(addr->addr_space_buffer); kfree(addr); return (-EFAULT); } } INIT_LIST_HEAD(&addr->addr_list); addr->arm_tag = req->req.tag; addr->start = req->req.address; addr->end = req->req.address + req->req.length; addr->access_rights = (u8) (req->req.misc & 0x0F); addr->notification_options = (u8) ((req->req.misc >> 4) & 0x0F); addr->client_transactions = (u8) ((req->req.misc >> 8) & 0x0F); addr->access_rights |= addr->client_transactions; addr->notification_options |= addr->client_transactions; addr->recvb = req->req.recvb; addr->rec_length = (u16) ((req->req.misc >> 16) & 0xFFFF); spin_lock_irqsave(&host_info_lock, flags); hi = find_host_info(fi->host); same_host = 0; another_host = 0; /* same host with address-entry containing same addressrange ? */ list_for_each(lh, &hi->file_info_list) { fi_hlp = list_entry(lh, struct file_info, list); entry = fi_hlp->addr_list.next; while (entry != &(fi_hlp->addr_list)) { arm_addr = list_entry(entry, struct arm_addr, addr_list); if ( (arm_addr->start == addr->start) && (arm_addr->end == addr->end)) { DBGMSG("same host ownes same " "addressrange -> EALREADY"); same_host = 1; break; } entry = entry->next; } if (same_host) { break; } } if (same_host) { /* addressrange occupied by same host */ vfree(addr->addr_space_buffer); kfree(addr); spin_unlock_irqrestore(&host_info_lock, flags); return (-EALREADY); } /* another host with valid address-entry containing same addressrange */ list_for_each(lh_1, &host_info_list) { hi = list_entry(lh_1, struct host_info, list); if (hi->host != fi->host) { list_for_each(lh_2, &hi->file_info_list) { fi_hlp = list_entry(lh_2, struct file_info, list); entry = fi_hlp->addr_list.next; while (entry != &(fi_hlp->addr_list)) { arm_addr = list_entry(entry, struct arm_addr, addr_list); if ( (arm_addr->start == addr->start) && (arm_addr->end == addr->end)) { DBGMSG("another host ownes same " "addressrange"); another_host = 1; break; } entry = entry->next; } if (another_host) { break; } } } } if (another_host) { DBGMSG("another hosts entry is valid -> SUCCESS"); if (copy_to_user(int2ptr(req->req.recvb), int2ptr(&addr->start),sizeof(u64))) { printk(KERN_ERR "raw1394: arm_register failed " " address-range-entry is invalid -> EFAULT !!!\n"); vfree(addr->addr_space_buffer); kfree(addr); spin_unlock_irqrestore(&host_info_lock, flags); return (-EFAULT); } free_pending_request(req); /* immediate success or fail */ /* INSERT ENTRY */ list_add_tail(&addr->addr_list, &fi->addr_list); spin_unlock_irqrestore(&host_info_lock, flags); return sizeof(struct raw1394_request); } retval = hpsb_register_addrspace(&raw1394_highlevel, &arm_ops, req->req.address, req->req.address + req->req.length); if (retval) { /* INSERT ENTRY */ list_add_tail(&addr->addr_list, &fi->addr_list); } else { DBGMSG("arm_register failed errno: %d \n",retval); vfree(addr->addr_space_buffer); kfree(addr); spin_unlock_irqrestore(&host_info_lock, flags); return (-EALREADY); } spin_unlock_irqrestore(&host_info_lock, flags); free_pending_request(req); /* immediate success or fail */ return sizeof(struct raw1394_request); } static int arm_unregister(struct file_info *fi, struct pending_request *req) { int found = 0; int retval = 0; struct list_head *entry; struct arm_addr *addr = NULL; struct list_head *lh_1, *lh_2; struct host_info *hi; struct file_info *fi_hlp = NULL; struct arm_addr *arm_addr = NULL; int another_host; unsigned long flags; DBGMSG("arm_Unregister called addr(Offset): " "%8.8x %8.8x", (u32) ((req->req.address >>32) & 0xFFFF), (u32) (req->req.address & 0xFFFFFFFF)); spin_lock_irqsave(&host_info_lock, flags); /* get addr */ entry = fi->addr_list.next; while (entry != &(fi->addr_list)) { addr = list_entry(entry, struct arm_addr, addr_list); if (addr->start == req->req.address) { found = 1; break; } entry = entry->next; } if (!found) { DBGMSG("arm_Unregister addr not found"); spin_unlock_irqrestore(&host_info_lock, flags); return (-EINVAL); } DBGMSG("arm_Unregister addr found"); another_host = 0; /* another host with valid address-entry containing same addressrange */ list_for_each(lh_1, &host_info_list) { hi = list_entry(lh_1, struct host_info, list); if (hi->host != fi->host) { list_for_each(lh_2, &hi->file_info_list) { fi_hlp = list_entry(lh_2, struct file_info, list); entry = fi_hlp->addr_list.next; while (entry != &(fi_hlp->addr_list)) { arm_addr = list_entry(entry, struct arm_addr, addr_list); if (arm_addr->start == addr->start) { DBGMSG("another host ownes " "same addressrange"); another_host = 1; break; } entry = entry->next; } if (another_host) { break; } } } } if (another_host) { DBGMSG("delete entry from list -> success"); list_del(&addr->addr_list); vfree(addr->addr_space_buffer); kfree(addr); free_pending_request(req); /* immediate success or fail */ spin_unlock_irqrestore(&host_info_lock, flags); return sizeof(struct raw1394_request); } retval = hpsb_unregister_addrspace(&raw1394_highlevel, addr->start); if (!retval) { printk(KERN_ERR "raw1394: arm_Unregister failed -> EINVAL\n"); spin_unlock_irqrestore(&host_info_lock, flags); return (-EINVAL); } DBGMSG("delete entry from list -> success"); list_del(&addr->addr_list); spin_unlock_irqrestore(&host_info_lock, flags); vfree(addr->addr_space_buffer); kfree(addr); free_pending_request(req); /* immediate success or fail */ return sizeof(struct raw1394_request); } static int reset_notification(struct file_info *fi, struct pending_request *req) { DBGMSG("reset_notification called - switch %s ", (req->req.misc == RAW1394_NOTIFY_OFF)?"OFF":"ON"); if ((req->req.misc == RAW1394_NOTIFY_OFF) || (req->req.misc == RAW1394_NOTIFY_ON)) { fi->notification=(u8)req->req.misc; free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */ return sizeof(struct raw1394_request); } /* error EINVAL (22) invalid argument */ return (-EINVAL); } static int write_phypacket(struct file_info *fi, struct pending_request *req) { struct hpsb_packet *packet = NULL; int retval=0; quadlet_t data; data = be32_to_cpu((u32)req->req.sendb); DBGMSG("write_phypacket called - quadlet 0x%8.8x ",data); packet = hpsb_make_phypacket (fi->host, data); if (!packet) return -ENOMEM; req->req.length=0; req->packet=packet; 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; retval = hpsb_send_packet(packet); DBGMSG("write_phypacket send_packet called => retval: %d ", retval); if (! retval) { req->req.error = RAW1394_ERROR_SEND_ERROR; req->req.length = 0; queue_complete_req(req); } return sizeof(struct raw1394_request); } static int get_config_rom(struct file_info *fi, struct pending_request *req) { size_t return_size; unsigned char rom_version; int ret=sizeof(struct raw1394_request); quadlet_t *data = kmalloc(req->req.length, SLAB_KERNEL); int status; if (!data) return -ENOMEM; status = hpsb_get_config_rom(fi->host, data, req->req.length, &return_size, &rom_version); if (copy_to_user(int2ptr(req->req.recvb), data, req->req.length)) ret = -EFAULT; if (copy_to_user(int2ptr(req->req.tag), &return_size, sizeof(return_size))) ret = -EFAULT; if (copy_to_user(int2ptr(req->req.address), &rom_version, sizeof(rom_version))) ret = -EFAULT; if (copy_to_user(int2ptr(req->req.sendb), &status, sizeof(status))) ret = -EFAULT; kfree(data); if (ret >= 0) { free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */ } return ret; } static int update_config_rom(struct file_info *fi, struct pending_request *req) { int ret=sizeof(struct raw1394_request); quadlet_t *data = kmalloc(req->req.length, SLAB_KERNEL); if (!data) return -ENOMEM; if (copy_from_user(data,int2ptr(req->req.sendb), req->req.length)) { ret= -EFAULT; } else { int status = hpsb_update_config_rom(fi->host, data, req->req.length, (unsigned char) req->req.misc); if (copy_to_user(int2ptr(req->req.recvb), &status, sizeof(status))) ret = -ENOMEM; } kfree(data); if (ret >= 0) { free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */ } return ret; } static int state_connected(struct file_info *fi, struct pending_request *req) { int node = req->req.address >> 48; req->req.error = RAW1394_ERROR_NONE; switch (req->req.type) { case RAW1394_REQ_ECHO: queue_complete_req(req); return sizeof(struct raw1394_request); case RAW1394_REQ_ISO_SEND: return handle_iso_send(fi, req, node); case RAW1394_REQ_ARM_REGISTER: return arm_register(fi, req); case RAW1394_REQ_ARM_UNREGISTER: return arm_unregister(fi, req); case RAW1394_REQ_RESET_NOTIFY: return reset_notification(fi, req); case RAW1394_REQ_ISO_LISTEN: handle_iso_listen(fi, req); return sizeof(struct raw1394_request); case RAW1394_REQ_FCP_LISTEN: handle_fcp_listen(fi, req); return sizeof(struct raw1394_request); case RAW1394_REQ_RESET_BUS: if (req->req.misc == RAW1394_LONG_RESET) { DBGMSG("busreset called (type: LONG)"); hpsb_reset_bus(fi->host, LONG_RESET); free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */ return sizeof(struct raw1394_request); } if (req->req.misc == RAW1394_SHORT_RESET) { DBGMSG("busreset called (type: SHORT)"); hpsb_reset_bus(fi->host, SHORT_RESET); free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */ return sizeof(struct raw1394_request); } /* error EINVAL (22) invalid argument */ return (-EINVAL); case RAW1394_REQ_GET_ROM: return get_config_rom(fi, req); case RAW1394_REQ_UPDATE_ROM: return update_config_rom(fi, req); } if (req->req.generation != get_hpsb_generation(fi->host)) { req->req.error = RAW1394_ERROR_GENERATION; req->req.generation = get_hpsb_generation(fi->host); req->req.length = 0; queue_complete_req(req); return sizeof(struct raw1394_request); } switch (req->req.type) { case RAW1394_REQ_PHYPACKET: return write_phypacket(fi, req); case RAW1394_REQ_ASYNC_SEND: return handle_async_send(fi, req); } if (req->req.length == 0) { req->req.error = RAW1394_ERROR_INVALID_ARG; queue_complete_req(req); return sizeof(struct raw1394_request); } return handle_async_request(fi, req, node); } static ssize_t raw1394_write(struct file *file, const char *buffer, size_t count, loff_t *offset_is_ignored) { struct file_info *fi = (struct file_info *)file->private_data; struct pending_request *req; ssize_t retval = 0; if (count != sizeof(struct raw1394_request)) { return -EINVAL; } req = alloc_pending_request(); if (req == NULL) { return -ENOMEM; } req->file_info = fi; if (copy_from_user(&req->req, buffer, sizeof(struct raw1394_request))) { free_pending_request(req); return -EFAULT; } switch (fi->state) { case opened: retval = state_opened(fi, req); break; case initialized: retval = state_initialized(fi, req); break; case connected: retval = state_connected(fi, req); break; } if (retval < 0) { free_pending_request(req); } return retval; } /* rawiso operations */ /* check if any RAW1394_REQ_RAWISO_ACTIVITY event is already in the * completion queue (reqlists_lock must be taken) */ static inline int __rawiso_event_in_queue(struct file_info *fi) { struct list_head *lh; struct pending_request *req; list_for_each(lh, &fi->req_complete) { req = list_entry(lh, struct pending_request, list); if (req->req.type == RAW1394_REQ_RAWISO_ACTIVITY) { return 1; } } return 0; } /* put a RAWISO_ACTIVITY event in the queue, if one isn't there already */ static void queue_rawiso_event(struct file_info *fi) { unsigned long flags; spin_lock_irqsave(&fi->reqlists_lock, flags); /* only one ISO activity event may be in the queue */ if (!__rawiso_event_in_queue(fi)) { struct pending_request *req = __alloc_pending_request(SLAB_ATOMIC); if (req) { req->file_info = fi; req->req.type = RAW1394_REQ_RAWISO_ACTIVITY; req->req.generation = get_hpsb_generation(fi->host); __queue_complete_req(req); } else { /* on allocation failure, signal an overflow */ if (fi->iso_handle) { atomic_inc(&fi->iso_handle->overflows); } } } spin_unlock_irqrestore(&fi->reqlists_lock, flags); } static void rawiso_activity_cb(struct hpsb_iso *iso) { unsigned long flags; struct list_head *lh; struct host_info *hi; spin_lock_irqsave(&host_info_lock, flags); hi = find_host_info(iso->host); if (hi != NULL) { list_for_each(lh, &hi->file_info_list) { struct file_info *fi = list_entry(lh, struct file_info, list); if (fi->iso_handle == iso) queue_rawiso_event(fi); } } spin_unlock_irqrestore(&host_info_lock, flags); } /* helper function - gather all the kernel iso status bits for returning to user-space */ static void raw1394_iso_fill_status(struct hpsb_iso *iso, struct raw1394_iso_status *stat) { stat->config.data_buf_size = iso->buf_size; stat->config.buf_packets = iso->buf_packets; stat->config.channel = iso->channel; stat->config.speed = iso->speed; stat->config.irq_interval = iso->irq_interval; stat->n_packets = hpsb_iso_n_ready(iso); stat->overflows = atomic_read(&iso->overflows); stat->xmit_cycle = iso->xmit_cycle; } static int raw1394_iso_xmit_init(struct file_info *fi, void *uaddr) { struct raw1394_iso_status stat; if (!fi->host) return -EINVAL; if (copy_from_user(&stat, uaddr, sizeof(stat))) return -EFAULT; fi->iso_handle = hpsb_iso_xmit_init(fi->host, stat.config.data_buf_size, stat.config.buf_packets, stat.config.channel, stat.config.speed, stat.config.irq_interval, rawiso_activity_cb); if (!fi->iso_handle) return -ENOMEM; fi->iso_state = RAW1394_ISO_XMIT; raw1394_iso_fill_status(fi->iso_handle, &stat); if (copy_to_user(uaddr, &stat, sizeof(stat))) return -EFAULT; /* queue an event to get things started */ rawiso_activity_cb(fi->iso_handle); return 0; } static int raw1394_iso_recv_init(struct file_info *fi, void *uaddr) { struct raw1394_iso_status stat; if (!fi->host) return -EINVAL; if (copy_from_user(&stat, uaddr, sizeof(stat))) return -EFAULT; fi->iso_handle = hpsb_iso_recv_init(fi->host, stat.config.data_buf_size, stat.config.buf_packets, stat.config.channel, stat.config.irq_interval, rawiso_activity_cb); if (!fi->iso_handle) return -ENOMEM; fi->iso_state = RAW1394_ISO_RECV; raw1394_iso_fill_status(fi->iso_handle, &stat); if (copy_to_user(uaddr, &stat, sizeof(stat))) return -EFAULT; return 0; } static int raw1394_iso_get_status(struct file_info *fi, void *uaddr) { struct raw1394_iso_status stat; struct hpsb_iso *iso = fi->iso_handle; raw1394_iso_fill_status(fi->iso_handle, &stat); if (copy_to_user(uaddr, &stat, sizeof(stat))) return -EFAULT; /* reset overflow counter */ atomic_set(&iso->overflows, 0); return 0; } /* copy N packet_infos out of the ringbuffer into user-supplied array */ static int raw1394_iso_recv_packets(struct file_info *fi, void *uaddr) { struct raw1394_iso_packets upackets; unsigned int packet = fi->iso_handle->first_packet; int i; if (copy_from_user(&upackets, uaddr, sizeof(upackets))) return -EFAULT; if (upackets.n_packets > hpsb_iso_n_ready(fi->iso_handle)) return -EINVAL; /* ensure user-supplied buffer is accessible and big enough */ if (verify_area(VERIFY_WRITE, upackets.infos, upackets.n_packets * sizeof(struct raw1394_iso_packet_info))) return -EFAULT; /* copy the packet_infos out */ for (i = 0; i < upackets.n_packets; i++) { if (__copy_to_user(&upackets.infos[i], &fi->iso_handle->infos[packet], sizeof(struct raw1394_iso_packet_info))) return -EFAULT; packet = (packet + 1) % fi->iso_handle->buf_packets; } return 0; } /* copy N packet_infos from user to ringbuffer, and queue them for transmission */ static int raw1394_iso_send_packets(struct file_info *fi, void *uaddr) { struct raw1394_iso_packets upackets; int i, rv; if (copy_from_user(&upackets, uaddr, sizeof(upackets))) return -EFAULT; if (upackets.n_packets > hpsb_iso_n_ready(fi->iso_handle)) return -EINVAL; /* ensure user-supplied buffer is accessible and big enough */ if (verify_area(VERIFY_READ, upackets.infos, upackets.n_packets * sizeof(struct raw1394_iso_packet_info))) return -EFAULT; /* copy the infos structs in and queue the packets */ for (i = 0; i < upackets.n_packets; i++) { struct raw1394_iso_packet_info info; if (__copy_from_user(&info, &upackets.infos[i], sizeof(struct raw1394_iso_packet_info))) return -EFAULT; rv = hpsb_iso_xmit_queue_packet(fi->iso_handle, info.offset, info.len, info.tag, info.sy); if (rv) return rv; } return 0; } static void raw1394_iso_shutdown(struct file_info *fi) { if (fi->iso_handle) hpsb_iso_shutdown(fi->iso_handle); fi->iso_handle = NULL; fi->iso_state = RAW1394_ISO_INACTIVE; } /* mmap the rawiso xmit/recv buffer */ static int raw1394_mmap(struct file *file, struct vm_area_struct *vma) { struct file_info *fi = file->private_data; if (fi->iso_state == RAW1394_ISO_INACTIVE) return -EINVAL; return dma_region_mmap(&fi->iso_handle->data_buf, file, vma); } /* ioctl is only used for rawiso operations */ static int raw1394_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct file_info *fi = file->private_data; switch(fi->iso_state) { case RAW1394_ISO_INACTIVE: switch(cmd) { case RAW1394_IOC_ISO_XMIT_INIT: return raw1394_iso_xmit_init(fi, (void*) arg); case RAW1394_IOC_ISO_RECV_INIT: return raw1394_iso_recv_init(fi, (void*) arg); default: break; } break; case RAW1394_ISO_RECV: switch(cmd) { case RAW1394_IOC_ISO_RECV_START: { /* copy args from user-space */ int args[3]; if (copy_from_user(&args[0], (void*) arg, sizeof(args))) return -EFAULT; return hpsb_iso_recv_start(fi->iso_handle, args[0], args[1], args[2]); } case RAW1394_IOC_ISO_XMIT_RECV_STOP: hpsb_iso_stop(fi->iso_handle); return 0; case RAW1394_IOC_ISO_RECV_LISTEN_CHANNEL: return hpsb_iso_recv_listen_channel(fi->iso_handle, arg); case RAW1394_IOC_ISO_RECV_UNLISTEN_CHANNEL: return hpsb_iso_recv_unlisten_channel(fi->iso_handle, arg); case RAW1394_IOC_ISO_RECV_SET_CHANNEL_MASK: { /* copy the u64 from user-space */ u64 mask; if (copy_from_user(&mask, (void*) arg, sizeof(mask))) return -EFAULT; return hpsb_iso_recv_set_channel_mask(fi->iso_handle, mask); } case RAW1394_IOC_ISO_GET_STATUS: return raw1394_iso_get_status(fi, (void*) arg); case RAW1394_IOC_ISO_RECV_PACKETS: return raw1394_iso_recv_packets(fi, (void*) arg); case RAW1394_IOC_ISO_RECV_RELEASE_PACKETS: return hpsb_iso_recv_release_packets(fi->iso_handle, arg); case RAW1394_IOC_ISO_RECV_FLUSH: return hpsb_iso_recv_flush(fi->iso_handle); case RAW1394_IOC_ISO_SHUTDOWN: raw1394_iso_shutdown(fi); return 0; case RAW1394_IOC_ISO_QUEUE_ACTIVITY: queue_rawiso_event(fi); return 0; } break; case RAW1394_ISO_XMIT: switch(cmd) { case RAW1394_IOC_ISO_XMIT_START: { /* copy two ints from user-space */ int args[2]; if (copy_from_user(&args[0], (void*) arg, sizeof(args))) return -EFAULT; return hpsb_iso_xmit_start(fi->iso_handle, args[0], args[1]); } case RAW1394_IOC_ISO_XMIT_SYNC: return hpsb_iso_xmit_sync(fi->iso_handle); case RAW1394_IOC_ISO_XMIT_RECV_STOP: hpsb_iso_stop(fi->iso_handle); return 0; case RAW1394_IOC_ISO_GET_STATUS: return raw1394_iso_get_status(fi, (void*) arg); case RAW1394_IOC_ISO_XMIT_PACKETS: return raw1394_iso_send_packets(fi, (void*) arg); case RAW1394_IOC_ISO_SHUTDOWN: raw1394_iso_shutdown(fi); return 0; case RAW1394_IOC_ISO_QUEUE_ACTIVITY: queue_rawiso_event(fi); return 0; } break; default: break; } return -EINVAL; } static unsigned int raw1394_poll(struct file *file, poll_table *pt) { struct file_info *fi = file->private_data; unsigned int mask = POLLOUT | POLLWRNORM; poll_wait(file, &fi->poll_wait_complete, pt); spin_lock_irq(&fi->reqlists_lock); if (!list_empty(&fi->req_complete)) { mask |= POLLIN | POLLRDNORM; } spin_unlock_irq(&fi->reqlists_lock); return mask; } static int raw1394_open(struct inode *inode, struct file *file) { struct file_info *fi; if (ieee1394_file_to_instance(file) > 0) { return -ENXIO; } fi = kmalloc(sizeof(struct file_info), SLAB_KERNEL); if (fi == NULL) return -ENOMEM; memset(fi, 0, sizeof(struct file_info)); fi->notification = (u8) RAW1394_NOTIFY_ON; /* busreset notification */ INIT_LIST_HEAD(&fi->list); fi->state = opened; INIT_LIST_HEAD(&fi->req_pending); INIT_LIST_HEAD(&fi->req_complete); sema_init(&fi->complete_sem, 0); spin_lock_init(&fi->reqlists_lock); init_waitqueue_head(&fi->poll_wait_complete); INIT_LIST_HEAD(&fi->addr_list); file->private_data = fi; return 0; } static int raw1394_release(struct inode *inode, struct file *file) { struct file_info *fi = file->private_data; struct list_head *lh; struct pending_request *req; int done = 0, i, fail = 0; int retval = 0; struct list_head *entry; struct arm_addr *addr = NULL; struct list_head *lh_1, *lh_2; struct host_info *hi; struct file_info *fi_hlp = NULL; struct arm_addr *arm_addr = NULL; int another_host; if (fi->iso_state != RAW1394_ISO_INACTIVE) raw1394_iso_shutdown(fi); for (i = 0; i < 64; i++) { if (fi->listen_channels & (1ULL << i)) { hpsb_unlisten_channel(&raw1394_highlevel, fi->host, i); } } spin_lock_irq(&host_info_lock); fi->listen_channels = 0; spin_unlock_irq(&host_info_lock); fail = 0; /* set address-entries invalid */ spin_lock_irq(&host_info_lock); while (!list_empty(&fi->addr_list)) { another_host = 0; lh = fi->addr_list.next; addr = list_entry(lh, struct arm_addr, addr_list); /* another host with valid address-entry containing same addressrange? */ list_for_each(lh_1, &host_info_list) { hi = list_entry(lh_1, struct host_info, list); if (hi->host != fi->host) { list_for_each(lh_2, &hi->file_info_list) { fi_hlp = list_entry(lh_2, struct file_info, list); entry = fi_hlp->addr_list.next; while (entry != &(fi_hlp->addr_list)) { arm_addr = list_entry(entry, struct arm_addr, addr_list); if (arm_addr->start == addr->start) { DBGMSG("raw1394_release: " "another host ownes " "same addressrange"); another_host = 1; break; } entry = entry->next; } if (another_host) { break; } } } } if (!another_host) { DBGMSG("raw1394_release: call hpsb_arm_unregister"); retval = hpsb_unregister_addrspace(&raw1394_highlevel, addr->start); if (!retval) { ++fail; printk(KERN_ERR "raw1394_release arm_Unregister failed\n"); } } DBGMSG("raw1394_release: delete addr_entry from list"); list_del(&addr->addr_list); vfree(addr->addr_space_buffer); kfree(addr); } /* while */ spin_unlock_irq(&host_info_lock); if (fail > 0) { printk(KERN_ERR "raw1394: during addr_list-release " "error(s) occurred \n"); } while (!done) { spin_lock_irq(&fi->reqlists_lock); while (!list_empty(&fi->req_complete)) { lh = fi->req_complete.next; list_del(lh); req = list_entry(lh, struct pending_request, list); free_pending_request(req); } if (list_empty(&fi->req_pending)) done = 1; spin_unlock_irq(&fi->reqlists_lock); if (!done) down_interruptible(&fi->complete_sem); } if (fi->state == connected) { spin_lock_irq(&host_info_lock); list_del(&fi->list); spin_unlock_irq(&host_info_lock); hpsb_unref_host(fi->host); } kfree(fi); return 0; } /*** HOTPLUG STUFF **********************************************************/ /* * Export information about protocols/devices supported by this driver. */ static struct ieee1394_device_id raw1394_id_table[] = { { .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, .version = AVC_SW_VERSION_ENTRY & 0xffffff }, { .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff, .version = CAMERA_SW_VERSION_ENTRY & 0xffffff }, { } }; MODULE_DEVICE_TABLE(ieee1394, raw1394_id_table); static struct hpsb_protocol_driver raw1394_driver = { .name = "raw1394 Driver", .id_table = raw1394_id_table, }; /******************************************************************************/ static struct hpsb_highlevel raw1394_highlevel = { .name = RAW1394_DEVICE_NAME, .add_host = add_host, .remove_host = remove_host, .host_reset = host_reset, .iso_receive = iso_receive, .fcp_request = fcp_request, }; static struct file_operations file_ops = { .owner = THIS_MODULE, .read = raw1394_read, .write = raw1394_write, .mmap = raw1394_mmap, .ioctl = raw1394_ioctl, .poll = raw1394_poll, .open = raw1394_open, .release = raw1394_release, }; static int __init init_raw1394(void) { hpsb_register_highlevel(&raw1394_highlevel); devfs_handle = devfs_register(NULL, RAW1394_DEVICE_NAME, DEVFS_FL_NONE, IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_RAW1394 * 16, S_IFCHR | S_IRUSR | S_IWUSR, &file_ops, NULL); if (ieee1394_register_chardev(IEEE1394_MINOR_BLOCK_RAW1394, THIS_MODULE, &file_ops)) { HPSB_ERR("raw1394 failed to register minor device block"); devfs_unregister(devfs_handle); hpsb_unregister_highlevel(&raw1394_highlevel); return -EBUSY; } printk(KERN_INFO "raw1394: /dev/%s device initialized\n", RAW1394_DEVICE_NAME); hpsb_register_protocol(&raw1394_driver); return 0; } static void __exit cleanup_raw1394(void) { hpsb_unregister_protocol(&raw1394_driver); ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_RAW1394); devfs_unregister(devfs_handle); hpsb_unregister_highlevel(&raw1394_highlevel); } module_init(init_raw1394); module_exit(cleanup_raw1394); MODULE_LICENSE("GPL");