📄 irlmp.c
字号:
* Incoming connection * */void irlmp_connect_indication(struct lsap_cb *self, struct sk_buff *skb){ int max_seg_size; int lap_header_size; int max_header_size; IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); IRDA_ASSERT(skb != NULL, return;); IRDA_ASSERT(self->lap != NULL, return;); IRDA_DEBUG(2, "%s(), slsap_sel=%02x, dlsap_sel=%02x\n", __FUNCTION__, self->slsap_sel, self->dlsap_sel); /* Note : self->lap is set in irlmp_link_data_indication(), * (case CONNECT_CMD:) because we have no way to set it here. * Similarly, self->dlsap_sel is usually set in irlmp_find_lsap(). * Jean II */ self->qos = *self->lap->qos; max_seg_size = self->lap->qos->data_size.value-LMP_HEADER; lap_header_size = IRLAP_GET_HEADER_SIZE(self->lap->irlap); max_header_size = LMP_HEADER + lap_header_size; /* Hide LMP_CONTROL_HEADER header from layer above */ skb_pull(skb, LMP_CONTROL_HEADER); if (self->notify.connect_indication) { /* Don't forget to refcount it - see irlap_driver_rcv(). */ skb_get(skb); self->notify.connect_indication(self->notify.instance, self, &self->qos, max_seg_size, max_header_size, skb); }}/* * Function irlmp_connect_response (handle, userdata) * * Service user is accepting connection * */int irlmp_connect_response(struct lsap_cb *self, struct sk_buff *userdata){ IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); IRDA_ASSERT(userdata != NULL, return -1;); /* We set the connected bit and move the lsap to the connected list * in the state machine itself. Jean II */ IRDA_DEBUG(2, "%s(), slsap_sel=%02x, dlsap_sel=%02x\n", __FUNCTION__, self->slsap_sel, self->dlsap_sel); /* Make room for MUX control header (3 bytes) */ IRDA_ASSERT(skb_headroom(userdata) >= LMP_CONTROL_HEADER, return -1;); skb_push(userdata, LMP_CONTROL_HEADER); irlmp_do_lsap_event(self, LM_CONNECT_RESPONSE, userdata); /* Drop reference count - see irlap_data_request(). */ dev_kfree_skb(userdata); return 0;}EXPORT_SYMBOL(irlmp_connect_response);/* * Function irlmp_connect_confirm (handle, skb) * * LSAP connection confirmed peer device! */void irlmp_connect_confirm(struct lsap_cb *self, struct sk_buff *skb){ int max_header_size; int lap_header_size; int max_seg_size; IRDA_DEBUG(3, "%s()\n", __FUNCTION__); IRDA_ASSERT(skb != NULL, return;); IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); IRDA_ASSERT(self->lap != NULL, return;); self->qos = *self->lap->qos; max_seg_size = self->lap->qos->data_size.value-LMP_HEADER; lap_header_size = IRLAP_GET_HEADER_SIZE(self->lap->irlap); max_header_size = LMP_HEADER + lap_header_size; IRDA_DEBUG(2, "%s(), max_header_size=%d\n", __FUNCTION__, max_header_size); /* Hide LMP_CONTROL_HEADER header from layer above */ skb_pull(skb, LMP_CONTROL_HEADER); if (self->notify.connect_confirm) { /* Don't forget to refcount it - see irlap_driver_rcv() */ skb_get(skb); self->notify.connect_confirm(self->notify.instance, self, &self->qos, max_seg_size, max_header_size, skb); }}/* * Function irlmp_dup (orig, instance) * * Duplicate LSAP, can be used by servers to confirm a connection on a * new LSAP so it can keep listening on the old one. * */struct lsap_cb *irlmp_dup(struct lsap_cb *orig, void *instance){ struct lsap_cb *new; unsigned long flags; IRDA_DEBUG(1, "%s()\n", __FUNCTION__); spin_lock_irqsave(&irlmp->unconnected_lsaps->hb_spinlock, flags); /* Only allowed to duplicate unconnected LSAP's, and only LSAPs * that have received a connect indication. Jean II */ if ((!hashbin_find(irlmp->unconnected_lsaps, (long) orig, NULL)) || (orig->lap == NULL)) { IRDA_DEBUG(0, "%s(), invalid LSAP (wrong state)\n", __FUNCTION__); spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags); return NULL; } /* Allocate a new instance */ new = kmemdup(orig, sizeof(*new), GFP_ATOMIC); if (!new) { IRDA_DEBUG(0, "%s(), unable to kmalloc\n", __FUNCTION__); spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags); return NULL; } /* new->lap = orig->lap; => done in the memcpy() */ /* new->slsap_sel = orig->slsap_sel; => done in the memcpy() */ new->conn_skb = NULL; spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags); /* Not everything is the same */ new->notify.instance = instance; init_timer(&new->watchdog_timer); hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) new, (long) new, NULL);#ifdef CONFIG_IRDA_CACHE_LAST_LSAP /* Make sure that we invalidate the LSAP cache */ new->lap->cache.valid = FALSE;#endif /* CONFIG_IRDA_CACHE_LAST_LSAP */ return new;}/* * Function irlmp_disconnect_request (handle, userdata) * * The service user is requesting disconnection, this will not remove the * LSAP, but only mark it as disconnected */int irlmp_disconnect_request(struct lsap_cb *self, struct sk_buff *userdata){ struct lsap_cb *lsap; IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); IRDA_ASSERT(userdata != NULL, return -1;); /* Already disconnected ? * There is a race condition between irlmp_disconnect_indication() * and us that might mess up the hashbins below. This fixes it. * Jean II */ if (! test_and_clear_bit(0, &self->connected)) { IRDA_DEBUG(0, "%s(), already disconnected!\n", __FUNCTION__); dev_kfree_skb(userdata); return -1; } skb_push(userdata, LMP_CONTROL_HEADER); /* * Do the event before the other stuff since we must know * which lap layer that the frame should be transmitted on */ irlmp_do_lsap_event(self, LM_DISCONNECT_REQUEST, userdata); /* Drop reference count - see irlap_data_request(). */ dev_kfree_skb(userdata); /* * Remove LSAP from list of connected LSAPs for the particular link * and insert it into the list of unconnected LSAPs */ IRDA_ASSERT(self->lap != NULL, return -1;); IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;); IRDA_ASSERT(self->lap->lsaps != NULL, return -1;); lsap = hashbin_remove(self->lap->lsaps, (long) self, NULL);#ifdef CONFIG_IRDA_CACHE_LAST_LSAP self->lap->cache.valid = FALSE;#endif IRDA_ASSERT(lsap != NULL, return -1;); IRDA_ASSERT(lsap->magic == LMP_LSAP_MAGIC, return -1;); IRDA_ASSERT(lsap == self, return -1;); hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) self, (long) self, NULL); /* Reset some values */ self->dlsap_sel = LSAP_ANY; self->lap = NULL; return 0;}EXPORT_SYMBOL(irlmp_disconnect_request);/* * Function irlmp_disconnect_indication (reason, userdata) * * LSAP is being closed! */void irlmp_disconnect_indication(struct lsap_cb *self, LM_REASON reason, struct sk_buff *skb){ struct lsap_cb *lsap; IRDA_DEBUG(1, "%s(), reason=%s\n", __FUNCTION__, irlmp_reasons[reason]); IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); IRDA_DEBUG(3, "%s(), slsap_sel=%02x, dlsap_sel=%02x\n", __FUNCTION__, self->slsap_sel, self->dlsap_sel); /* Already disconnected ? * There is a race condition between irlmp_disconnect_request() * and us that might mess up the hashbins below. This fixes it. * Jean II */ if (! test_and_clear_bit(0, &self->connected)) { IRDA_DEBUG(0, "%s(), already disconnected!\n", __FUNCTION__); return; } /* * Remove association between this LSAP and the link it used */ IRDA_ASSERT(self->lap != NULL, return;); IRDA_ASSERT(self->lap->lsaps != NULL, return;); lsap = hashbin_remove(self->lap->lsaps, (long) self, NULL);#ifdef CONFIG_IRDA_CACHE_LAST_LSAP self->lap->cache.valid = FALSE;#endif IRDA_ASSERT(lsap != NULL, return;); IRDA_ASSERT(lsap == self, return;); hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) lsap, (long) lsap, NULL); self->dlsap_sel = LSAP_ANY; self->lap = NULL; /* * Inform service user */ if (self->notify.disconnect_indication) { /* Don't forget to refcount it - see irlap_driver_rcv(). */ if(skb) skb_get(skb); self->notify.disconnect_indication(self->notify.instance, self, reason, skb); } else { IRDA_DEBUG(0, "%s(), no handler\n", __FUNCTION__); }}/* * Function irlmp_do_expiry (void) * * Do a cleanup of the discovery log (remove old entries) * * Note : separate from irlmp_do_discovery() so that we can handle * passive discovery properly. */void irlmp_do_expiry(void){ struct lap_cb *lap; /* * Expire discovery on all links which are *not* connected. * On links which are connected, we can't do discovery * anymore and can't refresh the log, so we freeze the * discovery log to keep info about the device we are * connected to. * This info is mandatory if we want irlmp_connect_request() * to work properly. - Jean II */ lap = (struct lap_cb *) hashbin_get_first(irlmp->links); while (lap != NULL) { IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;); if (lap->lap_state == LAP_STANDBY) { /* Expire discoveries discovered on this link */ irlmp_expire_discoveries(irlmp->cachelog, lap->saddr, FALSE); } lap = (struct lap_cb *) hashbin_get_next(irlmp->links); }}/* * Function irlmp_do_discovery (nslots) * * Do some discovery on all links * * Note : log expiry is done above. */void irlmp_do_discovery(int nslots){ struct lap_cb *lap; __u16 *data_hintsp; /* Make sure the value is sane */ if ((nslots != 1) && (nslots != 6) && (nslots != 8) && (nslots != 16)){ IRDA_WARNING("%s: invalid value for number of slots!\n", __FUNCTION__); nslots = sysctl_discovery_slots = 8; } /* Construct new discovery info to be used by IrLAP, */ data_hintsp = (__u16 *) irlmp->discovery_cmd.data.hints; put_unaligned(irlmp->hints.word, data_hintsp); /* * Set character set for device name (we use ASCII), and * copy device name. Remember to make room for a \0 at the * end */ irlmp->discovery_cmd.data.charset = CS_ASCII; strncpy(irlmp->discovery_cmd.data.info, sysctl_devname, NICKNAME_MAX_LEN); irlmp->discovery_cmd.name_len = strlen(irlmp->discovery_cmd.data.info); irlmp->discovery_cmd.nslots = nslots; /* * Try to send discovery packets on all links */ lap = (struct lap_cb *) hashbin_get_first(irlmp->links); while (lap != NULL) { IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;); if (lap->lap_state == LAP_STANDBY) { /* Try to discover */ irlmp_do_lap_event(lap, LM_LAP_DISCOVERY_REQUEST, NULL); } lap = (struct lap_cb *) hashbin_get_next(irlmp->links); }}/* * Function irlmp_discovery_request (nslots) * * Do a discovery of devices in front of the computer * * If the caller has registered a client discovery callback, this * allow him to receive the full content of the discovery log through * this callback (as normally he will receive only new discoveries). */void irlmp_discovery_request(int nslots){ /* Return current cached discovery log (in full) */ irlmp_discovery_confirm(irlmp->cachelog, DISCOVERY_LOG); /* * Start a single discovery operation if discovery is not already * running */ if (!sysctl_discovery) { /* Check if user wants to override the default */ if (nslots == DISCOVERY_DEFAULT_SLOTS) nslots = sysctl_discovery_slots; irlmp_do_discovery(nslots); /* Note : we never do expiry here. Expiry will run on the * discovery timer regardless of the state of sysctl_discovery * Jean II */ }}EXPORT_SYMBOL(irlmp_discovery_request);/* * Function irlmp_get_discoveries (pn, mask, slots) * * Return the current discovery log * * If discovery is not enabled, you should call this function again * after 1 or 2 seconds (i.e. after discovery has been done). */struct irda_device_info *irlmp_get_discoveries(int *pn, __u16 mask, int nslots){ /* If discovery is not enabled, it's likely that the discovery log * will be empty. So, we trigger a single discovery, so that next * time the user call us there might be some results in the log. * Jean II */ if (!sysctl_discovery) { /* Check if user wants to override the default */ if (nslots == DISCOVERY_DEFAULT_SLOTS) nslots = sysctl_discovery_slots; /* Start discovery - will complete sometime later */ irlmp_do_discovery(nslots); /* Note : we never do expiry here. Expiry will run on the * discovery timer regardless of the state of sysctl_discovery * Jean II */ } /* Return current cached discovery log */ return(irlmp_copy_discoveries(irlmp->cachelog, pn, mask, TRUE));}EXPORT_SYMBOL(irlmp_get_discoveries);/* * Function irlmp_notify_client (log) * * Notify all about discovered devices * * Clients registered with IrLMP are : * o IrComm * o IrLAN * o Any socket (in any state - ouch, that may be a lot !) * The client may have defined a callback to be notified in case of * partial/selective discovery based on the hints that it passed to IrLMP. */static inline voidirlmp_notify_client(irlmp_client_t *client, hashbin_t *log, DISCOVERY_MODE mode){ discinfo_t *discoveries; /* Copy of the discovery log */ int number; /* Number of nodes in the log */ int i; IRDA_DEBUG(3, "%s()\n", __FUNCTION__); /* Check if client wants or not partial/selective log (optimisation) */ if (!client->disco_callback) return; /* * Locking notes : * the old code was manipulating the log directly, which was * very racy. Now, we use copy_discoveries, that protects * itself while dumping the log for us. * The overhead of the copy is compensated by the fact that * we only pass new discoveries in normal mode and don't * pass the same old entry every 3s to the caller as we used * to do (virtual function calling is expensive). * Jean II */ /* * Now, check all discovered devices (if any), and notify client * only about the services that the client is interested in * We also notify only about the new devices unless the caller * explicitly request a dump of the log. Jean II */ discoveries = irlmp_copy_discoveries(log, &number, client->hint_mask.word, (mode == DISCOVERY_LOG)); /* Check if the we got some results */ if (discoveries == NULL) return; /* No nodes discovered */ /* Pass all entries to the listener */ for(i = 0; i < number; i++) client->disco_callback(&(discoveries[i]), mode, client->priv); /* Free up our buffer */ kfree(discoveries);}/* * Function irlmp_discovery_confirm ( self, log) * * Some device(s) answered to our discovery request! Check to see which * device it is, and give indication to the client(s) * */void irlmp_discovery_confirm(hashbin_t *log, DISCOVERY_MODE mode){ irlmp_client_t *client; irlmp_client_t *client_next;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -