📄 irnet_irda.c
字号:
/* Unregister with LM-IAS */ if(self->iriap) iriap_close(self->iriap); /* Prevent higher layer from accessing IrTTP */ self->ttp_open = 0; /* 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; /* 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->daddr, self->rname); } 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 incomming 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%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_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.. */ strncpy(self->rname, discoveries[i].info, NICKNAME_MAX_LEN); 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; unsigned long flags; int err; DENTER(IRDA_SERV_TRACE, "(self=0x%X)\n", (unsigned int) self); /* Get the address of the requester */ self->daddr = irttp_get_daddr(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_irqsave(&irnet_server.spinlock, flags); /* 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%X matches rname ``%s''.\n", (unsigned int) 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->raddr == self->daddr) || (new->daddr == self->daddr)) { /* Yes !!! Get it.. */ DEBUG(IRDA_SERV_INFO, "Socket 0x%X matches daddr %#08x.\n", (unsigned int) 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(!(new->ttp_open) && (new->raddr == DEV_ADDR_ANY) && (new->rname[0] == '\0') && (new->ppp_open)) { /* Yes !!! Get it.. */ DEBUG(IRDA_SERV_INFO, "Socket 0x%X is free.\n", (unsigned int) new); break; } new = (irnet_socket *) hashbin_get_next(irnet_server.list); } } /* Spin lock end */ spin_unlock_irqrestore(&irnet_server.spinlock, flags); DEXIT(IRDA_SERV_TRACE, " - new = 0x%X\n", (unsigned int) new); return new;}/*------------------------------------------------------------------*//* * Function irda_connect_socket (self) * * Connect an incomming connection to the socket * */static inline intirnet_connect_socket(irnet_socket * self, irnet_socket * new, struct qos_info * qos, __u32 max_sdu_size, __u8 max_header_size){ DENTER(IRDA_SERV_TRACE, "(self=0x%X, new=0x%X)\n", (unsigned int) self, (unsigned int) new); /* Now attach up the new socket */ new->tsap = irttp_dup(self->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 */ self->tsap->dtsap_sel = self->tsap->lsap->dlsap_sel = LSAP_ANY; self->tsap->lsap->lsap_state = LSAP_DISCONNECTED; /* 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... */ new->ttp_open = 1;#ifdef CONNECT_INDIC_KICK /* As currently we don't packets in ppp_irnet_send(), this is not needed... * Also, not doing it give IrDA a chance to finish the setup properly * before beeing swamped with packets... */ ppp_output_wakeup(&new->chan);#endif CONNECT_INDIC_KICK /* Notify the control channel */ irnet_post_event(new, IRNET_CONNECT_FROM, new->daddr, self->rname); DEXIT(IRDA_SERV_TRACE, "\n"); return 0;}/*------------------------------------------------------------------*//* * Function irda_disconnect_server (self) * * Cleanup the server socket when the incomming connection abort * */static inline voidirnet_disconnect_server(irnet_socket * self, struct sk_buff *skb){ DENTER(IRDA_SERV_TRACE, "(self=0x%X)\n", (unsigned int) 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 /* Clean up the server to keep it in listen state */ self->tsap->dtsap_sel = self->tsap->lsap->dlsap_sel = LSAP_ANY; self->tsap->lsap->lsap_state = LSAP_DISCONNECTED; /* Notify the control channel */ irnet_post_event(NULL, IRNET_REQUEST_FROM, self->daddr, self->rname); 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 */ /* 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%X\n", (unsigned int) &irnet_server.s); return 0;}/*------------------------------------------------------------------*//* * Function irda_destroy_server (self) * * Destroy the IrTTP server... * * Reverse of the previous function... */static inline voidirnet_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"); return;}/************************ 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 intirnet_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%X, skb=0x%X)\n", (unsigned int) ap,(unsigned int) 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 no handled */static voidirnet_disconnect_indication(void * instance, void * sap, LM_REASON reason, struct sk_buff *skb){ irnet_socket * self = (irnet_socket *) instance; DENTER(IRDA_TCB_TRACE, "(self=0x%X)\n", (unsigned int) self); DASSERT(self != NULL, , IRDA_CB_ERROR, "Self is NULL !!!\n"); /* If we were active, notify the control channel */ if(self->ttp_open) irnet_post_event(self, IRNET_DISCONNECT_FROM, self->daddr, self->rname); 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->daddr, self->rname); /* Prevent higher layer from accessing IrTTP */ self->ttp_open = 0; /* Close our IrTTP connection */ if((self->tsap) && (self != &irnet_server.s)) { DEBUG(IRDA_CB_INFO, "Closing our TTP connection.\n"); irttp_disconnect_request(self->tsap, NULL, P_NORMAL); irttp_close_tsap(self->tsap); self->tsap = NULL; /* Flush (drain) ppp_generic Tx queue (most often we have blocked it) */ if(self->ppp_open) ppp_output_wakeup(&self->chan); } /* Cleanup the socket in case we want to reconnect */ self->stsap_sel = 0; self->daddr = DEV_ADDR_ANY; self->tx_flow = FLOW_START; /* Note : what should we say to ppp ? * It seem the ppp_generic and pppd are happy that way and will eventually * timeout gracefully, so don't bother them... */ 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 voidirnet_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%X)\n", (unsigned int) self);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -