760 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			760 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * This is part of the Sequans SQN1130 driver.
 | |
|  * Copyright 2009 SEQUANS Communications
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU General Public License as published by
 | |
|  * the Free Software Foundation; either version 2 of the License, or (at
 | |
|  * your option) any later version.
 | |
|  */
 | |
| 
 | |
| 
 | |
| #include <linux/version.h>
 | |
| #include <linux/if_ether.h>
 | |
| #include <linux/types.h>
 | |
| #include <linux/skbuff.h>
 | |
| #include <linux/netdevice.h>
 | |
| #include <linux/proc_fs.h>
 | |
| #include <linux/device.h>
 | |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
 | |
| #include <linux/devfs_fs_kernel.h>
 | |
| #endif
 | |
| #include <linux/errno.h>
 | |
| #include <linux/types.h>
 | |
| #include <linux/list.h>
 | |
| #include <linux/poll.h>
 | |
| #include <linux/netdevice.h>
 | |
| #include <linux/cdev.h>
 | |
| #include <linux/byteorder/generic.h>
 | |
| 
 | |
| 
 | |
| #include "version.h"
 | |
| #include "msg.h"
 | |
| #include "thp.h"
 | |
| 
 | |
| #include "thp_ioctl.h"
 | |
| 
 | |
| #define THP_TRACE	0	/* print info messages from THP read/write handlers */
 | |
| #define THP_HEADER_DUMP	0	/* verbosely dump header of THP TX/RX packets */
 | |
| 
 | |
| #define THP_DEBUG 0
 | |
| #define SKB_DEBUG 0
 | |
| #define DRVREV  SQN_MODULE_VERSION
 | |
| 
 | |
| extern bool drop_packet;
 | |
| 
 | |
| const uint8_t  host_macaddr[ETH_ALEN] 	= {0x00, 0x16, 0x08, 0xff, 0x00, 0x01};
 | |
| const uint8_t  ss_macaddr[ETH_ALEN] 	= {0x00, 0x16, 0x08, 0xff, 0x00, 0x00};
 | |
| 
 | |
| 
 | |
| // Queue of packets destined to the Connection Manager
 | |
| // TODO: check size of the queue, it's should always be one.
 | |
| struct sk_buff_head to_sqntool_queue;
 | |
| 
 | |
| DECLARE_WAIT_QUEUE_HEAD(to_sqntool_wait);
 | |
| 
 | |
| struct net_device *this_device = NULL;
 | |
| 
 | |
| struct packet_type rx_packet_type = { 0 };
 | |
| extern int mmc_wimax_uart_switch(int uart);
 | |
| 
 | |
| uint8_t is_thp_packet(uint8_t  *dest_addr)
 | |
| {
 | |
| 	return (memcmp(dest_addr, host_macaddr, ETH_ALEN)==0);
 | |
| }
 | |
| 
 | |
| inline struct ethhdr *skb2ethhdr(const struct sk_buff *skb)
 | |
