📄 raw1394.c
字号:
/* * 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/compat.h>#include "csr1212.h"#include "highlevel.h"#include "hosts.h"#include "ieee1394.h"#include "ieee1394_core.h"#include "ieee1394_hotplug.h"#include "ieee1394_transactions.h"#include "ieee1394_types.h"#include "iso.h"#include "nodemgr.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...) do {} while (0)#endifstatic 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);#include <asm/current.h>static void print_old_iso_deprecation(void){ static pid_t p; if (p == current->pid) return; p = current->pid; printk(KERN_WARNING "raw1394: WARNING - Program \"%s\" uses unsupported" " isochronous request types which will be removed in a next" " kernel release\n", current->comm); printk(KERN_WARNING "raw1394: Update your software to use libraw1394's" " newer interface\n");}static struct pending_request *__alloc_pending_request(gfp_t flags){ struct pending_request *req; req = kzalloc(sizeof(*req), flags); if (req) INIT_LIST_HEAD(&req->list); return req;}static inline struct pending_request *alloc_pending_request(void){ return __alloc_pending_request(GFP_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_move_tail(&req->list, &fi->req_complete); wake_up(&fi->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 = kmalloc(sizeof(*hi), GFP_KERNEL); if (hi) { 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(GFP_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(GFP_ATOMIC); if (!req) break; if (!ibs) { ibs = kmalloc(sizeof(*ibs) + length, GFP_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(GFP_ATOMIC); if (!req) break; if (!ibs) { ibs = kmalloc(sizeof(*ibs) + length, GFP_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);}#ifdef CONFIG_COMPATstruct compat_raw1394_req { __u32 type; __s32 error; __u32 misc; __u32 generation; __u32 length; __u64 address; __u64 tag; __u64 sendb; __u64 recvb;} __attribute__((packed));static const char __user *raw1394_compat_write(const char __user *buf){ struct compat_raw1394_req __user *cr = (typeof(cr)) buf; struct raw1394_request __user *r; r = compat_alloc_user_space(sizeof(struct raw1394_request));#define C(x) __copy_in_user(&r->x, &cr->x, sizeof(r->x)) if (copy_in_user(r, cr, sizeof(struct compat_raw1394_req)) || C(address) || C(tag) || C(sendb) || C(recvb)) return ERR_PTR(-EFAULT); return (const char __user *)r;}#undef C#define P(x) __put_user(r->x, &cr->x)static intraw1394_compat_read(const char __user *buf, struct raw1394_request *r){ struct compat_raw1394_req __user *cr = (typeof(cr)) r; if (!access_ok(VERIFY_WRITE, cr, sizeof(struct compat_raw1394_req)) || P(type) ||
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -