📄 irnet_irda.c
字号:
* 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 not handled */static voidirnet_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%X)\n", (unsigned int) 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); 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); /* 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) { /* If we were connected, cleanup & close the PPP channel, * which will kill pppd (hangup) and the rest */ ppp_unregister_channel(&self->chan); self->ppp_open = 0; } 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 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); /* 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); 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 voidirnet_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%X, flow=%d)\n", (unsigned int) 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 voidirnet_status_indication(void * instance, LINK_STATUS link, LOCK_STATUS lock){ 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"); /* 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); 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 voidirnet_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%X)\n", (unsigned int) server); DASSERT(instance == &irnet_server, , IRDA_CB_ERROR, "Invalid instance (0x%X) !!!\n", (unsigned int) 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -