📄 irnet_irda.c
字号:
/* * 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 <linux/config.h>#include "irnet_irda.h" /* Private header *//************************* CONTROL CHANNEL *************************//* * When ppp is not active, /dev/irnet act as a control channel. * Writting 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 voidirnet_post_event(irnet_socket * ap, irnet_event event, __u32 addr, char * name){ unsigned long flags; /* For spinlock */ int index; /* In the log */ DENTER(CTRL_TRACE, "(ap=0x%X, event=%d, addr=%08x, name=``%s'')\n", (unsigned int) ap, event, addr, 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_irqsave(&irnet_events.spinlock, flags); /* Copy the event in the log */ index = irnet_events.index; irnet_events.log[index].event = event; irnet_events.log[index].addr = addr; /* Try to copy IrDA nickname */ if(name) strcpy(irnet_events.log[index].name, name); else irnet_events.log[index].name[0] = '\0'; /* 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_irqrestore(&irnet_events.spinlock, flags); /* 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 intirnet_open_tsap(irnet_socket * self){ notify_t notify; /* Callback structure */ DENTER(IRDA_SR_TRACE, "(self=0x%X)\n", (unsigned int) 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; strncpy(notify.name, IRNET_NOTIFY_NAME, NOTIFY_MAX_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%X, sel=0x%X\n", (unsigned int) self->tsap, self->stsap_sel); return 0;}/*------------------------------------------------------------------*//* * 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 intirnet_find_lsap_sel(irnet_socket * self){ DENTER(IRDA_SR_TRACE, "(self=0x%X)\n", (unsigned int) 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->saddr, self->daddr, IRNET_SERVICE_NAME, IRNET_IAS_VALUE); /* Wait for answer (if not already failed) */ if(self->iriap != NULL) interruptible_sleep_on(&self->query_wait); /* Check what happened */ if(self->errno) { DEBUG(IRDA_SR_INFO, "IAS query failed! (%d)\n", self->errno); /* Requested object/attribute doesn't exist */ if((self->errno == IAS_CLASS_UNKNOWN) || (self->errno == IAS_ATTRIB_UNKNOWN)) return (-EADDRNOTAVAIL); else return (-EHOSTUNREACH); } /* Get the remote TSAP selector */ switch(self->ias_result->type) { case IAS_INTEGER: DEBUG(IRDA_SR_INFO, "result=%d\n", self->ias_result->t.integer); if(self->ias_result->t.integer != -1) self->dtsap_sel = self->ias_result->t.integer; else self->dtsap_sel = 0; break; default: self->dtsap_sel = 0; DERROR(IRDA_SR_ERROR, "bad type ! (0x%X)\n", self->ias_result->type); break; } /* Cleanup */ if(self->ias_result) irias_delete_value(self->ias_result); DEXIT(IRDA_SR_TRACE, "\n"); if(self->dtsap_sel) return 0; return -EADDRNOTAVAIL;}/*------------------------------------------------------------------*//* * Function irnet_discover_daddr_and_lsap_sel (self) * * This try to find a device with the requested service. * * 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). * The, we set both the destination address and the lsap selector to point * on the service on the unique device we have found. * * Note : this function fails if there is more than one device in range, * because IrLMP doesn't disconnect the LAP when the last LSAP is closed. * Moreover, we would need to wait the LAP disconnection... */static inline intirnet_discover_daddr_and_lsap_sel(irnet_socket * self){ struct irda_device_info *discoveries; /* Copy of the discovery log */ int number; /* Number of nodes in the log */ int i; int err = -ENETUNREACH; __u32 daddr = DEV_ADDR_ANY; /* Address we found the service on */ __u8 dtsap_sel = 0x0; /* TSAP associated with it */ DENTER(IRDA_SR_TRACE, "(self=0x%X)\n", (unsigned int) self); /* Ask lmp for the current discovery log * Note : we have to use irlmp_get_discoveries(), as opposed * to play with the cachelog directly, because while we are * making our ias query, le log might change... */ discoveries = irlmp_get_discoveries(&number, self->mask); /* 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++) { /* Try the address in the log */ self->daddr = discoveries[i].daddr; self->saddr = 0x0; DEBUG(IRDA_SR_INFO, "trying daddr = %08x\n", self->daddr); /* Query remote LM-IAS for this service */ err = irnet_find_lsap_sel(self); switch(err) { case 0: /* We found the requested service */ if(daddr != DEV_ADDR_ANY) { DEBUG(IRDA_SR_INFO, "More than one device in range supports IrNET...\n"); } else { /* First time we found that one, save it ! */ daddr = self->daddr; dtsap_sel = self->dtsap_sel; } break; case -EADDRNOTAVAIL: /* Requested service simply doesn't exist on this node */ break; default: /* Something bad did happen :-( */ DERROR(IRDA_SR_ERROR, "unexpected IAS query failure\n"); self->daddr = DEV_ADDR_ANY; kfree(discoveries); return(-EHOSTUNREACH); break; } } /* Cleanup our copy of the discovery log */ kfree(discoveries); /* Check out what we found */ if(daddr == DEV_ADDR_ANY) { self->daddr = DEV_ADDR_ANY; DEXIT(IRDA_SR_INFO, "cannot discover IrNET in any device !!!\n"); return(-EADDRNOTAVAIL); } /* Revert back to discovered device & service */ self->daddr = daddr; self->saddr = 0x0; self->dtsap_sel = dtsap_sel; DEBUG(IRDA_SR_INFO, "discovered IrNET at address %08x\n", self->daddr); 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 intirnet_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%X)\n", (unsigned int) self); /* Ask lmp for the current discovery log */ discoveries = irlmp_get_discoveries(&number, 0xffff); /* 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... */intirda_irnet_create(irnet_socket * self){ DENTER(IRDA_SOCK_TRACE, "(self=0x%X)\n", (unsigned int) self); self->magic = IRNET_MAGIC; /* Paranoia */ init_waitqueue_head(&self->query_wait); self->ttp_open = 0; /* Prevent higher layer from accessing IrTTP */ self->rname[0] = '\0'; /* May be set via control channel */ self->raddr = DEV_ADDR_ANY; /* May be set via control channel */ self->daddr = DEV_ADDR_ANY; /* Until we get connected */ self->saddr = 0x0; /* so IrLMP assign us any link */ 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 */ 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 */intirda_irnet_connect(irnet_socket * self){ int err; DENTER(IRDA_SOCK_TRACE, "(self=0x%X)\n", (unsigned int) self); /* Check if we have opened a local TSAP : * If we have already opened a TSAP, it means that either we are already * connected or in the process of doing so... */ if(self->tsap != NULL) DRETURN(-EBUSY, IRDA_SOCK_INFO, "Already connecting...\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)) { unsigned long flags; spin_lock_irqsave(&irnet_server.spinlock, flags); hashbin_insert(irnet_server.list, (irda_queue_t *) self, 0, self->rname); spin_unlock_irqrestore(&irnet_server.spinlock, flags); DEBUG(IRDA_SOCK_INFO, "Inserted ``%s'' in hashbin...\n", self->rname); } /* If we don't have anything (no address, no name) */ if((self->raddr == 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"); } else { /* If we have only the name (no address), try to get an address */ if(self->raddr == 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->raddr; /* Query remote LM-IAS to find LSAP selector */ if((err = irnet_find_lsap_sel(self)) != 0) DRETURN(err, IRDA_SOCK_INFO, "connect failed!\n"); } DEBUG(IRDA_SOCK_INFO, "daddr = %08x, lsap = %d, starting IrTTP connection\n", self->daddr, self->dtsap_sel); /* Open a local TSAP (an IrTTP instance) */ err = irnet_open_tsap(self); DABORT(err != 0, err, IRDA_SOCK_ERROR, "connect aborted!\n"); /* Connect to remote device */ err = irttp_connect_request(self->tsap, self->dtsap_sel, self->saddr, self->daddr, NULL, self->max_sdu_size_rx, NULL); DABORT(err != 0, err, IRDA_SOCK_ERROR, "connect aborted!\n"); DEXIT(IRDA_SOCK_TRACE, "\n"); return(0);}/*------------------------------------------------------------------*//* * Function irda_irnet_destroy(self) * * Destroy irnet instance * */voidirda_irnet_destroy(irnet_socket * self){ DENTER(IRDA_SOCK_TRACE, "(self=0x%X)\n", (unsigned int) 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; unsigned long flags; DEBUG(IRDA_SOCK_INFO, "Removing from hash..\n"); spin_lock_irqsave(&irnet_server.spinlock, flags); entry = hashbin_remove_this(irnet_server.list, (irda_queue_t *) self); self->q.q_next = NULL; spin_unlock_irqrestore(&irnet_server.spinlock, flags); DASSERT(entry == self, , IRDA_SOCK_ERROR, "Can't remove from hash.\n"); } /* Unregister with IrLMP */ irlmp_unregister_client(self->ckey);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -