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

📄 rndis.c

📁 ARM S3C2410 USB SLAVE LINUX驱动
💻 C
📖 第 1 页 / 共 3 页
字号:
	resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
	resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
	
	if (rndis_per_dev_params [configNr].ack)
	    rndis_per_dev_params [configNr].ack (
	    		rndis_per_dev_params [configNr].dev);
	
	return 0;
}


/* 
 * Device to Host Comunication 
 */
static int rndis_indicate_status_msg (int configNr, u32 status)
{
	rndis_indicate_status_msg_type	*resp;	
	rndis_resp_t			*r;
	
	if (rndis_per_dev_params [configNr].state == RNDIS_UNINITIALIZED)
	    return -ENOTSUPP;
	
	r = rndis_add_response (configNr, 
				sizeof (rndis_indicate_status_msg_type));
	if (!r)
		return -ENOMEM;
	resp = (rndis_indicate_status_msg_type *) r->buf;
	
	resp->MessageType = __constant_cpu_to_le32 (
			REMOTE_NDIS_INDICATE_STATUS_MSG);
	resp->MessageLength = __constant_cpu_to_le32 (20);
	resp->Status = cpu_to_le32 (status);
	resp->StatusBufferLength = __constant_cpu_to_le32 (0);
	resp->StatusBufferOffset = __constant_cpu_to_le32 (0);
	
	if (rndis_per_dev_params [configNr].ack) 
	    rndis_per_dev_params [configNr].ack (
	    		rndis_per_dev_params [configNr].dev);
	return 0;
}

int rndis_signal_connect (int configNr)
{
	rndis_per_dev_params [configNr].media_state
			= NDIS_MEDIA_STATE_CONNECTED;
	return rndis_indicate_status_msg (configNr, 
					  RNDIS_STATUS_MEDIA_CONNECT);
}

int rndis_signal_disconnect (int configNr)
{
	rndis_per_dev_params [configNr].media_state
			= NDIS_MEDIA_STATE_DISCONNECTED;
	return rndis_indicate_status_msg (configNr,
					  RNDIS_STATUS_MEDIA_DISCONNECT);
}

void rndis_uninit (int configNr)
{
	u8 *buf;
	u32 length;

	if (configNr >= RNDIS_MAX_CONFIGS)
		return;
	rndis_per_dev_params [configNr].used = 0;
	rndis_per_dev_params [configNr].state = RNDIS_UNINITIALIZED;

	/* drain the response queue */
	while ((buf = rndis_get_next_response(configNr, &length)))
		rndis_free_response(configNr, buf);
}

void rndis_set_host_mac (int configNr, const u8 *addr)
{
	rndis_per_dev_params [configNr].host_mac = addr;
}

/* 
 * Message Parser 
 */
int rndis_msg_parser (u8 configNr, u8 *buf)
{
	u32 MsgType, MsgLength;
	__le32 *tmp;
	struct rndis_params		*params;
	
	if (!buf)
		return -ENOMEM;
	
	tmp = (__le32 *) buf; 
	MsgType   = le32_to_cpup(tmp++);
	MsgLength = le32_to_cpup(tmp++);
	
	if (configNr >= RNDIS_MAX_CONFIGS)
		return -ENOTSUPP;
	params = &rndis_per_dev_params [configNr];
	
	/* NOTE: RNDIS is *EXTREMELY* chatty ... Windows constantly polls for
	 * rx/tx statistics and link status, in addition to KEEPALIVE traffic
	 * and normal HC level polling to see if there's any IN traffic.
	 */

	/* For USB: responses may take up to 10 seconds */
	switch (MsgType) {
	case REMOTE_NDIS_INITIALIZE_MSG:
		DEBUG("%s: REMOTE_NDIS_INITIALIZE_MSG\n", 
			__FUNCTION__ );
		params->state = RNDIS_INITIALIZED;
		return  rndis_init_response (configNr,
					     (rndis_init_msg_type *) buf);
		
	case REMOTE_NDIS_HALT_MSG:
		DEBUG("%s: REMOTE_NDIS_HALT_MSG\n",
			__FUNCTION__ );
		params->state = RNDIS_UNINITIALIZED;
		if (params->dev) {
			netif_carrier_off (params->dev);
			netif_stop_queue (params->dev);
		}
		return 0;
		
	case REMOTE_NDIS_QUERY_MSG:
		return rndis_query_response (configNr, 
					     (rndis_query_msg_type *) buf);
		
	case REMOTE_NDIS_SET_MSG:
		return rndis_set_response (configNr, 
					   (rndis_set_msg_type *) buf);
		
	case REMOTE_NDIS_RESET_MSG:
		DEBUG("%s: REMOTE_NDIS_RESET_MSG\n", 
			__FUNCTION__ );
		return rndis_reset_response (configNr,
					     (rndis_reset_msg_type *) buf);

	case REMOTE_NDIS_KEEPALIVE_MSG:
		/* For USB: host does this every 5 seconds */
		if (rndis_debug > 1)
			DEBUG("%s: REMOTE_NDIS_KEEPALIVE_MSG\n", 
				__FUNCTION__ );
		return rndis_keepalive_response (configNr,
						 (rndis_keepalive_msg_type *) 
						 buf);
		
	default: 
		/* At least Windows XP emits some undefined RNDIS messages.
		 * In one case those messages seemed to relate to the host
		 * suspending itself.
		 */
		printk (KERN_WARNING
			"%s: unknown RNDIS message 0x%08X len %d\n", 
			__FUNCTION__ , MsgType, MsgLength);
		{
			unsigned i;
			for (i = 0; i < MsgLength; i += 16) {
				DEBUG ("%03d: "
					" %02x %02x %02x %02x"
					" %02x %02x %02x %02x"
					" %02x %02x %02x %02x"
					" %02x %02x %02x %02x"
					"\n",
					i,
					buf[i], buf [i+1],
						buf[i+2], buf[i+3],
					buf[i+4], buf [i+5],
						buf[i+6], buf[i+7],
					buf[i+8], buf [i+9],
						buf[i+10], buf[i+11],
					buf[i+12], buf [i+13],
						buf[i+14], buf[i+15]);
			}
		}
		break;
	}
	
	return -ENOTSUPP;
}

int rndis_register (int (* rndis_control_ack) (struct net_device *))
{
	u8 i;
	
	for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
		if (!rndis_per_dev_params [i].used) {
			rndis_per_dev_params [i].used = 1;
			rndis_per_dev_params [i].ack = rndis_control_ack;
			DEBUG("%s: configNr = %d\n", __FUNCTION__, i);
			return i;
		}
	}
	DEBUG("failed\n");
	
	return -1;
}

void rndis_deregister (int configNr)
{
	DEBUG("%s: \n", __FUNCTION__ );
	
	if (configNr >= RNDIS_MAX_CONFIGS) return;
	rndis_per_dev_params [configNr].used = 0;
	
	return;
}

int rndis_set_param_dev (u8 configNr, struct net_device *dev, 
			 struct net_device_stats *stats,
			 u16 *cdc_filter)
{
	DEBUG("%s:\n", __FUNCTION__ );
	if (!dev || !stats) return -1;
	if (configNr >= RNDIS_MAX_CONFIGS) return -1;
	
	rndis_per_dev_params [configNr].dev = dev;
	rndis_per_dev_params [configNr].stats = stats;
	rndis_per_dev_params [configNr].filter = cdc_filter;
	
	return 0;
}

int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr)
{
	DEBUG("%s:\n", __FUNCTION__ );
	if (!vendorDescr) return -1;
	if (configNr >= RNDIS_MAX_CONFIGS) return -1;
	
	rndis_per_dev_params [configNr].vendorID = vendorID;
	rndis_per_dev_params [configNr].vendorDescr = vendorDescr;
	
	return 0;
}

int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed)
{
	DEBUG("%s: %u %u\n", __FUNCTION__, medium, speed);
	if (configNr >= RNDIS_MAX_CONFIGS) return -1;
	
	rndis_per_dev_params [configNr].medium = medium;
	rndis_per_dev_params [configNr].speed = speed;
	
	return 0;
}

void rndis_add_hdr (struct sk_buff *skb)
{
	struct rndis_packet_msg_type	*header;

	if (!skb)
		return;
	header = (void *) skb_push (skb, sizeof *header);
	memset (header, 0, sizeof *header);
	header->MessageType = __constant_cpu_to_le32(REMOTE_NDIS_PACKET_MSG);
	header->MessageLength = cpu_to_le32(skb->len);
	header->DataOffset = __constant_cpu_to_le32 (36);
	header->DataLength = cpu_to_le32(skb->len - sizeof *header);
}

void rndis_free_response (int configNr, u8 *buf)
{
	rndis_resp_t		*r;
	struct list_head	*act, *tmp;
	
	list_for_each_safe (act, tmp, 
			    &(rndis_per_dev_params [configNr].resp_queue))
	{
		r = list_entry (act, rndis_resp_t, list);
		if (r && r->buf == buf) {
			list_del (&r->list);
			kfree (r);
		}
	}
}

u8 *rndis_get_next_response (int configNr, u32 *length)
{
	rndis_resp_t		*r;
	struct list_head 	*act, *tmp;
	
	if (!length) return NULL;
	
	list_for_each_safe (act, tmp, 
			    &(rndis_per_dev_params [configNr].resp_queue))
	{
		r = list_entry (act, rndis_resp_t, list);
		if (!r->send) {
			r->send = 1;
			*length = r->length;
			return r->buf;
		}
	}
	
	return NULL;
}

static rndis_resp_t *rndis_add_response (int configNr, u32 length)
{
	rndis_resp_t	*r;
	
	/* NOTE:  this gets copied into ether.c USB_BUFSIZ bytes ... */
	r = kmalloc (sizeof (rndis_resp_t) + length, GFP_ATOMIC);
	if (!r) return NULL;
	
	r->buf = (u8 *) (r + 1);
	r->length = length;
	r->send = 0;
	
	list_add_tail (&r->list, 
		       &(rndis_per_dev_params [configNr].resp_queue));
	return r;
}

int rndis_rm_hdr(struct sk_buff *skb)
{
	/* tmp points to a struct rndis_packet_msg_type */
	__le32		*tmp = (void *) skb->data;

	/* MessageType, MessageLength */
	if (__constant_cpu_to_le32(REMOTE_NDIS_PACKET_MSG)
			!= get_unaligned(tmp++))
		return -EINVAL;
	tmp++;

	/* DataOffset, DataLength */
	if (!skb_pull(skb, le32_to_cpu(get_unaligned(tmp++))
			+ 8 /* offset of DataOffset */))
		return -EOVERFLOW;
	skb_trim(skb, le32_to_cpu(get_unaligned(tmp++)));

	return 0;
}

#ifdef	CONFIG_USB_GADGET_DEBUG_FILES

static int rndis_proc_read (char *page, char **start, off_t off, int count, int *eof, 
		     void *data)
{
	char *out = page;
	int len;
	rndis_params *param = (rndis_params *) data;
	
	out += snprintf (out, count, 
			 "Config Nr. %d\n"
			 "used      : %s\n"
			 "state     : %s\n"
			 "medium    : 0x%08X\n"
			 "speed     : %d\n"
			 "cable     : %s\n"
			 "vendor ID : 0x%08X\n"
			 "vendor    : %s\n", 
			 param->confignr, (param->used) ? "y" : "n", 
			 ({ char *s = "?";
			 switch (param->state) {
			 case RNDIS_UNINITIALIZED:
				s = "RNDIS_UNINITIALIZED"; break;
			 case RNDIS_INITIALIZED:
				s = "RNDIS_INITIALIZED"; break;
			 case RNDIS_DATA_INITIALIZED:
				s = "RNDIS_DATA_INITIALIZED"; break;
			}; s; }),
			 param->medium, 
			 (param->media_state) ? 0 : param->speed*100, 
			 (param->media_state) ? "disconnected" : "connected",
			 param->vendorID, param->vendorDescr);      
	
	len = out - page;
	len -= off;
	
	if (len < count) {
		*eof = 1;
		if (len <= 0)
			return 0;
	} else
		len = count;
	
	*start = page + off;
	return len;
}

static int rndis_proc_write (struct file *file, const char __user *buffer, 
		      unsigned long count, void *data)
{
	rndis_params *p = data;
	u32 speed = 0;
	int i, fl_speed = 0;
	
	for (i = 0; i < count; i++) {
		char c;
		if (get_user(c, buffer))
			return -EFAULT;
		switch (c) {
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
			fl_speed = 1;
			speed = speed*10 + c - '0';
			break;
		case 'C':
		case 'c':
			rndis_signal_connect (p->confignr);
			break;
		case 'D':
		case 'd':
			rndis_signal_disconnect(p->confignr);
			break;
		default: 
			if (fl_speed) p->speed = speed;
			else DEBUG ("%c is not valid\n", c);
			break;
		}
		
		buffer++;
	}
	
	return count;
}

#define	NAME_TEMPLATE	"driver/rndis-%03d"

static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS];

#endif	/* CONFIG_USB_GADGET_DEBUG_FILES */


int __init rndis_init (void)
{
	u8 i;

	for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
#ifdef	CONFIG_USB_GADGET_DEBUG_FILES
		char name [20];

		sprintf (name, NAME_TEMPLATE, i);
		if (!(rndis_connect_state [i]
				= create_proc_entry (name, 0660, NULL))) 
		{
			DEBUG ("%s :remove entries", __FUNCTION__);
			while (i) {
				sprintf (name, NAME_TEMPLATE, --i);
				remove_proc_entry (name, NULL);
			}
			DEBUG ("\n");
			return -EIO;
		}

		rndis_connect_state [i]->nlink = 1;
		rndis_connect_state [i]->write_proc = rndis_proc_write;
		rndis_connect_state [i]->read_proc = rndis_proc_read;
		rndis_connect_state [i]->data = (void *)
				(rndis_per_dev_params + i);
#endif
		rndis_per_dev_params [i].confignr = i;
		rndis_per_dev_params [i].used = 0;
		rndis_per_dev_params [i].state = RNDIS_UNINITIALIZED;
		rndis_per_dev_params [i].media_state
				= NDIS_MEDIA_STATE_DISCONNECTED;
		INIT_LIST_HEAD (&(rndis_per_dev_params [i].resp_queue));
	}
	
	return 0;
}

void rndis_exit (void)
{
#ifdef	CONFIG_USB_GADGET_DEBUG_FILES
	u8 i;
	char name [20];
	
	for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
		sprintf (name, NAME_TEMPLATE, i);
		remove_proc_entry (name, NULL);
	}
#endif
}

⌨️ 快捷键说明

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