📄 irlmp.c
字号:
if (!service) { IRDA_DEBUG(1, "%s(), Unknown service!\n", __FUNCTION__); return -1; } hashbin_remove_this(irlmp->services, (irda_queue_t *) service); kfree(service); /* Remove old hint bits */ irlmp->hints.word = 0; /* Refresh current hint bits */ spin_lock_irqsave(&irlmp->services->hb_spinlock, flags); service = (irlmp_service_t *) hashbin_get_first(irlmp->services); while (service) { irlmp->hints.word |= service->hints.word; service = (irlmp_service_t *)hashbin_get_next(irlmp->services); } spin_unlock_irqrestore(&irlmp->services->hb_spinlock, flags); return 0;}EXPORT_SYMBOL(irlmp_unregister_service);/* * Function irlmp_register_client (hint_mask, callback1, callback2) * * Register a local client with IrLMP * First callback is selective discovery (based on hints) * Second callback is for selective discovery expiries * * Returns: handle > 0 on success, 0 on error */void *irlmp_register_client(__u16 hint_mask, DISCOVERY_CALLBACK1 disco_clb, DISCOVERY_CALLBACK2 expir_clb, void *priv){ irlmp_client_t *client; IRDA_DEBUG(1, "%s()\n", __FUNCTION__); IRDA_ASSERT(irlmp != NULL, return NULL;); /* Make a new registration */ client = kmalloc(sizeof(irlmp_client_t), GFP_ATOMIC); if (!client) { IRDA_DEBUG( 1, "%s(), Unable to kmalloc!\n", __FUNCTION__); return NULL; } /* Register the details */ client->hint_mask.word = hint_mask; client->disco_callback = disco_clb; client->expir_callback = expir_clb; client->priv = priv; hashbin_insert(irlmp->clients, (irda_queue_t *) client, (long) client, NULL); return (void *) client;}EXPORT_SYMBOL(irlmp_register_client);/* * Function irlmp_update_client (handle, hint_mask, callback1, callback2) * * Updates specified client (handle) with possibly new hint_mask and * callback * * Returns: 0 on success, -1 on error */int irlmp_update_client(void *handle, __u16 hint_mask, DISCOVERY_CALLBACK1 disco_clb, DISCOVERY_CALLBACK2 expir_clb, void *priv){ irlmp_client_t *client; if (!handle) return -1; client = hashbin_lock_find(irlmp->clients, (long) handle, NULL); if (!client) { IRDA_DEBUG(1, "%s(), Unknown client!\n", __FUNCTION__); return -1; } client->hint_mask.word = hint_mask; client->disco_callback = disco_clb; client->expir_callback = expir_clb; client->priv = priv; return 0;}EXPORT_SYMBOL(irlmp_update_client);/* * Function irlmp_unregister_client (handle) * * Returns: 0 on success, -1 on error * */int irlmp_unregister_client(void *handle){ struct irlmp_client *client; IRDA_DEBUG(4, "%s()\n", __FUNCTION__); if (!handle) return -1; /* Caller may call with invalid handle (it's legal) - Jean II */ client = hashbin_lock_find(irlmp->clients, (long) handle, NULL); if (!client) { IRDA_DEBUG(1, "%s(), Unknown client!\n", __FUNCTION__); return -1; } IRDA_DEBUG(4, "%s(), removing client!\n", __FUNCTION__); hashbin_remove_this(irlmp->clients, (irda_queue_t *) client); kfree(client); return 0;}EXPORT_SYMBOL(irlmp_unregister_client);/* * Function irlmp_slsap_inuse (slsap) * * Check if the given source LSAP selector is in use * * This function is clearly not very efficient. On the mitigating side, the * stack make sure that in 99% of the cases, we are called only once * for each socket allocation. We could probably keep a bitmap * of the allocated LSAP, but I'm not sure the complexity is worth it. * Jean II */static int irlmp_slsap_inuse(__u8 slsap_sel){ struct lsap_cb *self; struct lap_cb *lap; unsigned long flags; IRDA_ASSERT(irlmp != NULL, return TRUE;); IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return TRUE;); IRDA_ASSERT(slsap_sel != LSAP_ANY, return TRUE;); IRDA_DEBUG(4, "%s()\n", __FUNCTION__);#ifdef CONFIG_IRDA_ULTRA /* Accept all bindings to the connectionless LSAP */ if (slsap_sel == LSAP_CONNLESS) return FALSE;#endif /* CONFIG_IRDA_ULTRA */ /* Valid values are between 0 and 127 (0x0-0x6F) */ if (slsap_sel > LSAP_MAX) return TRUE; /* * Check if slsap is already in use. To do this we have to loop over * every IrLAP connection and check every LSAP associated with each * the connection. */ spin_lock_irqsave_nested(&irlmp->links->hb_spinlock, flags, SINGLE_DEPTH_NESTING); lap = (struct lap_cb *) hashbin_get_first(irlmp->links); while (lap != NULL) { IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, goto errlap;); /* Careful for priority inversions here ! * irlmp->links is never taken while another IrDA * spinlock is held, so we are safe. Jean II */ spin_lock(&lap->lsaps->hb_spinlock); /* For this IrLAP, check all the LSAPs */ self = (struct lsap_cb *) hashbin_get_first(lap->lsaps); while (self != NULL) { IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, goto errlsap;); if ((self->slsap_sel == slsap_sel)) { IRDA_DEBUG(4, "Source LSAP selector=%02x in use\n", self->slsap_sel); goto errlsap; } self = (struct lsap_cb*) hashbin_get_next(lap->lsaps); } spin_unlock(&lap->lsaps->hb_spinlock); /* Next LAP */ lap = (struct lap_cb *) hashbin_get_next(irlmp->links); } spin_unlock_irqrestore(&irlmp->links->hb_spinlock, flags); /* * Server sockets are typically waiting for connections and * therefore reside in the unconnected list. We don't want * to give out their LSAPs for obvious reasons... * Jean II */ spin_lock_irqsave(&irlmp->unconnected_lsaps->hb_spinlock, flags); self = (struct lsap_cb *) hashbin_get_first(irlmp->unconnected_lsaps); while (self != NULL) { IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, goto erruncon;); if ((self->slsap_sel == slsap_sel)) { IRDA_DEBUG(4, "Source LSAP selector=%02x in use (unconnected)\n", self->slsap_sel); goto erruncon; } self = (struct lsap_cb*) hashbin_get_next(irlmp->unconnected_lsaps); } spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags); return FALSE; /* Error exit from within one of the two nested loops. * Make sure we release the right spinlock in the righ order. * Jean II */errlsap: spin_unlock(&lap->lsaps->hb_spinlock);IRDA_ASSERT_LABEL(errlap:) spin_unlock_irqrestore(&irlmp->links->hb_spinlock, flags); return TRUE; /* Error exit from within the unconnected loop. * Just one spinlock to release... Jean II */erruncon: spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags); return TRUE;}/* * Function irlmp_find_free_slsap () * * Find a free source LSAP to use. This function is called if the service * user has requested a source LSAP equal to LM_ANY */static __u8 irlmp_find_free_slsap(void){ __u8 lsap_sel; int wrapped = 0; IRDA_ASSERT(irlmp != NULL, return -1;); IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return -1;); /* Most users don't really care which LSAPs they are given, * and therefore we automatically give them a free LSAP. * This function try to find a suitable LSAP, i.e. which is * not in use and is within the acceptable range. Jean II */ do { /* Always increment to LSAP number before using it. * In theory, we could reuse the last LSAP number, as long * as it is no longer in use. Some IrDA stack do that. * However, the previous socket may be half closed, i.e. * we closed it, we think it's no longer in use, but the * other side did not receive our close and think it's * active and still send data on it. * This is similar to what is done with PIDs and TCP ports. * Also, this reduce the number of calls to irlmp_slsap_inuse() * which is an expensive function to call. * Jean II */ irlmp->last_lsap_sel++; /* Check if we need to wraparound (0x70-0x7f are reserved) */ if (irlmp->last_lsap_sel > LSAP_MAX) { /* 0x00-0x10 are also reserved for well know ports */ irlmp->last_lsap_sel = 0x10; /* Make sure we terminate the loop */ if (wrapped++) { IRDA_ERROR("%s: no more free LSAPs !\n", __FUNCTION__); return 0; } } /* If the LSAP is in use, try the next one. * Despite the autoincrement, we need to check if the lsap * is really in use or not, first because LSAP may be * directly allocated in irlmp_open_lsap(), and also because * we may wraparound on old sockets. Jean II */ } while (irlmp_slsap_inuse(irlmp->last_lsap_sel)); /* Got it ! */ lsap_sel = irlmp->last_lsap_sel; IRDA_DEBUG(4, "%s(), found free lsap_sel=%02x\n", __FUNCTION__, lsap_sel); return lsap_sel;}/* * Function irlmp_convert_lap_reason (lap_reason) * * Converts IrLAP disconnect reason codes to IrLMP disconnect reason * codes * */LM_REASON irlmp_convert_lap_reason( LAP_REASON lap_reason){ int reason = LM_LAP_DISCONNECT; switch (lap_reason) { case LAP_DISC_INDICATION: /* Received a disconnect request from peer */ IRDA_DEBUG( 1, "%s(), LAP_DISC_INDICATION\n", __FUNCTION__); reason = LM_USER_REQUEST; break; case LAP_NO_RESPONSE: /* To many retransmits without response */ IRDA_DEBUG( 1, "%s(), LAP_NO_RESPONSE\n", __FUNCTION__); reason = LM_LAP_DISCONNECT; break; case LAP_RESET_INDICATION: IRDA_DEBUG( 1, "%s(), LAP_RESET_INDICATION\n", __FUNCTION__); reason = LM_LAP_RESET; break; case LAP_FOUND_NONE: case LAP_MEDIA_BUSY: case LAP_PRIMARY_CONFLICT: IRDA_DEBUG(1, "%s(), LAP_FOUND_NONE, LAP_MEDIA_BUSY or LAP_PRIMARY_CONFLICT\n", __FUNCTION__); reason = LM_CONNECT_FAILURE; break; default: IRDA_DEBUG(1, "%s(), Unknow IrLAP disconnect reason %d!\n", __FUNCTION__, lap_reason); reason = LM_LAP_DISCONNECT; break; } return reason;}#ifdef CONFIG_PROC_FSstruct irlmp_iter_state { hashbin_t *hashbin;};#define LSAP_START_TOKEN ((void *)1)#define LINK_START_TOKEN ((void *)2)static void *irlmp_seq_hb_idx(struct irlmp_iter_state *iter, loff_t *off){ void *element; spin_lock_irq(&iter->hashbin->hb_spinlock); for (element = hashbin_get_first(iter->hashbin); element != NULL; element = hashbin_get_next(iter->hashbin)) { if (!off || *off-- == 0) { /* NB: hashbin left locked */ return element; } } spin_unlock_irq(&iter->hashbin->hb_spinlock); iter->hashbin = NULL; return NULL;}static void *irlmp_seq_start(struct seq_file *seq, loff_t *pos){ struct irlmp_iter_state *iter = seq->private; void *v; loff_t off = *pos; iter->hashbin = NULL; if (off-- == 0) return LSAP_START_TOKEN; iter->hashbin = irlmp->unconnected_lsaps; v = irlmp_seq_hb_idx(iter, &off); if (v) return v; if (off-- == 0) return LINK_START_TOKEN; iter->hashbin = irlmp->links; return irlmp_seq_hb_idx(iter, &off);}static void *irlmp_seq_next(struct seq_file *seq, void *v, loff_t *pos){ struct irlmp_iter_state *iter = seq->private; ++*pos; if (v == LSAP_START_TOKEN) { /* start of list of lsaps */ iter->hashbin = irlmp->unconnected_lsaps; v = irlmp_seq_hb_idx(iter, NULL); return v ? v : LINK_START_TOKEN; } if (v == LINK_START_TOKEN) { /* start of list of links */ iter->hashbin = irlmp->links; return irlmp_seq_hb_idx(iter, NULL); } v = hashbin_get_next(iter->hashbin); if (v == NULL) { /* no more in this hash bin */ spin_unlock_irq(&iter->hashbin->hb_spinlock); if (iter->hashbin == irlmp->unconnected_lsaps) v = LINK_START_TOKEN; iter->hashbin = NULL; } return v;}static void irlmp_seq_stop(struct seq_file *seq, void *v){ struct irlmp_iter_state *iter = seq->private; if (iter->hashbin) spin_unlock_irq(&iter->hashbin->hb_spinlock);}static int irlmp_seq_show(struct seq_file *seq, void *v){ const struct irlmp_iter_state *iter = seq->private; struct lsap_cb *self = v; if (v == LSAP_START_TOKEN) seq_puts(seq, "Unconnected LSAPs:\n"); else if (v == LINK_START_TOKEN) seq_puts(seq, "\nRegistered Link Layers:\n"); else if (iter->hashbin == irlmp->unconnected_lsaps) { self = v; IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -EINVAL; ); seq_printf(seq, "lsap state: %s, ", irlsap_state[ self->lsap_state]); seq_printf(seq, "slsap_sel: %#02x, dlsap_sel: %#02x, ", self->slsap_sel, self->dlsap_sel); seq_printf(seq, "(%s)", self->notify.name); seq_printf(seq, "\n"); } else if (iter->hashbin == irlmp->links) { struct lap_cb *lap = v; seq_printf(seq, "lap state: %s, ", irlmp_state[lap->lap_state]); seq_printf(seq, "saddr: %#08x, daddr: %#08x, ", lap->saddr, lap->daddr); seq_printf(seq, "num lsaps: %d", HASHBIN_GET_SIZE(lap->lsaps)); seq_printf(seq, "\n"); /* Careful for priority inversions here ! * All other uses of attrib spinlock are independent of * the object spinlock, so we are safe. Jean II */ spin_lock(&lap->lsaps->hb_spinlock); seq_printf(seq, "\n Connected LSAPs:\n"); for (self = (struct lsap_cb *) hashbin_get_first(lap->lsaps); self != NULL; self = (struct lsap_cb *)hashbin_get_next(lap->lsaps)) { IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, goto outloop;); seq_printf(seq, " lsap state: %s, ", irlsap_state[ self->lsap_state]); seq_printf(seq, "slsap_sel: %#02x, dlsap_sel: %#02x, ", self->slsap_sel, self->dlsap_sel); seq_printf(seq, "(%s)", self->notify.name); seq_putc(seq, '\n'); } IRDA_ASSERT_LABEL(outloop:) spin_unlock(&lap->lsaps->hb_spinlock); seq_putc(seq, '\n'); } else return -EINVAL; return 0;}static const struct seq_operations irlmp_seq_ops = { .start = irlmp_seq_start, .next = irlmp_seq_next, .stop = irlmp_seq_stop, .show = irlmp_seq_show,};static int irlmp_seq_open(struct inode *inode, struct file *file){ IRDA_ASSERT(irlmp != NULL, return -EINVAL;); return seq_open_private(file, &irlmp_seq_ops, sizeof(struct irlmp_iter_state));}const struct file_operations irlmp_seq_fops = { .owner = THIS_MODULE, .open = irlmp_seq_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release_private,};#endif /* PROC_FS */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -