linux/net/irda/irnet/irnet_irda.c

1886 lines
56 KiB
C
Raw Normal View History

/*
* IrNET protocol module : Synchronous PPP over an IrDA socket.
*
* Jean II - HPL `00 - <jt@hpl.hp.com>
*
* This file implement the IRDA interface of IrNET.
* Basically, we sit on top of IrTTP. We set up IrTTP, IrIAS properly,
* and exchange frames with IrTTP.
*/
#include "irnet_irda.h" /* Private header */
#include <linux/sched.h>
#include <linux/seq_file.h>
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h percpu.h is included by sched.h and module.h and thus ends up being included when building most .c files. percpu.h includes slab.h which in turn includes gfp.h making everything defined by the two files universally available and complicating inclusion dependencies. percpu.h -> slab.h dependency is about to be removed. Prepare for this change by updating users of gfp and slab facilities include those headers directly instead of assuming availability. As this conversion needs to touch large number of source files, the following script is used as the basis of conversion. http://userweb.kernel.org/~tj/misc/slabh-sweep.py The script does the followings. * Scan files for gfp and slab usages and update includes such that only the necessary includes are there. ie. if only gfp is used, gfp.h, if slab is used, slab.h. * When the script inserts a new include, it looks at the include blocks and try to put the new include such that its order conforms to its surrounding. It's put in the include block which contains core kernel includes, in the same order that the rest are ordered - alphabetical, Christmas tree, rev-Xmas-tree or at the end if there doesn't seem to be any matching order. * If the script can't find a place to put a new include (mostly because the file doesn't have fitting include block), it prints out an error message indicating which .h file needs to be added to the file. The conversion was done in the following steps. 1. The initial automatic conversion of all .c files updated slightly over 4000 files, deleting around 700 includes and adding ~480 gfp.h and ~3000 slab.h inclusions. The script emitted errors for ~400 files. 2. Each error was manually checked. Some didn't need the inclusion, some needed manual addition while adding it to implementation .h or embedding .c file was more appropriate for others. This step added inclusions to around 150 files. 3. The script was run again and the output was compared to the edits from #2 to make sure no file was left behind. 4. Several build tests were done and a couple of problems were fixed. e.g. lib/decompress_*.c used malloc/free() wrappers around slab APIs requiring slab.h to be added manually. 5. The script was run on all .h files but without automatically editing them as sprinkling gfp.h and slab.h inclusions around .h files could easily lead to inclusion dependency hell. Most gfp.h inclusion directives were ignored as stuff from gfp.h was usually wildly available and often used in preprocessor macros. Each slab.h inclusion directive was examined and added manually as necessary. 6. percpu.h was updated not to include slab.h. 7. Build test were done on the following configurations and failures were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my distributed build env didn't work with gcov compiles) and a few more options had to be turned off depending on archs to make things build (like ipr on powerpc/64 which failed due to missing writeq). * x86 and x86_64 UP and SMP allmodconfig and a custom test config. * powerpc and powerpc64 SMP allmodconfig * sparc and sparc64 SMP allmodconfig * ia64 SMP allmodconfig * s390 SMP allmodconfig * alpha SMP allmodconfig * um on x86_64 SMP allmodconfig 8. percpu.h modifications were reverted so that it could be applied as a separate patch and serve as bisection point. Given the fact that I had only a couple of failures from tests on step 6, I'm fairly confident about the coverage of this conversion patch. If there is a breakage, it's likely to be something in one of the arch headers which should be easily discoverable easily on most builds of the specific arch. Signed-off-by: Tejun Heo <tj@kernel.org> Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 08:04:11 +00:00
#include <linux/slab.h>
#include <asm/unaligned.h>
/*
* PPP disconnect work: we need to make sure we're in
* process context when calling ppp_unregister_channel().
*/
static void irnet_ppp_disconnect(struct work_struct *work)
{
irnet_socket * self =
container_of(work, irnet_socket, disconnect_work);
if (self == NULL)
return;
/*
* If we were connected, cleanup & close the PPP
* channel, which will kill pppd (hangup) and the rest.
*/
if (self->ppp_open && !self->ttp_open && !self->ttp_connect) {
ppp_unregister_channel(&self->chan);
self->ppp_open = 0;
}
}
/************************* CONTROL CHANNEL *************************/
/*
* When ppp is not active, /dev/irnet act as a control channel.
* Writing allow to set up the IrDA destination of the IrNET channel,
* and any application may be read events happening on IrNET...
*/
/*------------------------------------------------------------------*/
/*
* Post an event to the control channel...
* Put the event in the log, and then wait all process blocked on read
* so they can read the log...
*/
static void
irnet_post_event(irnet_socket * ap,
irnet_event event,
__u32 saddr,
__u32 daddr,
char * name,
__u16 hints)
{
int index; /* In the log */
DENTER(CTRL_TRACE, "(ap=0x%p, event=%d, daddr=%08x, name=``%s'')\n",
ap, event, daddr, name);
/* Protect this section via spinlock.
* Note : as we are the only event producer, we only need to exclude
* ourself when touching the log, which is nice and easy.
*/
spin_lock_bh(&irnet_events.spinlock);
/* Copy the event in the log */
index = irnet_events.index;
irnet_events.log[index].event = event;
irnet_events.log[index].daddr = daddr;
irnet_events.log[index].saddr = saddr;
/* Try to copy IrDA nickname */
if(name)
strcpy(irnet_events.log[index].name, name);
else
irnet_events.log[index].name[0] = '\0';
/* Copy hints */
irnet_events.log[index].hints.word = hints;
/* Try to get ppp unit number */
if((ap != (irnet_socket *) NULL) && (ap->ppp_open))
irnet_events.log[index].unit = ppp_unit_number(&ap->chan);
else
irnet_events.log[index].unit = -1;
/* Increment the index
* Note that we increment the index only after the event is written,
* to make sure that the readers don't get garbage... */
irnet_events.index = (index + 1) % IRNET_MAX_EVENTS;
DEBUG(CTRL_INFO, "New event index is %d\n", irnet_events.index);
/* Spin lock end */
spin_unlock_bh(&irnet_events.spinlock);
/* Now : wake up everybody waiting for events... */
wake_up_interruptible_all(&irnet_events.rwait);
DEXIT(CTRL_TRACE, "\n");
}
/************************* IRDA SUBROUTINES *************************/
/*
* These are a bunch of subroutines called from other functions
* down there, mostly common code or to improve readability...
*
* Note : we duplicate quite heavily some routines of af_irda.c,
* because our input structure (self) is quite different
* (struct irnet instead of struct irda_sock), which make sharing
* the same code impossible (at least, without templates).
*/
/*------------------------------------------------------------------*/
/*
* Function irda_open_tsap (self)
*
* Open local Transport Service Access Point (TSAP)
*
* Create a IrTTP instance for us and set all the IrTTP callbacks.
*/
static inline int
irnet_open_tsap(irnet_socket * self)
{
notify_t notify; /* Callback structure */
DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self);
DABORT(self->tsap != NULL, -EBUSY, IRDA_SR_ERROR, "Already busy !\n");
/* Initialize IrTTP callbacks to be used by the IrDA stack */
irda_notify_init(&notify);
notify.connect_confirm = irnet_connect_confirm;
notify.connect_indication = irnet_connect_indication;
notify.disconnect_indication = irnet_disconnect_indication;
notify.data_indication = irnet_data_indication;
/*notify.udata_indication = NULL;*/
notify.flow_indication = irnet_flow_indication;
notify.status_indication = irnet_status_indication;
notify.instance = self;
strlcpy(notify.name, IRNET_NOTIFY_NAME, sizeof(notify.name));
/* Open an IrTTP instance */
self->tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT,
&notify);
DABORT(self->tsap == NULL, -ENOMEM,
IRDA_SR_ERROR, "Unable to allocate TSAP !\n");
/* Remember which TSAP selector we actually got */
self->stsap_sel = self->tsap->stsap_sel;
DEXIT(IRDA_SR_TRACE, " - tsap=0x%p, sel=0x%X\n",
self->tsap, self->stsap_sel);
return 0;
}
/*------------------------------------------------------------------*/
/*
* Function irnet_ias_to_tsap (self, result, value)
*
* Examine an IAS object and extract TSAP
*
* We do an IAP query to find the TSAP associated with the IrNET service.
* When IrIAP pass us the result of the query, this function look at
* the return values to check for failures and extract the TSAP if
* possible.
* Also deallocate value
* The failure is in self->errno
* Return TSAP or -1
*/
static inline __u8
irnet_ias_to_tsap(irnet_socket * self,
int result,
struct ias_value * value)
{
__u8 dtsap_sel = 0; /* TSAP we are looking for */
DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self);
/* By default, no error */
self->errno = 0;
/* Check if request succeeded */
switch(result)
{
/* Standard errors : service not available */
case IAS_CLASS_UNKNOWN:
case IAS_ATTRIB_UNKNOWN:
DEBUG(IRDA_SR_INFO, "IAS object doesn't exist ! (%d)\n", result);
self->errno = -EADDRNOTAVAIL;
break;
/* Other errors, most likely IrDA stack failure */
default :
DEBUG(IRDA_SR_INFO, "IAS query failed ! (%d)\n", result);
self->errno = -EHOSTUNREACH;
break;
/* Success : we got what we wanted */
case IAS_SUCCESS:
break;
}
/* Check what was returned to us */
if(value != NULL)
{
/* What type of argument have we got ? */
switch(value->type)
{
case IAS_INTEGER:
DEBUG(IRDA_SR_INFO, "result=%d\n", value->t.integer);
if(value->t.integer != -1)
/* Get the remote TSAP selector */
dtsap_sel = value->t.integer;
else
self->errno = -EADDRNOTAVAIL;
break;
default:
self->errno = -EADDRNOTAVAIL;
DERROR(IRDA_SR_ERROR, "bad type ! (0x%X)\n", value->type);
break;
}
/* Cleanup */
irias_delete_value(value);
}
else /* value == NULL */
{
/* Nothing returned to us - usually result != SUCCESS */
if(!(self->errno))
{
DERROR(IRDA_SR_ERROR,
"IrDA bug : result == SUCCESS && value == NULL\n");
self->errno = -EHOSTUNREACH;
}
}
DEXIT(IRDA_SR_TRACE, "\n");
/* Return the TSAP */
return dtsap_sel;
}
/*------------------------------------------------------------------*/
/*
* Function irnet_find_lsap_sel (self)
*
* Try to lookup LSAP selector in remote LM-IAS
*
* Basically, we start a IAP query, and then go to sleep. When the query
* return, irnet_getvalue_confirm will wake us up, and we can examine the
* result of the query...
* Note that in some case, the query fail even before we go to sleep,
* creating some races...
*/
static inline int
irnet_find_lsap_sel(irnet_socket * self)
{
DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self);
/* This should not happen */
DABORT(self->iriap, -EBUSY, IRDA_SR_ERROR, "busy with a previous query.\n");
/* Create an IAP instance, will be closed in irnet_getvalue_confirm() */
self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
irnet_getvalue_confirm);
/* Treat unexpected signals as disconnect */
self->errno = -EHOSTUNREACH;
/* Query remote LM-IAS */
iriap_getvaluebyclass_request(self->iriap, self->rsaddr, self->daddr,
IRNET_SERVICE_NAME, IRNET_IAS_VALUE);
/* The above request is non-blocking.
* After a while, IrDA will call us back in irnet_getvalue_confirm()
* We will then call irnet_ias_to_tsap() and finish the
* connection procedure */
DEXIT(IRDA_SR_TRACE, "\n");
return 0;
}
/*------------------------------------------------------------------*/
/*
* Function irnet_connect_tsap (self)
*
* Initialise the TTP socket and initiate TTP connection
*
*/
static inline int
irnet_connect_tsap(irnet_socket * self)
{
int err;
DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self);
/* Open a local TSAP (an IrTTP instance) */
err = irnet_open_tsap(self);
if(err != 0)
{
clear_bit(0, &self->ttp_connect);
DERROR(IRDA_SR_ERROR, "connect aborted!\n");
return err;
}
/* Connect to remote device */
err = irttp_connect_request(self->tsap, self->dtsap_sel,
self->rsaddr, self->daddr, NULL,
self->max_sdu_size_rx, NULL);
if(err != 0)
{
clear_bit(0, &self->ttp_connect);
DERROR(IRDA_SR_ERROR, "connect aborted!\n");
return err;
}
/* The above call is non-blocking.
* After a while, the IrDA stack will either call us back in
* irnet_connect_confirm() or irnet_disconnect_indication()
* See you there ;-) */
DEXIT(IRDA_SR_TRACE, "\n");
return err;
}
/*------------------------------------------------------------------*/
/*
* Function irnet_discover_next_daddr (self)
*
* Query the IrNET TSAP of the next device in the log.
*
* Used in the TSAP discovery procedure.
*/
static inline int
irnet_discover_next_daddr(irnet_socket * self)
{
/* Close the last instance of IrIAP, and open a new one.
* We can't reuse the IrIAP instance in the IrIAP callback */
if(self->iriap)
{
iriap_close(self->iriap);
self->iriap = NULL;
}
/* Create a new IAP instance */
self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
irnet_discovervalue_confirm);
if(self->iriap == NULL)
return -ENOMEM;
/* Next discovery - before the call to avoid races */
self->disco_index++;
/* Check if we have one more address to try */
if(self->disco_index < self->disco_number)
{
/* Query remote LM-IAS */
iriap_getvaluebyclass_request(self->iriap,
self->discoveries[self->disco_index].saddr,
self->discoveries[self->disco_index].daddr,
IRNET_SERVICE_NAME, IRNET_IAS_VALUE);
/* The above request is non-blocking.
* After a while, IrDA will call us back in irnet_discovervalue_confirm()
* We will then call irnet_ias_to_tsap() and come back here again... */
return 0;
}
else
return 1;
}
/*------------------------------------------------------------------*/
/*
* Function irnet_discover_daddr_and_lsap_sel (self)
*
* This try to find a device with the requested service.
*
* Initiate a TSAP discovery procedure.
* It basically look into the discovery log. For each address in the list,
* it queries the LM-IAS of the device to find if this device offer
* the requested service.
* If there is more than one node supporting the service, we complain
* to the user (it should move devices around).
* If we find one node which have the requested TSAP, we connect to it.
*
* This function just start the whole procedure. It request the discovery
* log and submit the first IAS query.
* The bulk of the job is handled in irnet_discovervalue_confirm()
*
* Note : this procedure fails if there is more than one device in range
* on the same dongle, because IrLMP doesn't disconnect the LAP when the
* last LSAP is closed. Moreover, we would need to wait the LAP
* disconnection...
*/
static inline int
irnet_discover_daddr_and_lsap_sel(irnet_socket * self)
{
int ret;
DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self);
/* Ask lmp for the current discovery log */
self->discoveries = irlmp_get_discoveries(&self->disco_number, self->mask,
DISCOVERY_DEFAULT_SLOTS);
/* Check if the we got some results */
if(self->discoveries == NULL)
{
self->disco_number = -1;
clear_bit(0, &self->ttp_connect);
DRETURN(-ENETUNREACH, IRDA_SR_INFO, "No Cachelog...\n");
}
DEBUG(IRDA_SR_INFO, "Got the log (0x%p), size is %d\n",
self->discoveries, self->disco_number);
/* Start with the first discovery */
self->disco_index = -1;
self->daddr = DEV_ADDR_ANY;
/* This will fail if the log is empty - this is non-blocking */
ret = irnet_discover_next_daddr(self);
if(ret)
{
/* Close IAP */
if(self->iriap)
iriap_close(self->iriap);
self->iriap = NULL;
/* Cleanup our copy of the discovery log */
kfree(self->discoveries);
self->discoveries = NULL;
clear_bit(0, &self->ttp_connect);
DRETURN(-ENETUNREACH, IRDA_SR_INFO, "Cachelog empty...\n");
}
/* Follow me in irnet_discovervalue_confirm() */
DEXIT(IRDA_SR_TRACE, "\n");
return 0;
}
/*------------------------------------------------------------------*/
/*
* Function irnet_dname_to_daddr (self)
*
* Convert an IrDA nickname to a valid IrDA address
*
* It basically look into the discovery log until there is a match.
*/
static inline int
irnet_dname_to_daddr(irnet_socket * self)
{
struct irda_device_info *discoveries; /* Copy of the discovery log */
int number; /* Number of nodes in the log */
int i;
DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self);
/* Ask lmp for the current discovery log */
discoveries = irlmp_get_discoveries(&number, 0xffff,
DISCOVERY_DEFAULT_SLOTS);
/* Check if the we got some results */
if(discoveries == NULL)
DRETURN(-ENETUNREACH, IRDA_SR_INFO, "Cachelog empty...\n");
/*
* Now, check all discovered devices (if any), and connect
* client only about the services that the client is
* interested in...
*/
for(i = 0; i < number; i++)
{
/* Does the name match ? */
if(!strncmp(discoveries[i].info, self->rname, NICKNAME_MAX_LEN))
{
/* Yes !!! Get it.. */
self->daddr = discoveries[i].daddr;
DEBUG(IRDA_SR_INFO, "discovered device ``%s'' at address 0x%08x.\n",
self->rname, self->daddr);
kfree(discoveries);
DEXIT(IRDA_SR_TRACE, "\n");
return 0;
}
}
/* No luck ! */
DEBUG(IRDA_SR_INFO, "cannot discover device ``%s'' !!!\n", self->rname);
kfree(discoveries);
return -EADDRNOTAVAIL;
}
/************************* SOCKET ROUTINES *************************/
/*
* This are the main operations on IrNET sockets, basically to create
* and destroy IrNET sockets. These are called from the PPP part...
*/
/*------------------------------------------------------------------*/
/*
* Create a IrNET instance : just initialise some parameters...
*/
int
irda_irnet_create(irnet_socket * self)
{
DENTER(IRDA_SOCK_TRACE, "(self=0x%p)\n", self);
self->magic = IRNET_MAGIC; /* Paranoia */
self->ttp_open = 0; /* Prevent higher layer from accessing IrTTP */
self->ttp_connect = 0; /* Not connecting yet */
self->rname[0] = '\0'; /* May be set via control channel */
self->rdaddr = DEV_ADDR_ANY; /* May be set via control channel */
self->rsaddr = DEV_ADDR_ANY; /* May be set via control channel */
self->daddr = DEV_ADDR_ANY; /* Until we get connected */
self->saddr = DEV_ADDR_ANY; /* Until we get connected */
self->max_sdu_size_rx = TTP_SAR_UNBOUND;
/* Register as a client with IrLMP */
self->ckey = irlmp_register_client(0, NULL, NULL, NULL);
#ifdef DISCOVERY_NOMASK
self->mask = 0xffff; /* For W2k compatibility */
#else /* DISCOVERY_NOMASK */
self->mask = irlmp_service_to_hint(S_LAN);
#endif /* DISCOVERY_NOMASK */
self->tx_flow = FLOW_START; /* Flow control from IrTTP */
INIT_WORK(&self->disconnect_work, irnet_ppp_disconnect);
DEXIT(IRDA_SOCK_TRACE, "\n");
return 0;
}
/*------------------------------------------------------------------*/
/*
* Connect to the other side :
* o convert device name to an address
* o find the socket number (dlsap)
* o Establish the connection
*
* Note : We no longer mimic af_irda. The IAS query for finding the TSAP
* is done asynchronously, like the TTP connection. This allow us to
* call this function from any context (not only process).
* The downside is that following what's happening in there is tricky
* because it involve various functions all over the place...
*/
int
irda_irnet_connect(irnet_socket * self)
{
int err;
DENTER(IRDA_SOCK_TRACE, "(self=0x%p)\n", self);
/* Check if we are already trying to connect.
* Because irda_irnet_connect() can be called directly by pppd plus
* packet retries in ppp_generic and connect may take time, plus we may
* race with irnet_connect_indication(), we need to be careful there... */
if(test_and_set_bit(0, &self->ttp_connect))
DRETURN(-EBUSY, IRDA_SOCK_INFO, "Already connecting...\n");
if((self->iriap != NULL) || (self->tsap != NULL))
DERROR(IRDA_SOCK_ERROR, "Socket not cleaned up...\n");
/* Insert ourselves in the hashbin so that the IrNET server can find us.
* Notes : 4th arg is string of 32 char max and must be null terminated
* When 4th arg is used (string), 3rd arg isn't (int)
* Can't re-insert (MUST remove first) so check for that... */
if((irnet_server.running) && (self->q.q_next == NULL))
{
spin_lock_bh(&irnet_server.spinlock);
hashbin_insert(irnet_server.list, (irda_queue_t *) self, 0, self->rname);
spin_unlock_bh(&irnet_server.spinlock);
DEBUG(IRDA_SOCK_INFO, "Inserted ``%s'' in hashbin...\n", self->rname);
}
/* If we don't have anything (no address, no name) */
if((self->rdaddr == DEV_ADDR_ANY) && (self->rname[0] == '\0'))
{
/* Try to find a suitable address */
if((err = irnet_discover_daddr_and_lsap_sel(self)) != 0)
DRETURN(err, IRDA_SOCK_INFO, "auto-connect failed!\n");
/* In most cases, the call above is non-blocking */
}
else
{
/* If we have only the name (no address), try to get an address */
if(self->rdaddr == DEV_ADDR_ANY)
{
if((err = irnet_dname_to_daddr(self)) != 0)
DRETURN(err, IRDA_SOCK_INFO, "name connect failed!\n");
}
else
/* Use the requested destination address */
self->daddr = self->rdaddr;
/* Query remote LM-IAS to find LSAP selector */
irnet_find_lsap_sel(self);
/* The above call is non blocking */
}
/* At this point, we are waiting for the IrDA stack to call us back,
* or we have already failed.
* We will finish the connection procedure in irnet_connect_tsap().
*/
DEXIT(IRDA_SOCK_TRACE, "\n");
return 0;
}
/*------------------------------------------------------------------*/
/*
* Function irda_irnet_destroy(self)
*
* Destroy irnet instance
*
* Note : this need to be called from a process context.
*/
void
irda_irnet_destroy(irnet_socket * self)
{
DENTER(IRDA_SOCK_TRACE, "(self=0x%p)\n", self);
if(self == NULL)
return;
/* Remove ourselves from hashbin (if we are queued in hashbin)
* Note : `irnet_server.running' protect us from calls in hashbin_delete() */
if((irnet_server.running) && (self->q.q_next != NULL))
{
struct irnet_socket * entry;
DEBUG(IRDA_SOCK_INFO, "Removing from hash..\n");
spin_lock_bh(&irnet_server.spinlock);
entry = hashbin_remove_this(irnet_server.list, (irda_queue_t *) self);
self->q.q_next = NULL;
spin_unlock_bh(&irnet_server.spinlock);
DASSERT(entry == self, , IRDA_SOCK_ERROR, "Can't remove from hash.\n");
}
/* If we were connected, post a message */
if(test_bit(0, &self->ttp_open))
{
/* Note : as the disconnect comes from ppp_generic, the unit number
* doesn't exist anymore when we post the event, so we need to pass
* NULL as the first arg... */
irnet_post_event(NULL, IRNET_DISCONNECT_TO,
self->saddr, self->daddr, self->rname, 0);
}
/* Prevent various IrDA callbacks from messing up things
* Need to be first */
clear_bit(0, &self->ttp_connect);
/* Prevent higher layer from accessing IrTTP */
clear_bit(0, &self->ttp_open);
/* Unregister with IrLMP */
irlmp_unregister_client(self->ckey);
/* Unregister with LM-IAS */
if(self->iriap)
{
iriap_close(self->iriap);
self->iriap = NULL;
}
/* Cleanup eventual discoveries from connection attempt or control channel */
if(self->discoveries != NULL)
{
/* Cleanup our copy of the discovery log */
kfree(self->discoveries);
self->discoveries = NULL;
}
/* Close our IrTTP connection */
if(self->tsap)
{
DEBUG(IRDA_SOCK_INFO, "Closing our TTP connection.\n");
irttp_disconnect_request(self->tsap, NULL, P_NORMAL);
irttp_close_tsap(self->tsap);
self->tsap = NULL;
}
self->stsap_sel = 0;
DEXIT(IRDA_SOCK_TRACE, "\n");
}
/************************** SERVER SOCKET **************************/
/*
* The IrNET service is composed of one server socket and a variable
* number of regular IrNET sockets. The server socket is supposed to
* handle incoming connections and redirect them to one IrNET sockets.
* It's a superset of the regular IrNET socket, but has a very distinct
* behaviour...
*/
/*------------------------------------------------------------------*/
/*
* Function irnet_daddr_to_dname (self)
*
* Convert an IrDA address to a IrDA nickname
*
* It basically look into the discovery log until there is a match.
*/
static inline int
irnet_daddr_to_dname(irnet_socket * self)
{
struct irda_device_info *discoveries; /* Copy of the discovery log */
int number; /* Number of nodes in the log */
int i;
DENTER(IRDA_SERV_TRACE, "(self=0x%p)\n", self);
/* Ask lmp for the current discovery log */
discoveries = irlmp_get_discoveries(&number, 0xffff,
DISCOVERY_DEFAULT_SLOTS);
/* Check if the we got some results */
if (discoveries == NULL)
DRETURN(-ENETUNREACH, IRDA_SERV_INFO, "Cachelog empty...\n");
/* Now, check all discovered devices (if any) */
for(i = 0; i < number; i++)
{
/* Does the name match ? */
if(discoveries[i].daddr == self->daddr)
{
/* Yes !!! Get it.. */
strlcpy(self->rname, discoveries[i].info, sizeof(self->rname));
self->rname[sizeof(self->rname) - 1] = '\0';
DEBUG(IRDA_SERV_INFO, "Device 0x%08x is in fact ``%s''.\n",
self->daddr, self->rname);
kfree(discoveries);
DEXIT(IRDA_SERV_TRACE, "\n");
return 0;
}
}
/* No luck ! */
DEXIT(IRDA_SERV_INFO, ": cannot discover device 0x%08x !!!\n", self->daddr);
kfree(discoveries);
return -EADDRNOTAVAIL;
}
/*------------------------------------------------------------------*/
/*
* Function irda_find_socket (self)
*
* Find the correct IrNET socket
*
* Look into the list of IrNET sockets and finds one with the right
* properties...
*/
static inline irnet_socket *
irnet_find_socket(irnet_socket * self)
{
irnet_socket * new = (irnet_socket *) NULL;
int err;
DENTER(IRDA_SERV_TRACE, "(self=0x%p)\n", self);
/* Get the addresses of the requester */
self->daddr = irttp_get_daddr(self->tsap);
self->saddr = irttp_get_saddr(self->tsap);
/* Try to get the IrDA nickname of the requester */
err = irnet_daddr_to_dname(self);
/* Protect access to the instance list */
spin_lock_bh(&irnet_server.spinlock);
/* So now, try to get an socket having specifically
* requested that nickname */
if(err == 0)
{
new = (irnet_socket *) hashbin_find(irnet_server.list,
0, self->rname);
if(new)
DEBUG(IRDA_SERV_INFO, "Socket 0x%p matches rname ``%s''.\n",
new, new->rname);
}
/* If no name matches, try to find an socket by the destination address */
/* It can be either the requested destination address (set via the
* control channel), or the current destination address if the
* socket is in the middle of a connection request */
if(new == (irnet_socket *) NULL)
{
new = (irnet_socket *) hashbin_get_first(irnet_server.list);
while(new !=(irnet_socket *) NULL)
{
/* Does it have the same address ? */
if((new->rdaddr == self->daddr) || (new->daddr == self->daddr))
{
/* Yes !!! Get it.. */
DEBUG(IRDA_SERV_INFO, "Socket 0x%p matches daddr %#08x.\n",
new, self->daddr);
break;
}
new = (irnet_socket *) hashbin_get_next(irnet_server.list);
}
}
/* If we don't have any socket, get the first unconnected socket */
if(new == (irnet_socket *) NULL)
{
new = (irnet_socket *) hashbin_get_first(irnet_server.list);
while(new !=(irnet_socket *) NULL)
{
/* Is it available ? */
if(!(test_bit(0, &new->ttp_open)) && (new->rdaddr == DEV_ADDR_ANY) &&
(new->rname[0] == '\0') && (new->ppp_open))
{
/* Yes !!! Get it.. */
DEBUG(IRDA_SERV_INFO, "Socket 0x%p is free.\n",
new);
break;
}
new = (irnet_socket *) hashbin_get_next(irnet_server.list);
}
}
/* Spin lock end */
spin_unlock_bh(&irnet_server.spinlock);
DEXIT(IRDA_SERV_TRACE, " - new = 0x%p\n", new);
return new;
}
/*------------------------------------------------------------------*/
/*
* Function irda_connect_socket (self)
*
* Connect an incoming connection to the socket
*
*/
static inline int
irnet_connect_socket(irnet_socket * server,
irnet_socket * new,
struct qos_info * qos,
__u32 max_sdu_size,
__u8 max_header_size)
{
DENTER(IRDA_SERV_TRACE, "(server=0x%p, new=0x%p)\n",
server, new);
/* Now attach up the new socket */
new->tsap = irttp_dup(server->tsap, new);
DABORT(new->tsap == NULL, -1, IRDA_SERV_ERROR, "dup failed!\n");
/* Set up all the relevant parameters on the new socket */
new->stsap_sel = new->tsap->stsap_sel;
new->dtsap_sel = new->tsap->dtsap_sel;
new->saddr = irttp_get_saddr(new->tsap);
new->daddr = irttp_get_daddr(new->tsap);
new->max_header_size = max_header_size;
new->max_sdu_size_tx = max_sdu_size;
new->max_data_size = max_sdu_size;
#ifdef STREAM_COMPAT
/* If we want to receive "stream sockets" */
if(max_sdu_size == 0)
new->max_data_size = irttp_get_max_seg_size(new->tsap);
#endif /* STREAM_COMPAT */
/* Clean up the original one to keep it in listen state */
irttp_listen(server->tsap);
/* Send a connection response on the new socket */
irttp_connect_response(new->tsap, new->max_sdu_size_rx, NULL);
/* Allow PPP to send its junk over the new socket... */
set_bit(0, &new->ttp_open);
/* Not connecting anymore, and clean up last possible remains
* of connection attempts on the socket */
clear_bit(0, &new->ttp_connect);
if(new->iriap)
{
iriap_close(new->iriap);
new->iriap = NULL;
}
if(new->discoveries != NULL)
{
kfree(new->discoveries);
new->discoveries = NULL;
}
#ifdef CONNECT_INDIC_KICK
/* As currently we don't block packets in ppp_irnet_send() while passive,
* this is not really needed...
* Also, not doing it give IrDA a chance to finish the setup properly
* before being swamped with packets... */
ppp_output_wakeup(&new->chan);
#endif /* CONNECT_INDIC_KICK */
/* Notify the control channel */
irnet_post_event(new, IRNET_CONNECT_FROM,
new->saddr, new->daddr, server->rname, 0);
DEXIT(IRDA_SERV_TRACE, "\n");
return 0;
}
/*------------------------------------------------------------------*/
/*
* Function irda_disconnect_server (self)
*
* Cleanup the server socket when the incoming connection abort
*
*/
static inline void
irnet_disconnect_server(irnet_socket * self,
struct sk_buff *skb)
{
DENTER(IRDA_SERV_TRACE, "(self=0x%p)\n", self);
/* Put the received packet in the black hole */
kfree_skb(skb);
#ifdef FAIL_SEND_DISCONNECT
/* Tell the other party we don't want to be connected */
/* Hum... Is it the right thing to do ? And do we need to send
* a connect response before ? It looks ok without this... */
irttp_disconnect_request(self->tsap, NULL, P_NORMAL);
#endif /* FAIL_SEND_DISCONNECT */
/* Notify the control channel (see irnet_find_socket()) */
irnet_post_event(NULL, IRNET_REQUEST_FROM,
self->saddr, self->daddr, self->rname, 0);
/* Clean up the server to keep it in listen state */
irttp_listen(self->tsap);
DEXIT(IRDA_SERV_TRACE, "\n");
}
/*------------------------------------------------------------------*/
/*
* Function irda_setup_server (self)
*
* Create a IrTTP server and set it up...
*
* Register the IrLAN hint bit, create a IrTTP instance for us,
* set all the IrTTP callbacks and create an IrIAS entry...
*/
static inline int
irnet_setup_server(void)
{
__u16 hints;
DENTER(IRDA_SERV_TRACE, "()\n");
/* Initialise the regular socket part of the server */
irda_irnet_create(&irnet_server.s);
/* Open a local TSAP (an IrTTP instance) for the server */
irnet_open_tsap(&irnet_server.s);
/* PPP part setup */
irnet_server.s.ppp_open = 0;
irnet_server.s.chan.private = NULL;
irnet_server.s.file = NULL;
/* Get the hint bit corresponding to IrLAN */
/* Note : we overload the IrLAN hint bit. As it is only a "hint", and as
* we provide roughly the same functionality as IrLAN, this is ok.
* In fact, the situation is similar as JetSend overloading the Obex hint
*/
hints = irlmp_service_to_hint(S_LAN);
#ifdef ADVERTISE_HINT
/* Register with IrLMP as a service (advertise our hint bit) */
irnet_server.skey = irlmp_register_service(hints);
#endif /* ADVERTISE_HINT */
/* Register with LM-IAS (so that people can connect to us) */
irnet_server.ias_obj = irias_new_object(IRNET_SERVICE_NAME, jiffies);
irias_add_integer_attrib(irnet_server.ias_obj, IRNET_IAS_VALUE,
irnet_server.s.stsap_sel, IAS_KERNEL_ATTR);
irias_insert_object(irnet_server.ias_obj);
#ifdef DISCOVERY_EVENTS
/* Tell IrLMP we want to be notified of newly discovered nodes */
irlmp_update_client(irnet_server.s.ckey, hints,
irnet_discovery_indication, irnet_expiry_indication,
(void *) &irnet_server.s);
#endif
DEXIT(IRDA_SERV_TRACE, " - self=0x%p\n", &irnet_server.s);
return 0;
}
/*------------------------------------------------------------------*/
/*
* Function irda_destroy_server (self)
*
* Destroy the IrTTP server...
*
* Reverse of the previous function...
*/
static inline void
irnet_destroy_server(void)
{
DENTER(IRDA_SERV_TRACE, "()\n");
#ifdef ADVERTISE_HINT
/* Unregister with IrLMP */
irlmp_unregister_service(irnet_server.skey);
#endif /* ADVERTISE_HINT */
/* Unregister with LM-IAS */
if(irnet_server.ias_obj)
irias_delete_object(irnet_server.ias_obj);
/* Cleanup the socket part */
irda_irnet_destroy(&irnet_server.s);
DEXIT(IRDA_SERV_TRACE, "\n");
}
/************************ IRDA-TTP CALLBACKS ************************/
/*
* When we create a IrTTP instance, we pass to it a set of callbacks
* that IrTTP will call in case of various events.
* We take care of those events here.
*/
/*------------------------------------------------------------------*/
/*
* Function irnet_data_indication (instance, sap, skb)
*
* Received some data from TinyTP. Just queue it on the receive queue
*
*/
static int
irnet_data_indication(void * instance,
void * sap,
struct sk_buff *skb)
{
irnet_socket * ap = (irnet_socket *) instance;
unsigned char * p;
int code = 0;
DENTER(IRDA_TCB_TRACE, "(self/ap=0x%p, skb=0x%p)\n",
ap, skb);
DASSERT(skb != NULL, 0, IRDA_CB_ERROR, "skb is NULL !!!\n");
/* Check is ppp is ready to receive our packet */
if(!ap->ppp_open)
{
DERROR(IRDA_CB_ERROR, "PPP not ready, dropping packet...\n");
/* When we return error, TTP will need to requeue the skb and
* will stop the sender. IrTTP will stall until we send it a
* flow control request... */
return -ENOMEM;
}
/* strip address/control field if present */
p = skb->data;
if((p[0] == PPP_ALLSTATIONS) && (p[1] == PPP_UI))
{
/* chop off address/control */
if(skb->len < 3)
goto err_exit;
p = skb_pull(skb, 2);
}
/* decompress protocol field if compressed */
if(p[0] & 1)
{
/* protocol is compressed */
skb_push(skb, 1)[0] = 0;
}
else
if(skb->len < 2)
goto err_exit;
/* pass to generic ppp layer */
/* Note : how do I know if ppp can accept or not the packet ? This is
* essential if I want to manage flow control smoothly... */
ppp_input(&ap->chan, skb);
DEXIT(IRDA_TCB_TRACE, "\n");
return 0;
err_exit:
DERROR(IRDA_CB_ERROR, "Packet too small, dropping...\n");
kfree_skb(skb);
ppp_input_error(&ap->chan, code);
return 0; /* Don't return an error code, only for flow control... */
}
/*------------------------------------------------------------------*/
/*
* Function irnet_disconnect_indication (instance, sap, reason, skb)
*
* Connection has been closed. Chech reason to find out why
*
* Note : there are many cases where we come here :
* o attempted to connect, timeout
* o connected, link is broken, LAP has timeout
* o connected, other side close the link
* o connection request on the server not handled
*/
static void
irnet_disconnect_indication(void * instance,
void * sap,
LM_REASON reason,
struct sk_buff *skb)
{
irnet_socket * self = (irnet_socket *) instance;
int test_open;
int test_connect;
DENTER(IRDA_TCB_TRACE, "(self=0x%p)\n", self);
DASSERT(self != NULL, , IRDA_CB_ERROR, "Self is NULL !!!\n");
/* Don't care about it, but let's not leak it */
if(skb)
dev_kfree_skb(skb);
/* Prevent higher layer from accessing IrTTP */
test_open = test_and_clear_bit(0, &self->ttp_open);
/* Not connecting anymore...
* (note : TSAP is open, so IAP callbacks are no longer pending...) */
test_connect = test_and_clear_bit(0, &self->ttp_connect);
/* If both self->ttp_open and self->ttp_connect are NULL, it mean that we
* have a race condition with irda_irnet_destroy() or
* irnet_connect_indication(), so don't mess up tsap...
*/
if(!(test_open || test_connect))
{
DERROR(IRDA_CB_ERROR, "Race condition detected...\n");
return;
}
/* If we were active, notify the control channel */
if(test_open)
irnet_post_event(self, IRNET_DISCONNECT_FROM,
self->saddr, self->daddr, self->rname, 0);
else
/* If we were trying to connect, notify the control channel */
if((self->tsap) && (self != &irnet_server.s))
irnet_post_event(self, IRNET_NOANSWER_FROM,
self->saddr, self->daddr, self->rname, 0);
/* Close our IrTTP connection, cleanup tsap */
if((self->tsap) && (self != &irnet_server.s))
{
DEBUG(IRDA_CB_INFO, "Closing our TTP connection.\n");
irttp_close_tsap(self->tsap);
self->tsap = NULL;
}
/* Cleanup the socket in case we want to reconnect in ppp_output_wakeup() */
self->stsap_sel = 0;
self->daddr = DEV_ADDR_ANY;
self->tx_flow = FLOW_START;
/* Deal with the ppp instance if it's still alive */
if(self->ppp_open)
{
if(test_open)
{
/* ppp_unregister_channel() wants a user context. */
schedule_work(&self->disconnect_work);
}
else
{
/* If we were trying to connect, flush (drain) ppp_generic
* Tx queue (most often we have blocked it), which will
* trigger an other attempt to connect. If we are passive,
* this will empty the Tx queue after last try. */
ppp_output_wakeup(&self->chan);
}
}
DEXIT(IRDA_TCB_TRACE, "\n");
}
/*------------------------------------------------------------------*/
/*
* Function irnet_connect_confirm (instance, sap, qos, max_sdu_size, skb)
*
* Connections has been confirmed by the remote device
*
*/
static void
irnet_connect_confirm(void * instance,
void * sap,
struct qos_info *qos,
__u32 max_sdu_size,
__u8 max_header_size,
struct sk_buff *skb)
{
irnet_socket * self = (irnet_socket *) instance;
DENTER(IRDA_TCB_TRACE, "(self=0x%p)\n", self);
/* Check if socket is closing down (via irda_irnet_destroy()) */
if(! test_bit(0, &self->ttp_connect))
{
DERROR(IRDA_CB_ERROR, "Socket no longer connecting. Ouch !\n");
return;
}
/* How much header space do we need to reserve */
self->max_header_size = max_header_size;
/* IrTTP max SDU size in transmit direction */
self->max_sdu_size_tx = max_sdu_size;
self->max_data_size = max_sdu_size;
#ifdef STREAM_COMPAT
if(max_sdu_size == 0)
self->max_data_size = irttp_get_max_seg_size(self->tsap);
#endif /* STREAM_COMPAT */
/* At this point, IrLMP has assigned our source address */
self->saddr = irttp_get_saddr(self->tsap);
/* Allow higher layer to access IrTTP */
set_bit(0, &self->ttp_open);
clear_bit(0, &self->ttp_connect); /* Not racy, IrDA traffic is serial */
/* Give a kick in the ass of ppp_generic so that he sends us some data */
ppp_output_wakeup(&self->chan);
/* Check size of received packet */
if(skb->len > 0)
{
#ifdef PASS_CONNECT_PACKETS
DEBUG(IRDA_CB_INFO, "Passing connect packet to PPP.\n");
/* Try to pass it to PPP */
irnet_data_indication(instance, sap, skb);
#else /* PASS_CONNECT_PACKETS */
DERROR(IRDA_CB_ERROR, "Dropping non empty packet.\n");
kfree_skb(skb); /* Note : will be optimised with other kfree... */
#endif /* PASS_CONNECT_PACKETS */
}
else
kfree_skb(skb);
/* Notify the control channel */
irnet_post_event(self, IRNET_CONNECT_TO,
self->saddr, self->daddr, self->rname, 0);
DEXIT(IRDA_TCB_TRACE, "\n");
}
/*------------------------------------------------------------------*/
/*
* Function irnet_flow_indication (instance, sap, flow)
*
* Used by TinyTP to tell us if it can accept more data or not
*
*/
static void
irnet_flow_indication(void * instance,
void * sap,
LOCAL_FLOW flow)
{
irnet_socket * self = (irnet_socket *) instance;
LOCAL_FLOW oldflow = self->tx_flow;
DENTER(IRDA_TCB_TRACE, "(self=0x%p, flow=%d)\n", self, flow);
/* Update our state */
self->tx_flow = flow;
/* Check what IrTTP want us to do... */
switch(flow)
{
case FLOW_START:
DEBUG(IRDA_CB_INFO, "IrTTP wants us to start again\n");
/* Check if we really need to wake up PPP */
if(oldflow == FLOW_STOP)
ppp_output_wakeup(&self->chan);
else
DEBUG(IRDA_CB_INFO, "But we were already transmitting !!!\n");
break;
case FLOW_STOP:
DEBUG(IRDA_CB_INFO, "IrTTP wants us to slow down\n");
break;
default:
DEBUG(IRDA_CB_INFO, "Unknown flow command!\n");
break;
}
DEXIT(IRDA_TCB_TRACE, "\n");
}
/*------------------------------------------------------------------*/
/*
* Function irnet_status_indication (instance, sap, reason, skb)
*
* Link (IrLAP) status report.
*
*/
static void
irnet_status_indication(void * instance,
LINK_STATUS link,
LOCK_STATUS lock)
{
irnet_socket * self = (irnet_socket *) instance;
DENTER(IRDA_TCB_TRACE, "(self=0x%p)\n", self);
DASSERT(self != NULL, , IRDA_CB_ERROR, "Self is NULL !!!\n");
/* We can only get this event if we are connected */
switch(link)
{
case STATUS_NO_ACTIVITY:
irnet_post_event(self, IRNET_BLOCKED_LINK,
self->saddr, self->daddr, self->rname, 0);
break;
default:
DEBUG(IRDA_CB_INFO, "Unknown status...\n");
}
DEXIT(IRDA_TCB_TRACE, "\n");
}
/*------------------------------------------------------------------*/
/*
* Function irnet_connect_indication(instance, sap, qos, max_sdu_size, userdata)
*
* Incoming connection
*
* In theory, this function is called only on the server socket.
* Some other node is attempting to connect to the IrNET service, and has
* sent a connection request on our server socket.
* We just redirect the connection to the relevant IrNET socket.
*
* Note : we also make sure that between 2 irnet nodes, there can
* exist only one irnet connection.
*/
static void
irnet_connect_indication(void * instance,
void * sap,
struct qos_info *qos,
__u32 max_sdu_size,
__u8 max_header_size,
struct sk_buff *skb)
{
irnet_socket * server = &irnet_server.s;
irnet_socket * new = (irnet_socket *) NULL;
DENTER(IRDA_TCB_TRACE, "(server=0x%p)\n", server);
DASSERT(instance == &irnet_server, , IRDA_CB_ERROR,
"Invalid instance (0x%p) !!!\n", instance);
DASSERT(sap == irnet_server.s.tsap, , IRDA_CB_ERROR, "Invalid sap !!!\n");
/* Try to find the most appropriate IrNET socket */
new = irnet_find_socket(server);
/* After all this hard work, do we have an socket ? */
if(new == (irnet_socket *) NULL)
{
DEXIT(IRDA_CB_INFO, ": No socket waiting for this connection.\n");
irnet_disconnect_server(server, skb);
return;
}
/* Is the socket already busy ? */
if(test_bit(0, &new->ttp_open))
{
DEXIT(IRDA_CB_INFO, ": Socket already connected.\n");
irnet_disconnect_server(server, skb);
return;
}
/* The following code is a bit tricky, so need comments ;-)
*/
/* If ttp_connect is set, the socket is trying to connect to the other
* end and may have sent a IrTTP connection request and is waiting for
* a connection response (that may never come).
* Now, the pain is that the socket may have opened a tsap and is
* waiting on it, while the other end is trying to connect to it on
* another tsap.
* Because IrNET can be peer to peer, we need to workaround this.
* Furthermore, the way the irnetd script is implemented, the
* target will create a second IrNET connection back to the
* originator and expect the originator to bind this new connection
* to the original PPPD instance.
* And of course, if we don't use irnetd, we can have a race when
* both side try to connect simultaneously, which could leave both
* connections half closed (yuck).
* Conclusions :
* 1) The "originator" must accept the new connection and get rid
* of the old one so that irnetd works
* 2) One side must deny the new connection to avoid races,
* but both side must agree on which side it is...
* Most often, the originator is primary at the LAP layer.
* Jean II
*/
/* Now, let's look at the way I wrote the test...
* We need to clear up the ttp_connect flag atomically to prevent
* irnet_disconnect_indication() to mess up the tsap we are going to close.
* We want to clear the ttp_connect flag only if we close the tsap,
* otherwise we will never close it, so we need to check for primary
* *before* doing the test on the flag.
* And of course, ALLOW_SIMULT_CONNECT can disable this entirely...
* Jean II
*/
/* Socket already connecting ? On primary ? */
if(0
#ifdef ALLOW_SIMULT_CONNECT
|| ((irttp_is_primary(server->tsap) == 1) && /* primary */
(test_and_clear_bit(0, &new->ttp_connect)))
#endif /* ALLOW_SIMULT_CONNECT */
)
{
DERROR(IRDA_CB_ERROR, "Socket already connecting, but going to reuse it !\n");
/* Cleanup the old TSAP if necessary - IrIAP will be cleaned up later */
if(new->tsap != NULL)
{
/* Close the old connection the new socket was attempting,
* so that we can hook it up to the new connection.
* It's now safe to do it... */
irttp_close_tsap(new->tsap);
new->tsap = NULL;
}
}
else
{
/* Three options :
* 1) socket was not connecting or connected : ttp_connect should be 0.
* 2) we don't want to connect the socket because we are secondary or
* ALLOW_SIMULT_CONNECT is undefined. ttp_connect should be 1.
* 3) we are half way in irnet_disconnect_indication(), and it's a
* nice race condition... Fortunately, we can detect that by checking
* if tsap is still alive. On the other hand, we can't be in
* irda_irnet_destroy() otherwise we would not have found this
* socket in the hashbin.
* Jean II */
if((test_bit(0, &new->ttp_connect)) || (new->tsap != NULL))
{
/* Don't mess this socket, somebody else in in charge... */
DERROR(IRDA_CB_ERROR, "Race condition detected, socket in use, abort connect...\n");
irnet_disconnect_server(server, skb);
return;
}
}
/* So : at this point, we have a socket, and it is idle. Good ! */
irnet_connect_socket(server, new, qos, max_sdu_size, max_header_size);
/* Check size of received packet */
if(skb->len > 0)
{
#ifdef PASS_CONNECT_PACKETS
DEBUG(IRDA_CB_INFO, "Passing connect packet to PPP.\n");
/* Try to pass it to PPP */
irnet_data_indication(new, new->tsap, skb);
#else /* PASS_CONNECT_PACKETS */
DERROR(IRDA_CB_ERROR, "Dropping non empty packet.\n");
kfree_skb(skb); /* Note : will be optimised with other kfree... */
#endif /* PASS_CONNECT_PACKETS */
}
else
kfree_skb(skb);
DEXIT(IRDA_TCB_TRACE, "\n");
}
/********************** IRDA-IAS/LMP CALLBACKS **********************/
/*
* These are the callbacks called by other layers of the IrDA stack,
* mainly LMP for discovery and IAS for name queries.
*/
/*------------------------------------------------------------------*/
/*
* Function irnet_getvalue_confirm (result, obj_id, value, priv)
*
* Got answer from remote LM-IAS, just connect
*
* This is the reply to a IAS query we were doing to find the TSAP of
* the device we want to connect to.
* If we have found a valid TSAP, just initiate the TTP connection
* on this TSAP.
*/
static void
irnet_getvalue_confirm(int result,
__u16 obj_id,
struct ias_value *value,
void * priv)
{
irnet_socket * self = (irnet_socket *) priv;
DENTER(IRDA_OCB_TRACE, "(self=0x%p)\n", self);
DASSERT(self != NULL, , IRDA_OCB_ERROR, "Self is NULL !!!\n");
/* Check if already connected (via irnet_connect_socket())
* or socket is closing down (via irda_irnet_destroy()) */
if(! test_bit(0, &self->ttp_connect))
{
DERROR(IRDA_OCB_ERROR, "Socket no longer connecting. Ouch !\n");
return;
}
/* We probably don't need to make any more queries */
iriap_close(self->iriap);
self->iriap = NULL;
/* Post process the IAS reply */
self->dtsap_sel = irnet_ias_to_tsap(self, result, value);
/* If error, just go out */
if(self->errno)
{
clear_bit(0, &self->ttp_connect);
DERROR(IRDA_OCB_ERROR, "IAS connect failed ! (0x%X)\n", self->errno);
return;
}
DEBUG(IRDA_OCB_INFO, "daddr = %08x, lsap = %d, starting IrTTP connection\n",
self->daddr, self->dtsap_sel);
/* Start up TTP - non blocking */
irnet_connect_tsap(self);
DEXIT(IRDA_OCB_TRACE, "\n");
}
/*------------------------------------------------------------------*/
/*
* Function irnet_discovervalue_confirm (result, obj_id, value, priv)
*
* Handle the TSAP discovery procedure state machine.
* Got answer from remote LM-IAS, try next device
*
* We are doing a TSAP discovery procedure, and we got an answer to
* a IAS query we were doing to find the TSAP on one of the address
* in the discovery log.
*
* If we have found a valid TSAP for the first time, save it. If it's
* not the first time we found one, complain.
*
* If we have more addresses in the log, just initiate a new query.
* Note that those query may fail (see irnet_discover_daddr_and_lsap_sel())
*
* Otherwise, wrap up the procedure (cleanup), check if we have found
* any device and connect to it.
*/
static void
irnet_discovervalue_confirm(int result,
__u16 obj_id,
struct ias_value *value,
void * priv)
{
irnet_socket * self = (irnet_socket *) priv;
__u8 dtsap_sel; /* TSAP we are looking for */
DENTER(IRDA_OCB_TRACE, "(self=0x%p)\n", self);
DASSERT(self != NULL, , IRDA_OCB_ERROR, "Self is NULL !!!\n");
/* Check if already connected (via irnet_connect_socket())
* or socket is closing down (via irda_irnet_destroy()) */
if(! test_bit(0, &self->ttp_connect))
{
DERROR(IRDA_OCB_ERROR, "Socket no longer connecting. Ouch !\n");
return;
}
/* Post process the IAS reply */
dtsap_sel = irnet_ias_to_tsap(self, result, value);
/* Have we got something ? */
if(self->errno == 0)
{
/* We found the requested service */
if(self->daddr != DEV_ADDR_ANY)
{
DERROR(IRDA_OCB_ERROR, "More than one device in range supports IrNET...\n");
}
else
{
/* First time we found that one, save it ! */
self->daddr = self->discoveries[self->disco_index].daddr;
self->dtsap_sel = dtsap_sel;
}
}
/* If no failure */
if((self->errno == -EADDRNOTAVAIL) || (self->errno == 0))
{
int ret;
/* Search the next node */
ret = irnet_discover_next_daddr(self);
if(!ret)
{
/* In this case, the above request was non-blocking.
* We will return here after a while... */
return;
}
/* In this case, we have processed the last discovery item */
}
/* No more queries to be done (failure or last one) */
/* We probably don't need to make any more queries */
iriap_close(self->iriap);
self->iriap = NULL;
/* No more items : remove the log and signal termination */
DEBUG(IRDA_OCB_INFO, "Cleaning up log (0x%p)\n",
self->discoveries);
if(self->discoveries != NULL)
{
/* Cleanup our copy of the discovery log */
kfree(self->discoveries);
self->discoveries = NULL;
}
self->disco_number = -1;
/* Check out what we found */
if(self->daddr == DEV_ADDR_ANY)
{
self->daddr = DEV_ADDR_ANY;
clear_bit(0, &self->ttp_connect);
DEXIT(IRDA_OCB_TRACE, ": cannot discover IrNET in any device !!!\n");
return;
}
/* We have a valid address - just connect */
DEBUG(IRDA_OCB_INFO, "daddr = %08x, lsap = %d, starting IrTTP connection\n",
self->daddr, self->dtsap_sel);
/* Start up TTP - non blocking */
irnet_connect_tsap(self);
DEXIT(IRDA_OCB_TRACE, "\n");
}
#ifdef DISCOVERY_EVENTS
/*------------------------------------------------------------------*/
/*
* Function irnet_discovery_indication (discovery)
*
* Got a discovery indication from IrLMP, post an event
*
* Note : IrLMP take care of matching the hint mask for us, and also
* check if it is a "new" node for us...
*
* As IrLMP filter on the IrLAN hint bit, we get both IrLAN and IrNET
* nodes, so it's only at connection time that we will know if the
* node support IrNET, IrLAN or both. The other solution is to check
* in IAS the PNP ids and service name.
* Note : even if a node support IrNET (or IrLAN), it's no guarantee
* that we will be able to connect to it, the node might already be
* busy...
*
* One last thing : in some case, this function will trigger duplicate
* discovery events. On the other hand, we should catch all
* discoveries properly (i.e. not miss one). Filtering duplicate here
* is to messy, so we leave that to user space...
*/
static void
irnet_discovery_indication(discinfo_t * discovery,
DISCOVERY_MODE mode,
void * priv)
{
irnet_socket * self = &irnet_server.s;
DENTER(IRDA_OCB_TRACE, "(self=0x%p)\n", self);
DASSERT(priv == &irnet_server, , IRDA_OCB_ERROR,
"Invalid instance (0x%p) !!!\n", priv);
DEBUG(IRDA_OCB_INFO, "Discovered new IrNET/IrLAN node %s...\n",
discovery->info);
/* Notify the control channel */
irnet_post_event(NULL, IRNET_DISCOVER,
discovery->saddr, discovery->daddr, discovery->info,
get_unaligned((__u16 *)discovery->hints));
DEXIT(IRDA_OCB_TRACE, "\n");
}
/*------------------------------------------------------------------*/
/*
* Function irnet_expiry_indication (expiry)
*
* Got a expiry indication from IrLMP, post an event
*
* Note : IrLMP take care of matching the hint mask for us, we only
* check if it is a "new" node...
*/
static void
irnet_expiry_indication(discinfo_t * expiry,
DISCOVERY_MODE mode,
void * priv)
{
irnet_socket * self = &irnet_server.s;
DENTER(IRDA_OCB_TRACE, "(self=0x%p)\n", self);
DASSERT(priv == &irnet_server, , IRDA_OCB_ERROR,
"Invalid instance (0x%p) !!!\n", priv);
DEBUG(IRDA_OCB_INFO, "IrNET/IrLAN node %s expired...\n",
expiry->info);
/* Notify the control channel */
irnet_post_event(NULL, IRNET_EXPIRE,
expiry->saddr, expiry->daddr, expiry->info,
get_unaligned((__u16 *)expiry->hints));
DEXIT(IRDA_OCB_TRACE, "\n");
}
#endif /* DISCOVERY_EVENTS */
/*********************** PROC ENTRY CALLBACKS ***********************/
/*
* We create a instance in the /proc filesystem, and here we take care
* of that...
*/
#ifdef CONFIG_PROC_FS
static int
irnet_proc_show(struct seq_file *m, void *v)
{
irnet_socket * self;
char * state;
int i = 0;
/* Get the IrNET server information... */
seq_printf(m, "IrNET server - ");
seq_printf(m, "IrDA state: %s, ",
(irnet_server.running ? "running" : "dead"));
seq_printf(m, "stsap_sel: %02x, ", irnet_server.s.stsap_sel);
seq_printf(m, "dtsap_sel: %02x\n", irnet_server.s.dtsap_sel);
/* Do we need to continue ? */
if(!irnet_server.running)
return 0;
/* Protect access to the instance list */
spin_lock_bh(&irnet_server.spinlock);
/* Get the sockets one by one... */
self = (irnet_socket *) hashbin_get_first(irnet_server.list);
while(self != NULL)
{
/* Start printing info about the socket. */
seq_printf(m, "\nIrNET socket %d - ", i++);
/* First, get the requested configuration */
seq_printf(m, "Requested IrDA name: \"%s\", ", self->rname);
seq_printf(m, "daddr: %08x, ", self->rdaddr);
seq_printf(m, "saddr: %08x\n", self->rsaddr);
/* Second, get all the PPP info */
seq_printf(m, " PPP state: %s",
(self->ppp_open ? "registered" : "unregistered"));
if(self->ppp_open)
{
seq_printf(m, ", unit: ppp%d",
ppp_unit_number(&self->chan));
seq_printf(m, ", channel: %d",
ppp_channel_index(&self->chan));
seq_printf(m, ", mru: %d",
self->mru);
/* Maybe add self->flags ? Later... */
}
/* Then, get all the IrDA specific info... */
if(self->ttp_open)
state = "connected";
else
if(self->tsap != NULL)
state = "connecting";
else
if(self->iriap != NULL)
state = "searching";
else
if(self->ttp_connect)
state = "weird";
else
state = "idle";
seq_printf(m, "\n IrDA state: %s, ", state);
seq_printf(m, "daddr: %08x, ", self->daddr);
seq_printf(m, "stsap_sel: %02x, ", self->stsap_sel);
seq_printf(m, "dtsap_sel: %02x\n", self->dtsap_sel);
/* Next socket, please... */
self = (irnet_socket *) hashbin_get_next(irnet_server.list);
}
/* Spin lock end */
spin_unlock_bh(&irnet_server.spinlock);
return 0;
}
static int irnet_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, irnet_proc_show, NULL);
}
static const struct file_operations irnet_proc_fops = {
.owner = THIS_MODULE,
.open = irnet_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
#endif /* PROC_FS */
/********************** CONFIGURATION/CLEANUP **********************/
/*
* Initialisation and teardown of the IrDA part, called at module
* insertion and removal...
*/
/*------------------------------------------------------------------*/
/*
* Prepare the IrNET layer for operation...
*/
int __init
irda_irnet_init(void)
{
int err = 0;
DENTER(MODULE_TRACE, "()\n");
/* Pure paranoia - should be redundant */
memset(&irnet_server, 0, sizeof(struct irnet_root));
/* Setup start of irnet instance list */
irnet_server.list = hashbin_new(HB_NOLOCK);
DABORT(irnet_server.list == NULL, -ENOMEM,
MODULE_ERROR, "Can't allocate hashbin!\n");
/* Init spinlock for instance list */
spin_lock_init(&irnet_server.spinlock);
/* Initialise control channel */
init_waitqueue_head(&irnet_events.rwait);
irnet_events.index = 0;
/* Init spinlock for event logging */
spin_lock_init(&irnet_events.spinlock);
#ifdef CONFIG_PROC_FS
/* Add a /proc file for irnet infos */
proc_create("irnet", 0, proc_irda, &irnet_proc_fops);
#endif /* CONFIG_PROC_FS */
/* Setup the IrNET server */
err = irnet_setup_server();
if(!err)
/* We are no longer functional... */
irnet_server.running = 1;
DEXIT(MODULE_TRACE, "\n");
return err;
}
/*------------------------------------------------------------------*/
/*
* Cleanup at exit...
*/
void __exit
irda_irnet_cleanup(void)
{
DENTER(MODULE_TRACE, "()\n");
/* We are no longer there... */
irnet_server.running = 0;
#ifdef CONFIG_PROC_FS
/* Remove our /proc file */
remove_proc_entry("irnet", proc_irda);
#endif /* CONFIG_PROC_FS */
/* Remove our IrNET server from existence */
irnet_destroy_server();
/* Remove all instances of IrNET socket still present */
hashbin_delete(irnet_server.list, (FREE_FUNC) irda_irnet_destroy);
DEXIT(MODULE_TRACE, "\n");
}