| {
 | |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
 | |
| 	return (struct ethhdr *)skb->mac.raw;
 | |
| #else
 | |
| 	return (struct ethhdr *)skb_mac_header(skb);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| 
 | |
| /* TODO: Fix kernel version */
 | |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
 | |
| int thp_handler(struct sk_buff *skb, struct net_device *pDev, struct packet_type *pPt, struct net_device *pOrigDev)
 | |
| #else
 | |
| int thp_handler(struct sk_buff *skb, struct net_device *pDev, struct packet_type *pPt)
 | |
| #endif
 | |
| {
 | |
| 	struct sk_buff *skb_thp = 0;
 | |
| 	struct ethhdr *eth = 0;
 | |
| 
 | |
| 	sqn_pr_enter();
 | |
| 
 | |
| 	/* We need only ETH_P_802_2 protocol packets with THP mac address */
 | |
| 	eth = skb2ethhdr(skb);
 | |
| 	if(ntohs(skb->protocol) != ETH_P_802_2 || !is_thp_packet(eth->h_dest)) {
 | |
| 		//for DDTM, drop all NOT THP packets
 | |
| 		if(drop_packet) {
 | |
| 			sqn_pr_dbg("HTC CODE: drop packet for DDTM\n");
 | |
| 			skb->pkt_type = PACKET_OTHERHOST;
 | |
| 		}
 | |
| 		goto not_thp_out;
 | |
| 	}
 | |
| 
 | |
| 	skb_thp = skb_clone(skb, GFP_ATOMIC);
 | |
|     /* Bugz 22554: strip CRC at the end of packet */
 | |
|     skb_trim(skb_thp, skb_thp->len - 4);
 | |
| 
 | |
| #if THP_TRACE
 | |
| 	sqn_pr_info("%s: RX packet, len = %d\n", __func__, skb_thp->len);
 | |
| #endif
 | |
| 	sqn_pr_dbg("RX THP packet, length %d\n", skb_thp->len);
 | |
| 	skb_queue_tail(&to_sqntool_queue, skb_thp);
 | |
| 
 | |
| 	if(skb_queue_len(&to_sqntool_queue) == 256){
 | |
| 		skb_thp = skb_dequeue(&to_sqntool_queue);
 | |
| 		kfree_skb(skb_thp);
 | |
| 	}
 | |
| 
 | |
| 	wake_up_interruptible(&to_sqntool_wait);	//Wake up wait queue
 | |
| 
 | |
| thp_out:
 | |
|     dev_kfree_skb_any(skb); 
 | |
| 	sqn_pr_leave();
 | |
| 	return NET_RX_DROP;
 | |
| not_thp_out:
 | |
| 	dev_kfree_skb_any(skb);
 | |
| 	sqn_pr_leave();
 | |
| 	return NET_RX_SUCCESS;
 | |
| }
 | |
| 
 | |
| // Initialization function for THP handler
 | |
| int  init_thp_handler(struct net_device *dev)
 | |
| {
 | |
| 	sqn_pr_enter();
 | |
| #if THP_DEBUG
 | |
| 	printk(KERN_WARNING "init_thp_handler +\n");
 | |
| #endif
 | |
| 
 | |
| 	skb_queue_head_init(&to_sqntool_queue);
 | |
| 
 | |
| 	/* Define type of intercepted packets */
 | |
| 	rx_packet_type.type = htons(ETH_P_ALL);    /* Intercept all packets */
 | |
| 	rx_packet_type.dev  = dev;
 | |
| 	rx_packet_type.func = thp_handler;     /* Network packet handler function */
 | |
| 
 | |
| 	/* Register packet handler */
 | |
| 	dev_add_pack(&rx_packet_type);
 | |
| 
 | |
| #if THP_DEBUG
 | |
| 	printk(KERN_WARNING "init_thp_handler -\n");
 | |
| #endif
 | |
| 	sqn_pr_leave();
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| // Clean up function for THP handler
 | |
| void cleanup_thp_handler(void)
 | |
| {
 | |
| 	sqn_pr_enter();
 | |
| #if THP_DEBUG
 | |
| 	printk(KERN_WARNING "cleanup_thp_handler +\n");
 | |
| #endif
 | |
| 
 | |
| 	/* unregister packet handler */
 | |
| 	dev_remove_pack(&rx_packet_type);
 | |
| 
 | |
| 	if(!skb_queue_empty(&to_sqntool_queue))
 | |
| 		skb_queue_purge(&to_sqntool_queue) ;
 | |
| 
 | |
| #if THP_DEBUG
 | |
| 	printk(KERN_WARNING "cleanup_thp_handler -\n");
 | |
| #endif
 | |
| 	sqn_pr_leave();
 | |
| }
 | |
| 
 | |
| #define PROC_DIR_NAME 	"kthp"
 | |
| #define DRV_REVISION 	"kthp/drvrev"
 | |
| #define IFACE_FILENAME 	"kthp/iface_name"
 | |
| 
 | |
| char procfs_dir[64] = PROC_DIR_NAME;
 | |
| 
 | |
| static struct proc_dir_entry*   kthp_proc_dir;
 | |
| 
 | |
| extern struct net_device *this_device;
 | |
| 
 | |
| /** PROC_FS Read Functions */
 | |
| 
 | |
| static int ifacename_read(char *page, char **start, off_t off,
 | |
| 		int count, int *eof, void *data)
 | |
| {
 | |
| 	int len = 0;
 | |
| 
 | |
| 	sqn_pr_enter();
 | |
| #if THP_DEBUG
 | |
| 	printk(KERN_WARNING "ifacename_read +\n");
 | |
| #endif
 | |
| 
 | |
| 	if(this_device)
 | |
| 		len += sprintf(page, "%s\n",  this_device->name);
 | |
| 
 | |
| 	*eof = 1;
 | |
| 
 | |
| #if THP_DEBUG
 | |
| 	printk(KERN_WARNING "ifacename_read -\n");
 | |
| #endif
 | |
| 	sqn_pr_leave();
 | |
| 
 | |
| 	return len;
 | |
| }
 | |
| 
 | |
| 
 | |
| static int drvrev_read(char *page, char **start, off_t off,
 | |
| 		int count, int *eof, void *data)
 | |
| {
 | |
| 	int len = 0;
 | |
| 
 | |
| 	sqn_pr_enter();
 | |
| #if THP_DEBUG
 | |
| 	printk(KERN_WARNING "drvrev_read +\n");
 | |
| #endif
 | |
| 
 | |
| 	len += sprintf(page, "%s\n",  SQN_MODULE_VERSION);
 | |
| 
 | |
| 	*eof = 1;
 | |
| 
 | |
| 	sqn_pr_leave();
 | |
| 
 | |
| 	return len;
 | |
| }
 | |
| 
 | |
| static int install_entry(char *entry_name, read_proc_t* read_func)
 | |
| {
 | |
| 	struct proc_dir_entry* proc;
 | |
| 
 | |
| 	sqn_pr_enter();
 | |
| 
 | |
| 	proc = create_proc_read_entry(entry_name, S_IFREG | S_IRUGO | S_IWUSR
 | |
| 			, NULL, (read_proc_t*)read_func, NULL);
 | |
| 
 | |
| 	if (proc) {
 | |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)
 | |
| 		proc->owner = THIS_MODULE;
 | |
| #endif
 | |
| 	} else {
 | |
| 		printk(KERN_ALERT"/proc/ %s failed", entry_name);
 | |
| 		return 1;
 | |
| 	}
 | |
| 
 | |
| 	sqn_pr_leave();
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int init_procfs_handler(void)
 | |
| {
 | |
| 	sqn_pr_enter();
 | |
| 
 | |
| 	kthp_proc_dir = proc_mkdir(procfs_dir, NULL);
 | |
| 
 | |
| 	if (kthp_proc_dir) {
 | |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)
 | |
| 		kthp_proc_dir->owner = THIS_MODULE;
 | |
| #endif
 | |
| 	} else {
 | |
| 		remove_proc_entry(PROC_DIR_NAME, NULL);
 | |
| 		return 1;
 | |
| 	}
 | |
| 
 | |
| 	if(install_entry(IFACE_FILENAME, ifacename_read) ||
 | |
| 			install_entry(DRV_REVISION, drvrev_read))
 | |
| 	{
 | |
| 		return 1;
 | |
| 	}
 | |
| 
 | |
| #if THP_DEBUG
 | |
| 	printk(KERN_WARNING "drvrev_read -\n");
 | |
| #endif
 | |
| 	sqn_pr_leave();
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| void cleanup_procfs_handler(void)
 | |
| {
 | |
| 	sqn_pr_enter();
 | |
| #if THP_DEBUG
 | |
| 	printk(KERN_WARNING "cleanup_procfs_handler +\n");
 | |
| #endif
 | |
| 
 | |
| 	remove_proc_entry(IFACE_FILENAME, NULL);
 | |
| 	remove_proc_entry(DRV_REVISION, NULL);
 | |
| 
 | |
| 	if (kthp_proc_dir)
 | |
| 		remove_proc_entry(procfs_dir, NULL);
 | |
| 
 | |
| #if THP_DEBUG
 | |
| 	printk(KERN_WARNING "cleanup_procfs_handler -\n");
 | |
| #endif
 | |
| 	sqn_pr_leave();
 | |
| }
 | |
| 
 | |
| 
 | |
| #define THP_FILENAME   "thp"
 | |
| 
 | |
| 
 | |
| static dev_t dev_num;
 | |
| //static int dev_index;
 | |
| static struct cdev *thp_dev;
 | |
| static struct class *thp_class;
 | |
| static struct device *thp_device;
 | |
| 
 | |
| static uint8_t once_open_flag = 0;
 | |
| 
 | |
| const char thp_filename[64] = THP_FILENAME;
 | |
| 
 | |
| /********************File operations*****************************/
 | |
| static int thp_open(struct inode*, struct file*);
 | |
| 
 | |
| static ssize_t thp_release(struct inode*, struct file*);
 | |
| 
 | |
| static ssize_t thp_read(struct file*, char*, size_t, loff_t*);
 | |
| 
 | |
| static ssize_t thp_write(struct file *file, const char *buf,
 | |
| 		size_t count, loff_t *ppos);
 | |
| 
 | |
| static unsigned int thp_poll(struct file *filp, poll_table *wait);
 | |
| 
 | |
| static int thp_ioctl(struct inode*, struct file*, unsigned int, unsigned long);
 | |
| 
 | |
| struct file_operations thp_fops =
 | |
| {
 | |
| 	.owner	=	THIS_MODULE
 | |
| 	, .open	=	thp_open
 | |
| 	, .release=	thp_release
 | |
| 	, .read	=	thp_read
 | |
| 	, .write=	thp_write
 | |
| 	, .poll	=	thp_poll
 | |
| 	, .ioctl =	thp_ioctl
 | |
| };
 | |
| 
 | |
| /********************** File Operations BEGIN  *****************************/
 | |
| 
 | |
| static int thp_open(struct inode * inode, struct file * filp)
 | |
| {
 | |
| 	sqn_pr_enter();
 | |
| #if THP_DEBUG
 | |
| 	printk(KERN_WARNING "thp_open +\n");
 | |
| #endif
 | |
| 
 | |
| 	// allow multiple open() call for supporting ioctl on HTC Supersonic
 | |
| 	/*
 | |
| 	if(once_open_flag)
 | |
| 		return -EBUSY;
 | |
| 	*/
 | |
| 
 | |
| 	once_open_flag = 1;
 | |
| 
 | |
| #if THP_DEBUG
 | |
| 	printk(KERN_WARNING "thp_open -\n");
 | |
| #endif
 | |
| 	sqn_pr_leave();
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static ssize_t thp_release(struct inode *inode, struct file *filp)
 | |
| {
 | |
| 	sqn_pr_enter();
 | |
| 
 | |
| 	once_open_flag = 0;
 | |
| 
 | |
| 	if(!skb_queue_empty(&to_sqntool_queue))
 | |
| 		skb_queue_purge(&to_sqntool_queue);
 | |
| 
 | |
| 	sqn_pr_leave();
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static ssize_t thp_read(struct file *filp, char *buf, size_t count, loff_t*ppos)
 | |
| {
 | |
| 	DECLARE_WAITQUEUE(wait, current);
 | |
| 
 | |
| 	struct sk_buff_head *head = &to_sqntool_queue;
 | |
| 	struct sk_buff *curr = NULL;
 | |
| 	ssize_t retval;
 | |
| #if THP_HEADER_DUMP
 | |
| 	const struct sqn_thp_header *th = 0;
 | |
| #endif
 | |
| 	sqn_pr_enter();
 | |
| #if THP_DEBUG
 | |
| 	printk(KERN_WARNING "thp_read +\n");
 | |
| #endif
 | |
| 
 | |
| 	add_wait_queue(&to_sqntool_wait, &wait);
 | |
| 	retval = -ERESTARTSYS;
 | |
| 
 | |
| 	if(0 == this_device) {
 | |
| 		printk(KERN_WARNING "thp_read() device removed\n");
 | |
| 		retval = -EINVAL;
 | |
| 		goto out;
 | |
| 	}
 | |
| 
 | |
| 	while(1)
 | |
| 	{
 | |
| 		if(!skb_queue_empty(head))
 | |
| 			break;
 | |
| 		if(signal_pending(current) || 0 == this_device) {
 | |
| 			printk(KERN_WARNING "thp_read() interrupted by signal\n");
 | |
| 			retval = -EINTR;
 | |
| 			goto out;
 | |
| 		}
 | |
| 		set_current_state(TASK_INTERRUPTIBLE);
 | |
| 		schedule();
 | |
| 	}
 | |
| 	curr = skb_dequeue(head);
 | |
| 
 | |
| 	if (count < curr->len) {
 | |
| 		printk(KERN_WARNING "%s: userspace buffer is too small (%u bytes)"
 | |
| 			" to hold THP packet (%u bytes)\n"
 | |
| 			, __func__, count, curr->len);
 | |
| 		retval = -EINVAL;
 | |
| 		goto free_skb;
 | |
| 	} else {
 | |
| 		count =  curr->len;
 | |
| 	}
 | |
| 
 | |
| 	if(copy_to_user(buf, curr->data, count))
 | |
| 	{
 | |
| 		printk(KERN_ERR "error copying data to user space\n");
 | |
| 		retval = -EFAULT;
 | |
| 		goto free_skb;
 | |
| 	}
 | |
| #if THP_TRACE
 | |
| 	sqn_pr_info("%s: [to_user]: len = %d\n", __func__, count);
 | |
| #endif
 | |
| #if THP_HEADER_DUMP
 | |
| 	th = (struct sqn_thp_header *) curr->data;
 | |
| 	sqn_pr_info("%s:  PKTLen: %4u | TVer: 0x0%x | Flags: 0x0%x | Len: %4u"
 | |
| 		" | SeqNum: %5u | AckNum: %5u | TLen: %5u\n", __func__
 | |
| 		, count
 | |
| 		, th->transport_version
 | |
| 		, th->flags
 | |
| 		, be16_to_cpu(th->length)
 | |
| 		, be16_to_cpu(th->seq_number)
 | |
| 		, be16_to_cpu(th->ack_number)
 | |
| 		, be32_to_cpu(th->total_length));
 | |
| #endif
 | |
| 	sqn_pr_dbg("[to_user]: len = %d\n", count);
 | |
| #ifdef SQN_DEBUG_DUMP
 | |
| 	sqn_pr_dbg_dump("RX:", curr->data, count);
 | |
| #endif
 | |
| 
 | |
| #if SKB_DEBUG
 | |
|     sqn_pr_info("%s: free skb [0x%p], users %d\n", __func__, curr, atomic_read(&curr->users));
 | |
| #endif
 | |
| 
 | |
|     retval = (ssize_t)count;
 | |
| free_skb:
 | |
| 
 | |
|     dev_kfree_skb_any(curr);
 | |
| 
 | |
| out:
 | |
| 	set_current_state(TASK_RUNNING);
 | |
| 	remove_wait_queue(&to_sqntool_wait, &wait);
 | |
| 
 | |
| #if THP_DEBUG
 | |
| 	printk(KERN_WARNING "thp_read -\n");
 | |
| #endif
 | |
| 	sqn_pr_leave();
 | |
| 
 | |
| 	return retval;
 | |
| }
 | |
| 
 | |
| 
 | |
| static ssize_t thp_write(struct file *file, const char *buf,
 | |
| 		size_t count, loff_t *ppos)
 | |
| {
 | |
| 
 | |
| 	ssize_t retval = -ENOMEM;
 | |
| 	struct sk_buff *skb;
 | |
| 	struct ethhdr ethh;
 | |
| 	int size = count + ETH_HLEN;
 | |
| #if THP_HEADER_DUMP
 | |
| 	const struct sqn_thp_header *th = 0;
 | |
| #endif
 | |
| 
 | |
| 	sqn_pr_enter();
 | |
| #if THP_DEBUG
 | |
| 	printk(KERN_WARNING "thp_write +\n");
 | |
| #endif
 | |
| 
 | |
| 	if(0 == this_device)
 | |
| 		return -ENODEV;
 | |
| 
 | |
| 	skb = __dev_alloc_skb(size, GFP_ATOMIC | GFP_DMA);
 | |
| 	if(skb == NULL)
 | |
| 		return retval;
 | |
| 
 | |
| #if SKB_DEBUG
 | |
|     sqn_pr_info("%s: [0x%p] alloc skb, users %d\n", __func__, skb, atomic_read(&skb->users)); 
 | |
| #endif
 | |
| 
 | |
| 	memcpy(ethh.h_dest, ss_macaddr, ETH_ALEN);
 | |
| 	memcpy(ethh.h_source, host_macaddr, ETH_ALEN);
 | |
| 	ethh.h_proto = htons(count);
 | |
| 
 | |
| 	memcpy(skb->data, ðh, sizeof(struct ethhdr));
 | |
| 	skb_put(skb, sizeof(struct ethhdr));
 | |
| 
 | |
| 	if(copy_from_user(skb->tail, buf, count)) {
 | |
| 		dev_kfree_skb_any(skb);
 | |
| 		return  -EFAULT;
 | |
| 	}
 | |
| 	skb_put(skb, count);
 | |
| #if THP_TRACE
 | |
| 	sqn_pr_info("%s: [from_user]: len = %d\n", __func__, count);
 | |
| #endif
 | |
| #if THP_HEADER_DUMP
 | |
| 	th = (struct sqn_thp_header *) buf;
 | |
| 	sqn_pr_info("%s: PKTLen: %4u | TVer: 0x0%x | Flags: 0x0%x | Len: %4u"
 | |
| 		" | SeqNum: %5u | AckNum: %5u | TLen: %5u\n", __func__
 | |
| 		, count
 | |
| 		, th->transport_version
 | |
| 		, th->flags
 | |
| 		, be16_to_cpu(th->length)
 | |
| 		, be16_to_cpu(th->seq_number)
 | |
| 		, be16_to_cpu(th->ack_number)
 | |
| 		, be32_to_cpu(th->total_length));
 | |
| #endif
 | |
| 	sqn_pr_dbg("[from_user]: len = %d\n", count);
 | |
| #ifdef SQN_DEBUG_DUMP
 | |
| 	sqn_pr_dbg_dump("TX:", skb->data, count);
 | |
| #endif
 | |
| 
 | |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
 | |
| 	this_device->hard_start_xmit(skb, this_device);
 | |
| #else
 | |
| 	this_device->netdev_ops->ndo_start_xmit(skb, this_device);
 | |
| #endif
 | |
| 
 | |
| 	retval = count;
 | |
| 
 | |
| #if THP_DEBUG
 | |
| 	printk(KERN_WARNING "thp_write -\n");
 | |
| #endif
 | |
| 	sqn_pr_leave();
 | |
| 
 | |
| 	return retval;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
| 
 | |
| */
 | |
| static unsigned int thp_poll(struct file *filp, poll_table *wait)
 | |
| {
 | |
| 	unsigned int mask = 0;
 | |
| 
 | |
| 	sqn_pr_enter();
 | |
| #if THP_DEBUG
 | |
| 	//printk(KERN_WARNING "thp_poll +\n");
 | |
| #endif
 | |
| 
 | |
| 	poll_wait(filp, &to_sqntool_wait, wait);
 | |
| 
 | |
| 	if (0 == this_device) {
 | |
| 		printk(KERN_WARNING "thp_poll() device removed\n");
 | |
| 		mask = POLLERR;
 | |
| 	} else if(skb_queue_empty(&to_sqntool_queue)) {
 | |
| 		mask = 0;
 | |
| 	} else {
 | |
| 		mask = (POLLIN | POLLRDNORM);
 | |
| 	}
 | |
| 
 | |
| #if THP_DEBUG
 | |
| 	//printk(KERN_WARNING "thp_poll -\n");
 | |
| #endif
 | |
| 	sqn_pr_leave();
 | |
| 
 | |
| 	return mask;
 | |
| }
 | |
| 
 | |
| static int thp_ioctl(struct inode* dev, struct file* handle, unsigned int cmd, unsigned long arg)
 | |
| {
 | |
| #if THP_DEBUG
 | |
| 	printk(KERN_WARNING "thp_ioctl +\n");
 | |
| #endif
 | |
| 	sqn_pr_enter();
 | |
| 
 | |
| 	switch (cmd) {
 | |
| 		case IOCTL_DROP_PACKETS:
 | |
| 			printk(KERN_WARNING "IOCTL_DROP_PACKETS arg=%d\n",(int)arg);
 | |
| 			if(arg == 1)
 | |
| 				drop_packet = true;
 | |
| 			else
 | |
| 				drop_packet = false;
 | |
| 			break;
 | |
| 
 | |
|         case IOCTL_SWITCH_UART:
 | |
| 			printk(KERN_WARNING "IOCTL_SWITCH_UART arg=%d\n",(int)arg);
 | |
| 			if(arg == 1) 
 | |
| 			    mmc_wimax_uart_switch(2); // Wimax
 | |
| 			else 
 | |
| 				mmc_wimax_uart_switch(0); // USB
 | |
| 			break;  
 | |
|         
 | |
| 		default:
 | |
| 			printk(KERN_WARNING "UNKNOWN OPERATION in thp_ioctl\n");
 | |
| 			return -1;
 | |
| 	}
 | |
| 
 | |
| 	sqn_pr_leave();
 | |
| #if THP_DEBUG
 | |
| 	printk(KERN_WARNING "thp_ioctl -\n");
 | |
| #endif
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int init_thp_devfile(void)
 | |
| {
 | |
| 	sqn_pr_enter();
 | |
| #if THP_DEBUG
 | |
| 	printk(KERN_WARNING "init_thp_devfile +\n");
 | |
| #endif
 | |
| 
 | |
| 	//Dynamic allocation of device number
 | |
| 	if(alloc_chrdev_region(&dev_num, 0, 1, thp_filename))
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	thp_dev = cdev_alloc();
 | |
| 	if(thp_dev == NULL)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	thp_dev->ops = &thp_fops;
 | |
| 	thp_dev->owner = THIS_MODULE;
 | |
| 
 | |
| 	if(cdev_add(thp_dev, dev_num, 1))
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	thp_class = class_create(THIS_MODULE, thp_filename);
 | |
| 	if (IS_ERR(thp_class))
 | |
| 	{
 | |
| 		printk("class_create error(0x%x)\n",(unsigned int)(thp_class));
 | |
| 		return PTR_ERR(thp_class);
 | |
| 	}
 | |
| 
 | |
| 
 | |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
 | |
| 	thp_device = device_create(thp_class, NULL, dev_num, NULL, thp_filename);
 | |
| #else
 | |
| 	thp_device = device_create(thp_class, NULL, dev_num, thp_filename);
 | |
| #endif
 | |
| 	if (IS_ERR(thp_device))
 | |
| 	{
 | |
| 		printk("device_create error(0x%x)\n", (unsigned int)(thp_device));
 | |
| 		return PTR_ERR(thp_device);
 | |
| 	}
 | |
| 
 | |
| #if THP_DEBUG
 | |
| 	printk(KERN_WARNING "init_thp_devfile -\n");
 | |
| #endif
 | |
| 	sqn_pr_leave();
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   \brief dev-fs cleanup function
 | |
| 
 | |
|  *   This function is called in module cleanup function
 | |
|  */
 | |
| void cleanup_thp_devfile(void)
 | |
| {
 | |
| 	sqn_pr_enter();
 | |
| #if THP_DEBUG
 | |
| 	printk(KERN_WARNING "cleanup_thp_devfile +\n");
 | |
| #endif
 | |
| 
 | |
| 	/* Unregister entry from /dev */
 | |
| 	device_destroy(thp_class, dev_num);
 | |
| 	class_destroy(thp_class);
 | |
| 	unregister_chrdev_region(dev_num, 1);
 | |
| 	cdev_del(thp_dev);
 | |
| 
 | |
| #if THP_DEBUG
 | |
| 	printk(KERN_WARNING "cleanup_thp_devfile -\n");
 | |
| #endif
 | |
| 	sqn_pr_leave();
 | |
| }
 | |
| /********************** File Operations END  *****************************/
 | |
| 
 | |
| int thp_wimax_uart_switch(int on)
 | |
| {
 | |
|     printk("%s on%d\n", __func__, on);
 | |
| 
 | |
| 	if (on) {
 | |
|         mmc_wimax_uart_switch(2); // Wimax
 | |
| 	}
 | |
| 	else {
 | |
|         mmc_wimax_uart_switch(0); // USB
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int init_thp(struct net_device* dev)
 | |
| {
 | |
| 	sqn_pr_enter();
 | |
| #if THP_DEBUG
 | |
| 	printk(KERN_WARNING "init_thp +\n");
 | |
| #endif
 | |
| 
 | |
| 	if (0 == this_device) {
 | |
| 		if(init_procfs_handler()) {
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		if(init_thp_devfile())
 | |
| 			return -1;
 | |
| 
 | |
| 		/* Don't call init_thp_handler() here, it will be called from
 | |
| 		 * probe() before interrupts are enabled, to ensure that we will
 | |
| 		 * catch all THP packets as soon as they appear
 | |
| 		 */
 | |
| 		/* if (init_thp_handler(dev)) */
 | |
| 			/* return -1; */
 | |
| 
 | |
| 		this_device = dev;
 | |
| 
 | |
| 		sqn_pr_info("KTHP initialized\n");
 | |
| 	}
 | |
| 
 | |
| #if THP_DEBUG
 | |
| 	printk(KERN_WARNING "init_thp -\n");
 | |
| #endif
 | |
| 	sqn_pr_leave();
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| void cleanup_thp(void)
 | |
| {
 | |
| 	sqn_pr_enter();
 | |
| #if THP_DEBUG
 | |
| 	printk(KERN_WARNING "cleanup_thp +\n");
 | |
| #endif
 | |
| 
 | |
| 	if (this_device) {
 | |
| 		cleanup_procfs_handler();
 | |
| 		cleanup_thp_handler();
 | |
| 		cleanup_thp_devfile();
 | |
| 		this_device = 0;
 | |
| 		sqn_pr_info("KTHP cleaned up\n");
 | |
| 	}
 | |
| 
 | |
| #if THP_DEBUG
 | |
| 	printk(KERN_WARNING "cleanup_thp -\n");
 | |
| #endif
 | |
| 	sqn_pr_leave();
 | |
| }
 |