⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 raw1394.c

📁 Ieee1394驱动实现
💻 C
📖 第 1 页 / 共 5 页
字号:
/*
 * 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/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)
#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(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 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_COMPAT
struct compat_raw1394_req {
	__u32 type;
	__s32 error;
	__u32 misc;

	__u32 generation;
	__u32 length;

	__u64 address;

	__u64 tag;

	__u64 sendb;
	__u64 recvb;
}
#if defined(CONFIG_X86_64) || defined(CONFIG_IA64)
__attribute__((packed))
#endif
;

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 int
raw1394_compat_read(const char __user *buf, struct raw1394_request *r)
{
	struct compat_raw1394_req __user *cr = (typeof(cr)) buf;
	if (!access_ok(VERIFY_WRITE, cr, sizeof(struct compat_raw1394_req)) ||
	    P(type) ||
	    P(error) ||
	    P(misc) ||
	    P(generation) ||
	    P(length) ||
	    P(address) ||
	    P(tag) ||
	    P(sendb) ||
	    P(recvb))
		return -EFAULT;
	return sizeof(struct compat_raw1394_req);
}
#undef P

#endif

/* get next completed request  (caller must hold fi->reqlists_lock) */
static inline struct pending_request *__next_complete_req(struct file_info *fi)
{
	struct list_head *lh;
	struct pending_request *req = NULL;

	if (!list_empty(&fi->req_complete)) {
		lh = fi->req_complete.next;
		list_del(lh);
		req = list_entry(lh, struct pending_request, list);
	}
	return req;
}

/* atomically get next completed request */
static struct pending_request *next_complete_req(struct file_info *fi)
{
	unsigned long flags;
	struct pending_request *req;

	spin_lock_irqsave(&fi->reqlists_lock, flags);
	req = __next_complete_req(fi);
	spin_unlock_irqrestore(&fi->reqlists_lock, flags);
	return 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 pending_request *req;
	ssize_t ret;

#ifdef CONFIG_COMPAT
	if (count == sizeof(struct compat_raw1394_req)) {
		/* ok */
	} else
#endif
	if (count != sizeof(struct raw1394_request)) {
		return -EINVAL;
	}

	if (!access_ok(VERIFY_WRITE, buffer, count)) {
		return -EFAULT;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -