?? irnet_irda.c
字號:
/* * 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%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 */ 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... */intirda_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. */voidirda_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 */ 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"); return;}/************************** 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 intirnet_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[NICKNAME_MAX_LEN + 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 intirnet_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 voidirnet_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"); return;}/*------------------------------------------------------------------*//* * 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 intirnet_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 */
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -