name='generator' content='cgit v1.2.2'/>
aboutsummaryrefslogblamecommitdiffstats
path: root/drivers/ieee1394/raw1394.c
blob: da5f8829b503d1047c7cb36126033c036ad630ca (plain) (tree)



































                                                                         
                        
                       




                            
                         

                    

                      
                     
                          
                             
                                  


                           













                                                         
                                            





















                                                                           
                                                







                                                           
                                                                   


                                    

                                           
                                           





                                                                 
                                                   



















                                                                          
 

                                                      



















































                                                                                
                                              
 
                 



































































                                                                          
                                                                          






















                                                                              






















                                                                          
                                                                  



                                      
                                                                    
                                                          






























                                                                        

                           


                    
 

                         
 
                      
 
                  
 

                    




                                                  


                                                                      
                                                                
                                         
 




                                                                     



                         

                                                                     





                                      
          

                                                                      
                                                                
 
                                                                              









                               
 





                                                 
























                                                                               
 



                                                                      


                                    




                                                         








                                                      
                                                   
                                       
                








                                                                              
                                            

         





                                                                    
 
                    


                                                         
                                                             
              




                                                                        
                 

                                                               


























                                                                                
                 



                                                                               
                            







                                                                        
                         



                                    
                                                          
                                                                     
 
                          








                                                                          
                                                               
 
                          











                                                                                
                                                          
                                                  
                                                                   
                                          
                 





















                                                                               
                                                               










                                                           
                 

 






















                                                                                
                            































































































































                                                                                
                         




                                                                                
                                                     
                                                    
                                                          








                                                          
                 

 

                                                                               
                            


                                                   
                         
 

                                                                       


                                                           
                         

         

                                                    








                                                                   
                         



                                                                   
                          


                                                        
                         








                                                          
                                      




                                                                                
                                                     
                                                    
                                                          








                                                                      
                 




                                                                           
                               









                                                         
                                              


                                                                        
                                                     























                                                                               
                                                                  

























                                                                                         
                                                          

                                                                   
                                                                          














                                                                                            
                                                      


                                                                   
                                                                          























































                                                                                            
                                                          





                                                                          
                               









                                                                 
                                              


                                                                        
                                                     























                                                                                
                                                                  
























                                                                                         
                                                          

                                                                    
                                                                          






                                                                                            
                                                      


                                                                    
                                                                          













































                                                                                            
                                                          






                                                                           
                               
























                                                                                 
                                                     
























                                                                               
                                                                  














































































                                                                                                  
                                                          

                                                                   
                                                                          



                                                                                                                                                             
                                                      


                                                                   
                                                                          
































































                                                                                            
                                                          






                                                                           
                               































                                                                                 
                                                     

























                                                                                         
                                                                  
















































































                                                                                                  
                                                          
                           
                                                                          




                                                                                                                                                             
                                                      

                                                  
                                                                          

































































                                                                                            
                                                          





























                                                                             
                                                  




                                             
                                                           





























                                                                        
 
























                                                                          
                                                               

                                               


























                                                                                

                                                       







                                                                                  



                                                                               
                                                          

                                                                
                         






                                                                           
                                                          
                                                                
                                                               



                                                                   

                                   
                                                                       
                 






























































                                                                                
                                                               


                                                                               
                         














                                                                              
                 























                                                                                  
                                                                               





                                                                                


                                                                          
                                                      
                                                         
 



                                                                          
                                         

































                                                                                  
                                                                               





                                                                                  


                                                                          
                                                      
                                                         
 



                                                                          
                                         



















                                                                                                                                                  
                         









                                                                             
                            









                                                                                
                                                     
                                                    
                                                          







                                                                            
                 



                                                                            
                    
                                                               



























                                                                                                                                                  
                    
                                                               
































































                                                                                                                                                  
                                 









                                                             
                                                                              











                                                                


































                                                                               
                            




                                                                             
                         























                                                                                
                         
 














                                                   
                                  
                                    



                                                                             
                         


                                           
                         





                                                                                                                                                          
                                 




                                                                                                                                                          
                                 

















                                                                    
                         











                                                           
                         









                                                                           
                                 
 
                    


                                                         
                                                      

                                                               

              














                                                                                

                                             
 













                                                    

                                       

                                          


                               





























                                                                          
                                                        






































                                                                                         


                                                     





                                                      
                                                                            









































































                                                                            

                                   


















                                                                              

                                                               























                                                                                  
                                                              

                               


                                                                   

                                                                      

                                                               



























                                                                             











                                                                               



                                                                      

                

                                             

                                                  




                                                                            
 
                   

 














                                                                          
 

                                               







                                                                              
                 













                                                                             
                 





























                                                                          
                 














                                                          
                
                               
         








                                                              
 







                                                          

                                             
 













                                                            
 
                                       
 


                   








                              




                                                  











                                                                                   
                           






                                                                                                             
                                                                           






















                                                                                 















                                                                             
                                                    














                                                                                                




                   



                                                                    
                            
 
                                                
 
                                                     


                                             
                                                          







                                                               
                                              
                

                               


                                                                                   
                                     


                                          
                                           
                                                











                                                                  
                    







                                         
                            



                                                  
                                                  


                                         












                                                                                
                                                                                   


































                                                                                  
                                                       




                                                                    


                                                                   
                                                             
                                                       
                                                  
 
                                                 
                                                                  
 











                                                                               

























                                                                                
                                                          
                                    
                                                               



                                              




                                                       








                                                                              
             
                                                             



















                                                                             
                   

                                                     
                          








                                                                                



                                   
                                                    



                               
                                        


                                             










                                                    
                  



                                                                    


                               
 

                                                
















                                                                              
              


                                                                 
                
                                                      
          




                                        


                                                                 
                                






                                                      
/*
 * 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/mutex.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 const 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 (__force const char __user *)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;
	}

	if (file->f_flags & O_NONBLOCK) {
		if (!(req = next_complete_req(fi)))
			return -EAGAIN;
	} else {
		/*
		 * NB: We call the macro wait_event_interruptible() with a
		 * condition argument with side effect.  This is only possible
		 * because the side effect does not occur until the condition
		 * became true, and wait_event_interruptible() won't evaluate
		 * the condition again after that.
		 */
		if (wait_event_interruptible(fi->wait_complete,
					     (req = next_complete_req(fi))))
			return -ERESTARTSYS;
	}

	if (req->req.length) {
		if (copy_to_user(int2ptr(req->req.recvb), req->data,
				 req->req.length)) {
			req->req.error = RAW1394_ERROR_MEMFAULT;
		}
	}

#ifdef CONFIG_COMPAT
	if (count == sizeof(struct compat_raw1394_req) &&
   	    sizeof(struct compat_raw1394_req) !=
			sizeof(struct raw1394_request)) {
		ret = raw1394_compat_read(buffer, &req->req);
	} else
#endif
	{
		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 0;
}

static int state_initialized(struct file_info *fi, struct pending_request *req)
{
	unsigned long flags;
	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 0;
	}

	switch (req->req.type) {
	case RAW1394_REQ_LIST_CARDS:
		spin_lock_irqsave(&host_info_lock, flags);
		khl = kmalloc(sizeof(*khl) * host_count, GFP_ATOMIC);

		if (khl) {
			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_irqrestore(&host_info_lock, flags);

		if (khl) {
			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_irqsave(&host_info_lock, flags);
		if (req->req.misc >= host_count) {
			req->req.error = RAW1394_ERROR_INVALID_ARG;
			goto out_set_card;
		}
		list_for_each_entry(hi, &host_info_list, list)
			if (!req->req.misc--)
				break;
		get_device(&hi->host->device); /* FIXME handle failure case */
		list_add_tail(&fi->list, &hi->file_info_list);

		/* prevent unloading of the host's low-level driver */
		if (!try_module_get(hi->host->driver->owner)) {
			req->req.error = RAW1394_ERROR_ABORTED;
			goto out_set_card;
		}
		WARN_ON(fi->host);
		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;
out_set_card:
		spin_unlock_irqrestore(&host_info_lock, flags);

		req->req.length = 0;
		break;

	default:
		req->req.error = RAW1394_ERROR_STATE_ORDER;
		req->req.length = 0;
		break;
	}

	queue_complete_req(req);
	return 0;
}

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 {