/* * IrNET protocol module : Synchronous PPP over an IrDA socket. * * Jean II - HPL `00 - * * 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 #include #include #include /* * 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(¬ify); 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, ¬ify); 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"); }