/*
 * query_serve.c - Contains functions that implement NetBIOS Query Serivce.
 *		   It also implements a remote name cache which is intended
 * 		   to cache a mapping between NetBIOS name and MAC address.
 *
 * Notes:
 *	- VRP in comments is the acronym of "Value Result Parameter"
 *	- Remote name cache needs upper layer considerations which makes its
 *	  impossible or hardly possible in other modules. Up to now only Session
 * 	  service uses this mechanism to speed-up connection establishment.
 *
 * Copyright (c) 1997 by Procom Technology,Inc.
 *
 * This program can be redistributed or modified under the terms of the 
 * GNU General Public License as published by the Free Software Foundation.
 * This program is distributed without any warranty or implied warranty
 * of merchantability or fitness for a particular purpose.
 *
 * See the GNU General Public License for more details.
 *
 */
 

#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <linux/skbuff.h>
#include <linux/netbeui.h>



static void nbqs_timer_function(unsigned long);

/* These functions are Query State Transition handlers */
static int nbqs_name_query_in_initial(query_t *);
static int nbqs_name_find_in_initial(query_t *);
static int nbqs_retry_timeout_in_all(query_t *);
static int nbqs_response_timeout_in_all(query_t *);
static int nbqs_name_recognized_in_qrywait(query_t *);
static int nbqs_name_recognized_in_findwait(query_t *);
static int nbqs_end_query_in_name_recognized(query_t *);


static unsigned short int nbqs_correlator= 0;
#define nbqs_next_correlator() (++nbqs_correlator)

static query_t *query_list= NULL;


/*
 * Remote name cache definition
 */  
struct rnc_struct {
	__u8			name[NB_NAME_LEN];
	__u8			mac[6];
	struct device * 	dev;
	unsigned long int	time_stamp;
	struct rnc_struct *	prev;
	struct rnc_struct *	next;
};
typedef struct rnc_struct rnc_t;

static rnc_t * rnc_list= NULL;
static int rnc_count= 0;



/*
 * Query Service State Machine definition
 */
typedef int (* query_event_handler_t)(query_t *);

struct event_struct {
	query_state_t  next_state;
	query_event_handler_t event_handler;
};

static struct event_struct 
query_state_table[4][6]= {
			/* NB_QURY_INITIAL */
{
{NB_QURY_QRYWAIT, nbqs_name_query_in_initial},	/* NB_QURY_NAME_QUERY */  
{NB_QURY_FINDWAIT, nbqs_name_find_in_initial},	/* NB_QURY_NAME_FIND */
{-1, NULL},					/* NB_QURY_RETRY_TIMEOUT */ 
{-1, NULL},					/* NB_QURY_RESPONSE_TIMEOUT */
{-1, NULL},					/* NB_QURY_NAME_RECOGNIZED */
{-1, NULL},					/* NB_QURY_END_QURY */
},
			/* NB_QURY_QRYWAIT */
{
{-1, NULL},					/* NB_QURY_NAME_QUERY */  
{-1, NULL},					/* NB_QURY_NAME_FIND */
{NB_QURY_QRYWAIT, nbqs_retry_timeout_in_all},	/* NB_QURY_RETRY_TIMEOUT */ 
{NB_QURY_INITIAL, nbqs_response_timeout_in_all},	/* NB_QURY_RESPONSE_TIMEOUT */
{NB_QURY_RECOGNIZED, nbqs_name_recognized_in_qrywait},	/* NB_QURY_NAME_RECOGNIZED */
{-1, NULL},					/* NB_QURY_END_QURY */
},
			/* NB_QURY_FINDWAIT */
{
{-1, NULL},					/* NB_QURY_NAME_QUERY */  
{-1, NULL},					/* NB_QURY_NAME_FIND */
{NB_QURY_FINDWAIT, nbqs_retry_timeout_in_all},	/* NB_QURY_RETRY_TIMEOUT */ 
{NB_QURY_INITIAL, nbqs_response_timeout_in_all},/* NB_QURY_RESPONSE_TIMEOUT */
{NB_QURY_FINDWAIT, nbqs_name_recognized_in_findwait},	/* NB_QURY_NAME_RECOGNIZED */
{-1, NULL},					/* NB_QURY_END_QURY */
},
			/* NB_QURY_RECOGNIZED */
{
{-1, NULL},					/* NB_QURY_NAME_QUERY */  
{-1, NULL},					/* NB_QURY_NAME_FIND */
{-1, NULL},					/* NB_QURY_RETRY_TIMEOUT */ 
{-1, NULL},					/* NB_QURY_RESPONSE_TIMEOUT */
{-1, NULL},					/* NB_QURY_NAME_RECOGNIZED */
{NB_QURY_INITIAL, nbqs_end_query_in_name_recognized},	/* NB_QURY_END_QURY */
}
};



/*
 * Query service state machine functions
 * Implementing remote name cache
 */

/*
 * Function: nbqs_remove_rnc
 *	Removes a rnc_t entry from remote name cache list rnc_list
 *
 * Parameters:
 *	nb_rnc	: pointer to rnc_t entry to remove
 *
 * Returns: none
 */
static inline void
nbqs_remove_rnc(rnc_t *nb_rnc)
{
	if (nb_rnc->next != NULL)
		nb_rnc->next->prev= nb_rnc->prev;
	
	if (nb_rnc->prev != NULL)
		nb_rnc->prev->next= nb_rnc->next;
	else
		rnc_list= nb_rnc->next;

	return;
}


/*
 * Function: nbqs_find_rnc
 *	Finds a rnc_t entry in remote name cache list for a specific NetBIOS
 *	16 byte name.
 *
 * Parameters:
 *	name	: NetBIOS name of remote node to find its rnc_t 
 *
 * Returns: 
 *	NULL	: if no matching entry found in remote cache name list
 *	non-NULL: pointer to rnc_t in remote_cache name list 
 */
static inline rnc_t *
nbqs_find_rnc(char *name)
{
	rnc_t *nb_rnc= rnc_list;

	while (nb_rnc)
		{
		if (memcmp(nb_rnc->name, name, NB_NAME_LEN) == 0)
			{
			nb_rnc->time_stamp= jiffies;
			return nb_rnc;
			}
		nb_rnc= nb_rnc->next;
		}

	return NULL;
}


/*
 * Function: nbqs_cleanup_rnc
 *	This routine will throw thoese cache entries who live in cache more 
 *	than a specific time (measured in jiffies) <NB_QURY_CACHE_LIVING_TIME>.
 *
 * Parameters: none
 *
 * Returns: none
 *
 * Notes:
 *	- For the sake of efficiency, remote name cache list wont grow more 
 *	  than a specific size <NB_QURY_MAX_CACHE_ENTRIES>. Note that this is
 *	  just a high water mark. Refer to nbqs_add_rnc
 */
static inline void
nbqs_cleanup_rnc(void)
{
	rnc_t *nb_rnc= rnc_list;

	while (nb_rnc)
		{
		if ((jiffies - nb_rnc->time_stamp) > NB_QURY_CACHE_LIVING_TIME)
			{
			rnc_t *tmp_rnc= nb_rnc;

			nb_rnc= nb_rnc->next;
			nbqs_remove_rnc(tmp_rnc);
			kfree(tmp_rnc);
			}
		else
			{
			rnc_count++;
			nb_rnc= nb_rnc->next;
			}
		}
}


/*
 * Function: nbqs_add_rnc
 *	Adds/Updates an entry to remote name cache list
 *
 * Parameters:
 *	name	: pointer to NetBIOS name of remote node
 *	dev	: pointer to device structure node is connected via
 *	mac	: pointer to MAC address of remote node
 *
 * Returns: none
 *	
 * Notes:
 *	- If an rnc_t corresponding to NetBIOS name exists in cache it is
 *	  updated else a new entry is created.
 *	- This routine handles cache grow beyond NB_QURY_MAX_CACHE_ENTRIES
 *	  but does not guarantee to preserve the entry count below the value
 *	- This routine does not guarantee adding memory since it may fail
 *	  to allocate memory. This does not affect the system consistency.
 */
void
nbqs_add_rnc(char *name, struct device *dev, unsigned char *mac)
{
	rnc_t *nb_rnc= nbqs_find_rnc(name);

	if (nb_rnc)
		{
		memcpy(nb_rnc->mac, mac, 6);
		nb_rnc->dev= dev;
		nb_rnc->time_stamp= jiffies;
		}

	if (rnc_count >= NB_QURY_MAX_CACHE_ENTRIES)
		nbqs_cleanup_rnc();

	nb_rnc= kmalloc(sizeof(rnc_t), GFP_KERNEL);
	if (!nb_rnc)
		return;
		
	rnc_count++;
		
	memcpy(nb_rnc->name, name, NB_NAME_LEN);
	memcpy(nb_rnc->mac, mac, 6);
	nb_rnc->dev= dev;
	nb_rnc->time_stamp= jiffies;

	if (rnc_list)
		{
		rnc_list->prev= nb_rnc;
		nb_rnc->next= rnc_list;
		nb_rnc->prev= NULL;
		rnc_list= nb_rnc;
		}
	else
		{
		rnc_list= nb_rnc;
		nb_rnc->next= nb_rnc->prev= NULL;
		}

	return;
}


/*
 * Function: nbqs_delete_rnc
 *	This is a wrapper to nbqs_remove_rnc which both remove entry from
 *	remote name cahche and deallocates memory. 
 *
 * Parameters:
 *	name	: NetBIOS name of remote node to remove its rnc_t 
 *
 * Returns: none
 */
void
nbqs_delete_rnc(char *name)
{
	rnc_t *nb_rnc= nbqs_find_rnc(name);

	if (nb_rnc)
		{
		nbqs_remove_rnc(nb_rnc);
		kfree(nb_rnc);
		rnc_count--;
		}

	return;
}

/*
 * Query service state machine functions
 * Implementing general functions
 */

/*
 * Function: nbqs_alloc_query
 *	Allocates a query_t structure and does completely initialize all fields
 *
 * Parameters: none
 *
 * Returns: 
 *	NULL	: if can not allocate memory for query_t or its sk_buff
 *	non-NULL: pointer to query_t 
 *	
 * Notes:
 *	- An skb is attached to each new query for query processing purposes
 *	- query timer is initialized but not started.
 *	- The call to memset does implicitly initialize all fields. Those 
 *	  fileds that need explicit non-zero initialization are manipulated
 *	  afterwards.
 */
static inline query_t *
nbqs_alloc_query(void)
{
	int name_dgram_len;
	query_t *nb_query= kmalloc(sizeof(query_t), GFP_KERNEL);

	if (!nb_query)
		return NULL;

	/* Implicitly initialize all fields */
	memset(nb_query, 0, sizeof(query_t));
	init_timer(&nb_query->timer);
	nb_query->timer.data= (unsigned long)nb_query;
	nb_query->timer.function= nbqs_timer_function;

	/* Allocate query skb */
	/* All commands use same length */
	name_dgram_len= nb_command_header_len[NAME_QUERY];  
	nb_query->skb= alloc_skb(CALC_DG_SKBLEN(MAC_B_HEADLEN, name_dgram_len),
								GFP_KERNEL);
	if (!nb_query->skb)
		{
		kfree(nb_query);
		return NULL;
		}
	skb_reserve(nb_query->skb, LLCMAC_UIB_HEADLEN());

#ifdef _NB_TR_DBG_
printk("nbqs_alloc_query 1>>> name_dgram_len=%d\n", name_dgram_len);
#endif
	skb_put(nb_query->skb, name_dgram_len);
#ifdef _NB_TR_DBG_
printk("nbqs_alloc_query 2>>> OK!\n");
#endif

	nb_query->skb->free= 1;
	nb_query->skb->dev= NULL;

	return nb_query;
}


/*
 * Function: nbqs_free_query
 *	Deallocates memory used for a query_t and its sk_buff
 *
 * Parameters:
 *	nb_query : pointer to query_t memory to be freed
 *
 * Returns: none
 */
static inline void
nbqs_free_query(query_t *nb_query)
{
	kfree_skb(nb_query->skb, 0);
	kfree(nb_query);
}


/*
 * Function: nbqs_add_query_to_list
 *	Inserts a previously allocated/initialized query_t into queyr list
 *
 * Parameters:
 *	nb_query : pointer to query_t to insert
 *
 * Returns: 
 *	0 	 : always returns zero
 */
static inline int
nbqs_add_query_to_list( query_t *nb_query)
{
	nb_query->next= query_list;
	query_list= nb_query;

	return 0;
}


/*
 * Function: nbqs_remove_query_from_list
 *	Removes a query_t from query list
 *
 * Parameters:
 *	nb_query : pointer to query_t to remove
 *
 * Returns: none
 */
static inline void
nbqs_remove_query_from_list( query_t *nb_query)
{
	query_t *entry= query_list;
	query_t *prev_entry= NULL;

	while (entry)
		{
		if (entry == nb_query)
			{
			if (prev_entry)
				prev_entry->next= entry->next;
			else
				query_list= entry->next;
			return;
			}
		prev_entry= entry;
		entry= entry->next;
		}
	return;
}


/*
 * Function: nbqs_find_correlator
 *	finds a query_t in query_list, which has transmitted frames with
 *	specific correlator.
 *
 * Parameters:
 *	correlator : a sixteen bit integer which contains the response 
 *	             correlator of input frame. It should be machted against
 *		     xmit correlator of frames sent by the element.
 *
 * Returns: 
 *	NULL	   : if no query_t element found with the requested characteristic.
 *	non-NULL   : pointer to matching query_t with the requested characteristic.
 */
static query_t *
nbqs_find_correlator(unsigned short correlator)
{
	query_t *nb_query= query_list;
	
	while (nb_query)
		{
		if (nb_query->resp_correlator == correlator)
			return nb_query;
		nb_query= nb_query->next;
		}
	return NULL;
}


/*
 * Function: nbqs_boradcast_name_query
 *	Prepares a NetBIOS NAME QUERY frames and nbll_uisends it to network.
 *
 * Parameters:
 *	nb_query : pointer to query_t element which the frame should be built from
 *
 * Returns: 
 *	0 	 : if frame is successfully boradcatsed to network
 *	non-zero : if frame transmission encountered an error (usually at NDI layer)
 *	
 * Notes:
 *	- Since ADD NAME QUERY frames are retransmitted in timed intervals, it 
 *	  is considered to build frame once, but transmit it multiple times.
 *	  having built frames in each retransmission does generate multiple
 *	  correlators and does frustrate processing responses.
 */
static int 
nbqs_broadcast_name_query(query_t *nb_query)
{
	if (nb_query->retries == 0)
		{
		dgram_t *hdr= (dgram_t *)nb_query->skb->data;
	
		hdr->length= nb_command_header_len[NAME_QUERY];
		hdr->delimiter= NB_DELIMITER;
		hdr->command= NAME_QUERY;
		hdr->data1= 0;
		
		if (nb_query->lsn > 0)
			hdr->data2= CALL_DATA2(nb_query->calling_name->type, 
			                       nb_query->lsn);
		else
			hdr->data2= 0;

		hdr->xmit_correlator= 0;
		hdr->resp_correlator= nb_query->resp_correlator= nbqs_next_correlator();
		memcpy(hdr->dest_name, nb_query->called_name, NB_NAME_LEN);
		
		if (nb_query->lsn > 0)
			memcpy(hdr->source_name, nb_query->calling_name->name, NB_NAME_LEN);
		else
			memset(hdr->source_name, 0, NB_NAME_LEN);
			
		}

	return nbll_uisend(NULL, nb_query->skb);
}


/*
 * Function: nbqs_handle_event
 *	This is the heart of Query Service State Machine, which performs a 
 *	transition from current state of query element to new state based
 *	on event occured and query state table contents.
 *
 * Parameters:
 *	event	: An integer of NB_QURY_* family that implies type of event
 *	nb_name	: pointer to query_t structure which the event occured on
 *
 * Returns: none
 *
 * Notes:
 *	- The state changes before actions be executed. This is due to
 *	  non deterministic behaviour of actions which may sleep the current
 *	  process, thus stopping the function in the mid-way. 
 *
 *	- Setting NBQS_DEBUG in Makefile causes the module to generate the code 
 *         that provide usefull information about transitions on every query element. 
 */
static void
nbqs_handle_event( query_event_t event, query_t *nb_query)
{
	struct event_struct *ev= &query_state_table[nb_query->state][event];
	unsigned char old_state;

#ifdef NBQS_DEBUG
	printk("NBQS event handler (%s): EVENT=%u on STATE=%u\n", nb_query->called_name, event, nb_query->state);
#endif NBQS_DEBUG

	if ((ev) && (ev->event_handler))
		{
		old_state= nb_query->state;
		nb_query->state= ev->next_state;
		if (ev->event_handler(nb_query) != 0)
			nb_query->state= old_state;
		}

#ifdef NBQS_DEBUG
	printk("NBQS event handler (%s): NEW STATE=%u\n", nb_query->called_name, nb_query->state);
#endif NBQS_DEBUG

	return;
}



/*
 * Function: nbqs_timer_function
 *	This is the callback function triggered upon expiration of name 
 *	retransmittion timer. It just injects an event into state machine for
 *	its link.
 *
 * Parameters:
 *	input	: pointer to query_t structure whose timer is expired.
 *
 * Returns: none
 */
static void 
nbqs_timer_function(unsigned long input)
{
	query_t *nb_query= (query_t *)input;

	if (nb_query->retries < NB_TRANSMIT_COUNT)
		{
		nbqs_handle_event(NB_QURY_RETRY_TIMEOUT, nb_query);
		return;
		}
		
	nbqs_handle_event(NB_QURY_RESPONSE_TIMEOUT, nb_query);
}


/*
 * Query service state machine functions
 * Implementing transition actions
 */

/*
 * Function: nbqs_xxxx_in_ssss
 *	The section below contains functions that implement actions needed
 *	to  legally transit from one state to another.
 *
 * Parameters:
 *	nb_query: pointer to query_t structure which the actions are to be 
 *		  applied to
 *
 * Returns: 
 *	0	: if all actions are done successfully
 *	non-zero: if one of actions failed
 *
 * Note:
 *	- For the sake of simplicity, the actions are automatically rollbacked 
 *	  in each function, if an action in transition fails. The design documents
 *	  do not cover these parts of code.
 */


static int
nbqs_name_query_in_initial(query_t *nb_query)
{
	unsigned long flags;

	nb_query->retries= 0;
	nb_query->responses= 0;

	if (nbqs_broadcast_name_query(nb_query) != 0)
		return -ENOMEM;

	nb_query->retries++;

	nb_query->timer.expires= jiffies+ NB_TRANSMIT_TIMEOUT;
	add_timer(&nb_query->timer);

	save_flags(flags);
	cli();

	nbqs_add_query_to_list(nb_query);

	sleep_on(&nb_query->waitq);

	restore_flags(flags);

	return 0;
}


static int 
nbqs_name_find_in_initial(query_t *nb_query)
{    
	unsigned long flags;

	nb_query->retries= 0;
	nb_query->responses= 0;
	nb_query->buff_ofs= 0;

	if (nbqs_broadcast_name_query(nb_query) != 0)
		return -ENOMEM;

	nb_query->retries++;

	nb_query->timer.expires= jiffies+ NB_TRANSMIT_TIMEOUT;
	add_timer(&nb_query->timer);
	
	save_flags(flags);
	cli();

	nbqs_add_query_to_list(nb_query);

	sleep_on(&nb_query->waitq);

	restore_flags(flags);

	return 0;
}


static int
nbqs_retry_timeout_in_all(query_t *nb_query)
{
	nb_query->timer.expires= jiffies+ NB_TRANSMIT_TIMEOUT;
	add_timer(&nb_query->timer);

	if (nbqs_broadcast_name_query(nb_query) != 0)
		return -ENOMEM;
		
	nb_query->retries++; 
   
	return 0;
}


static int
nbqs_response_timeout_in_all(query_t *nb_query)
{
	wake_up(&nb_query->waitq);

	nbqs_remove_query_from_list(nb_query);

	return 0;
}


static int 
nbqs_name_recognized_in_qrywait(query_t * nb_query)
{
	del_timer(&nb_query->timer);

	wake_up(&nb_query->waitq);

	nbqs_remove_query_from_list(nb_query);

	return 0;
}


static int 
nbqs_name_recognized_in_findwait(query_t * nb_query)
{
	/* Here we should add the MAC header to buffer, considering unicity of resp */
	int  i;
	struct device * dev;

	char * mac_buff= nb_query->mac_buff;

	for (i=0; i<nb_query->buff_ofs; i++)
		{
		dev= nb_query->dev_buff[i];

		if ((nb_query->remote_dev == dev) &&
		    (memcmp(nb_query->remote_mac, mac_buff, dev->addr_len) == 0))
			return 0;

		mac_buff += dev->addr_len;
		}

	if (nb_query->buff_ofs < nb_query->buff_len)
		{
		dev= nb_query->remote_dev;
		nb_query->dev_buff [nb_query->buff_ofs]= dev;
		memcpy(mac_buff, nb_query->remote_mac, dev->addr_len);
		nb_query->buff_ofs++;
		}

	return 0;
}


static int 
nbqs_end_query_in_name_recognized(query_t * nb_query)
{
	return 0;
}



/*
 * Query service state machine functions
 * Implementing interface functions
 */

/*
 * Function: nbqs_query_name
 *	Does the first step of session establishment process and determines
 *	remote session number, Largest Frame Bits and response correlator for
 *	further negotiations.
 *
 * Parameters:
 *	called_name    : pointer to NetBIOS name of remote node 
 *	calling_name   : pointer to name_t elemnent registered locally
 *	lsn	       : local session number
 *	rsn	       : (VRP) pointer to remote session number 
 *			  valid if return value is zero
 *	lfb            : (VRP) pointer to Largest Frame Bits (for support of
 *	                 Token-Ring)
 *	xmit_correlator: (VRP) pointer to xmit correlator found in NAME RESPONSE
 *			  valid if return value is zero
 *
 * Returns: 
 *	0 	       : if query was successful
 *	-EINVAL	       : if called_name is not a valid NetBIOS name
 *	-ENOMEM	       : if memory allocation for query_t element failed
 *	-ETIMEDOUT     : if remote node did not respond
 */
int 
nbqs_query_name(char *called_name, name_t *calling_name, unsigned char lsn, 
        unsigned char *rsn, unsigned char *lfb, unsigned short *xmit_correlator)
{
	query_t *nb_query;

	if (VALIDATE_NAME(called_name) != 0)
		return -EINVAL;

	nb_query= nbqs_alloc_query();
	if (!nb_query)
		return -ENOMEM;

	nb_query->state= NB_QURY_INITIAL;
	nb_query->calling_name= calling_name;
	memcpy(nb_query->called_name, called_name, NB_NAME_LEN);
	nb_query->lsn= lsn;

	nbqs_handle_event(NB_QURY_NAME_QUERY, nb_query);

	if (nb_query->state != NB_QURY_RECOGNIZED)
		{
		nbqs_free_query(nb_query);
		return -ETIMEDOUT;
		}

	*rsn= nb_query->rsn;
	*lfb = nb_query->tr_lfb;
	*xmit_correlator= nb_query->xmit_correlator;

	nbqs_handle_event(NB_QURY_END_QUERY, nb_query);

	nbqs_free_query(nb_query);
	return 0;
}


/*
 * Function: nbqs_find_name
 *	This is NAME FIND interface which determines MAC address of a remote
 *	node with a specific name. It also finds the device which connects
 *	the host with remote node.
 *
 * Parameters:
 *	called_name : pointer to NetBIOS name of remote node 
 *	mac_buff    : pointer to array of MAC buffers 
 *	dev_buff    : pointer to array of device pointers
 *	buff_len    : maximum number of entries in mac_buff and dev_buff arrays
 *
 * Returns: 
 *	>= 0	    : count of dev_buff and mac_buff entries corresponding to
 *		      to nodes which own called_name
 *	-EINVAL	    : if called_name is not a valid NetBIOS name
 *	-ENOMEM	    : if memory allocation for query_t element failed
 *
 * Notes:
 *	- This routines is the point in which RNC cache is used for NetBIOS
 *	  name resolution. The consistency of cache contents depends on 
 *	  upper layers which insert/delete cache entries.
 */
int 
nbqs_find_name(char *called_name, char *mac_buff, struct device **dev_buff, 
		int buff_len)
{
	int names_found;
	query_t *nb_query;
	rnc_t *nb_rnc;

	if (VALIDATE_NAME(called_name) != 0)
		return -EINVAL;

	nb_rnc= nbqs_find_rnc(called_name);
	if(nb_rnc)
		{
		*dev_buff= nb_rnc->dev;
		memcpy(mac_buff, nb_rnc->mac, 6);
		return 1;
		}

	nb_query= nbqs_alloc_query();
	if (!nb_query)
		return -ENOMEM;

	nb_query->state= NB_QURY_INITIAL;
	nb_query->calling_name= NULL;
	memcpy(nb_query->called_name, called_name, NB_NAME_LEN);
	nb_query->lsn= 0;
	nb_query->mac_buff= mac_buff;
	nb_query->dev_buff= dev_buff;
	nb_query->buff_len= buff_len;

	nbqs_handle_event(NB_QURY_NAME_FIND, nb_query);

	names_found= nb_query->buff_ofs;

	nbqs_free_query(nb_query);

	if (names_found > 0)
		nbqs_add_rnc(called_name, *dev_buff, mac_buff);

	return names_found;
}


void  
nbqs_get_name_recognized(struct sk_buff *skb, unsigned char *remote_mac)
{
	dgram_t *dgram= (dgram_t *)skb->data;
	unsigned short data2= dgram->data2;
	query_t *nb_query= nbqs_find_correlator(dgram->xmit_correlator);

	/* If it does not match a query */
	if (!nb_query)
		{
		kfree_skb(skb, 0);
		return;
		}

	nb_query->rsn= CALL_SS(data2);
	nb_query->remote_dev= skb->dev;
	memcpy(nb_query->remote_mac, remote_mac, 6);
	nb_query->xmit_correlator= dgram->resp_correlator;
	nb_query->tr_lfb = skb->proto_priv[0]; /* Token Ring support */
	nb_query->responses++;

	/* We should also set mac_header */
	nbqs_handle_event(NB_QURY_NAME_RECOGNIZED, nb_query);

	kfree_skb(skb, 0);
	return;
}
