📄 af_irda.c
字号:
IRDA_DEBUG( 0, __FUNCTION__ "(), Unable to allocate TSAP!\n"); return -ENOMEM; } /* Remember which TSAP selector we actually got */ self->stsap_sel = self->tsap->stsap_sel; return 0;}/* * Function irda_open_lsap (self) * * Open local Link Service Access Point (LSAP). Used for opening Ultra * sockets */#ifdef CONFIG_IRDA_ULTRAstatic int irda_open_lsap(struct irda_sock *self, int pid){ notify_t notify; if (self->lsap) { WARNING(__FUNCTION__ "(), busy!\n"); return -EBUSY; } /* Initialize callbacks to be used by the IrDA stack */ irda_notify_init(¬ify); notify.udata_indication = irda_data_indication; notify.instance = self; strncpy(notify.name, "Ultra", NOTIFY_MAX_NAME); self->lsap = irlmp_open_lsap(LSAP_CONNLESS, ¬ify, pid); if (self->lsap == NULL) { IRDA_DEBUG( 0, __FUNCTION__ "(), Unable to allocate LSAP!\n"); return -ENOMEM; } return 0;}#endif /* CONFIG_IRDA_ULTRA *//* * Function irda_find_lsap_sel (self, name) * * Try to lookup LSAP selector in remote LM-IAS * * Basically, we start a IAP query, and then go to sleep. When the query * return, irda_getvalue_confirm will wake us up, and we can examine the * result of the query... * Note that in some case, the query fail even before we go to sleep, * creating some races... */static int irda_find_lsap_sel(struct irda_sock *self, char *name){ IRDA_DEBUG(2, __FUNCTION__ "(), name=%s\n", name); ASSERT(self != NULL, return -1;); if (self->iriap) { WARNING(__FUNCTION__ "(), busy with a previous query\n"); return -EBUSY; } self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, irda_getvalue_confirm); /* Treat unexpected signals as disconnect */ self->errno = -EHOSTUNREACH; /* Query remote LM-IAS */ iriap_getvaluebyclass_request(self->iriap, self->saddr, self->daddr, name, "IrDA:TinyTP:LsapSel"); /* Wait for answer (if not already failed) */ if(self->iriap != NULL) interruptible_sleep_on(&self->query_wait); /* Check what happened */ if (self->errno) { /* Requested object/attribute doesn't exist */ if((self->errno == IAS_CLASS_UNKNOWN) || (self->errno == IAS_ATTRIB_UNKNOWN)) return (-EADDRNOTAVAIL); else return (-EHOSTUNREACH); } /* Get the remote TSAP selector */ switch (self->ias_result->type) { case IAS_INTEGER: IRDA_DEBUG(4, __FUNCTION__ "() int=%d\n", self->ias_result->t.integer); if (self->ias_result->t.integer != -1) self->dtsap_sel = self->ias_result->t.integer; else self->dtsap_sel = 0; break; default: self->dtsap_sel = 0; IRDA_DEBUG(0, __FUNCTION__ "(), bad type!\n"); break; } if (self->ias_result) irias_delete_value(self->ias_result); if (self->dtsap_sel) return 0; return -EADDRNOTAVAIL;}/* * Function irda_discover_daddr_and_lsap_sel (self, name) * * This try to find a device with the requested service. * * It basically look into the discovery log. For each address in the list, * it queries the LM-IAS of the device to find if this device offer * the requested service. * If there is more than one node supporting the service, we complain * to the user (it should move devices around). * The, we set both the destination address and the lsap selector to point * on the service on the unique device we have found. * * Note : this function fails if there is more than one device in range, * because IrLMP doesn't disconnect the LAP when the last LSAP is closed. * Moreover, we would need to wait the LAP disconnection... */static int irda_discover_daddr_and_lsap_sel(struct irda_sock *self, char *name){ struct irda_device_info *discoveries; /* Copy of the discovery log */ int number; /* Number of nodes in the log */ int i; int err = -ENETUNREACH; __u32 daddr = DEV_ADDR_ANY; /* Address we found the service on */ __u8 dtsap_sel = 0x0; /* TSAP associated with it */ IRDA_DEBUG(2, __FUNCTION__ "(), name=%s\n", name); ASSERT(self != NULL, return -1;); /* Ask lmp for the current discovery log * Note : we have to use irlmp_get_discoveries(), as opposed * to play with the cachelog directly, because while we are * making our ias query, le log might change... */ discoveries = irlmp_get_discoveries(&number, self->mask); /* Check if the we got some results */ if (discoveries == NULL) return -ENETUNREACH; /* No nodes discovered */ /* * Now, check all discovered devices (if any), and connect * client only about the services that the client is * interested in... */ for(i = 0; i < number; i++) { /* Try the address in the log */ self->daddr = discoveries[i].daddr; self->saddr = 0x0; IRDA_DEBUG(1, __FUNCTION__ "(), trying daddr = %08x\n", self->daddr); /* Query remote LM-IAS for this service */ err = irda_find_lsap_sel(self, name); switch (err) { case 0: /* We found the requested service */ if(daddr != DEV_ADDR_ANY) { IRDA_DEBUG(1, __FUNCTION__ "(), discovered service ''%s'' in two different devices !!!\n", name); self->daddr = DEV_ADDR_ANY; kfree(discoveries); return(-ENOTUNIQ); } /* First time we found that one, save it ! */ daddr = self->daddr; dtsap_sel = self->dtsap_sel; break; case -EADDRNOTAVAIL: /* Requested service simply doesn't exist on this node */ break; default: /* Something bad did happen :-( */ IRDA_DEBUG(0, __FUNCTION__ "(), unexpected IAS query failure\n"); self->daddr = DEV_ADDR_ANY; kfree(discoveries); return(-EHOSTUNREACH); break; } } /* Cleanup our copy of the discovery log */ kfree(discoveries); /* Check out what we found */ if(daddr == DEV_ADDR_ANY) { IRDA_DEBUG(1, __FUNCTION__ "(), cannot discover service ''%s'' in any device !!!\n", name); self->daddr = DEV_ADDR_ANY; return(-EADDRNOTAVAIL); } /* Revert back to discovered device & service */ self->daddr = daddr; self->saddr = 0x0; self->dtsap_sel = dtsap_sel; IRDA_DEBUG(1, __FUNCTION__ "(), discovered requested service ''%s'' at address %08x\n", name, self->daddr); return 0;}/* * Function irda_getname (sock, uaddr, uaddr_len, peer) * * Return the our own, or peers socket address (sockaddr_irda) * */static int irda_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer){ struct sockaddr_irda saddr; struct sock *sk = sock->sk; struct irda_sock *self = sk->protinfo.irda; if (peer) { if (sk->state != TCP_ESTABLISHED) return -ENOTCONN; saddr.sir_family = AF_IRDA; saddr.sir_lsap_sel = self->dtsap_sel; saddr.sir_addr = self->daddr; } else { saddr.sir_family = AF_IRDA; saddr.sir_lsap_sel = self->stsap_sel; saddr.sir_addr = self->saddr; } IRDA_DEBUG(1, __FUNCTION__ "(), tsap_sel = %#x\n", saddr.sir_lsap_sel); IRDA_DEBUG(1, __FUNCTION__ "(), addr = %08x\n", saddr.sir_addr); /* uaddr_len come to us uninitialised */ *uaddr_len = sizeof (struct sockaddr_irda); memcpy(uaddr, &saddr, *uaddr_len); return 0;}/* * Function irda_listen (sock, backlog) * * Just move to the listen state * */static int irda_listen(struct socket *sock, int backlog){ struct sock *sk = sock->sk; IRDA_DEBUG(2, __FUNCTION__ "()\n"); if ((sk->type != SOCK_STREAM) && (sk->type != SOCK_SEQPACKET) && (sk->type != SOCK_DGRAM)) return -EOPNOTSUPP; if (sk->state != TCP_LISTEN) { sk->max_ack_backlog = backlog; sk->state = TCP_LISTEN; return 0; } return -EOPNOTSUPP;}/* * Function irda_bind (sock, uaddr, addr_len) * * Used by servers to register their well known TSAP * */static int irda_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len){ struct sock *sk = sock->sk; struct sockaddr_irda *addr = (struct sockaddr_irda *) uaddr; struct irda_sock *self; __u16 hints = 0; int err; IRDA_DEBUG(2, __FUNCTION__ "()\n"); self = sk->protinfo.irda; ASSERT(self != NULL, return -1;); if (addr_len != sizeof(struct sockaddr_irda)) return -EINVAL;#ifdef CONFIG_IRDA_ULTRA /* Special care for Ultra sockets */ if ((sk->type == SOCK_DGRAM) && (sk->protocol == IRDAPROTO_ULTRA)) { self->pid = addr->sir_lsap_sel; if (self->pid & 0x80) { IRDA_DEBUG(0, __FUNCTION__ "(), extension in PID not supp!\n"); return -EOPNOTSUPP; } err = irda_open_lsap(self, self->pid); if (err < 0) return err; self->max_data_size = ULTRA_MAX_DATA - LMP_PID_HEADER; self->max_header_size = IRDA_MAX_HEADER + LMP_PID_HEADER; /* Pretend we are connected */ sock->state = SS_CONNECTED; sk->state = TCP_ESTABLISHED; return 0; }#endif /* CONFIG_IRDA_ULTRA */ err = irda_open_tsap(self, addr->sir_lsap_sel, addr->sir_name); if (err < 0) return err; /* Register with LM-IAS */ self->ias_obj = irias_new_object(addr->sir_name, jiffies); irias_add_integer_attrib(self->ias_obj, "IrDA:TinyTP:LsapSel", self->stsap_sel, IAS_KERNEL_ATTR); irias_insert_object(self->ias_obj); #if 1 /* Will be removed in near future */ /* Fill in some default hint bits values */ if (strncmp(addr->sir_name, "OBEX", 4) == 0) hints = irlmp_service_to_hint(S_OBEX); if (hints) self->skey = irlmp_register_service(hints);#endif return 0;}/* * Function irda_accept (sock, newsock, flags) * * Wait for incomming connection * */static int irda_accept(struct socket *sock, struct socket *newsock, int flags){ struct irda_sock *self, *new; struct sock *sk = sock->sk; struct sock *newsk; struct sk_buff *skb; int err; IRDA_DEBUG(2, __FUNCTION__ "()\n"); self = sk->protinfo.irda; ASSERT(self != NULL, return -1;); err = irda_create(newsock, sk->protocol); if (err) return err; if (sock->state != SS_UNCONNECTED) return -EINVAL; if ((sk = sock->sk) == NULL) return -EINVAL; if ((sk->type != SOCK_STREAM) && (sk->type != SOCK_SEQPACKET) && (sk->type != SOCK_DGRAM)) return -EOPNOTSUPP; if (sk->state != TCP_LISTEN) return -EINVAL; /* * The read queue this time is holding sockets ready to use * hooked into the SABM we saved */ do { if ((skb = skb_dequeue(&sk->receive_queue)) == NULL) { if (flags & O_NONBLOCK) return -EWOULDBLOCK; interruptible_sleep_on(sk->sleep); if (signal_pending(current)) return -ERESTARTSYS; } } while (skb == NULL); newsk = newsock->sk; newsk->state = TCP_ESTABLISHED; new = newsk->protinfo.irda; ASSERT(new != NULL, return -1;); /* Now attach up the new socket */ new->tsap = irttp_dup(self->tsap, new); if (!new->tsap) { IRDA_DEBUG(0, __FUNCTION__ "(), dup failed!\n"); return -1; } 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_sdu_size_tx = self->max_sdu_size_tx; new->max_sdu_size_rx = self->max_sdu_size_rx; new->max_data_size = self->max_data_size; new->max_header_size = self->max_header_size; memcpy(&new->qos_tx, &self->qos_tx, sizeof(struct qos_info)); /* 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; skb->sk = NULL; skb->destructor = NULL; kfree_skb(skb); sk->ack_backlog--; newsock->state = SS_CONNECTED; irda_connect_response(new); return 0;}/* * Function irda_connect (sock, uaddr, addr_len, flags) * * Connect to a IrDA device * * The main difference with a "standard" connect is that with IrDA we need * to resolve the service name into a TSAP selector (in TCP, port number * doesn't have to be resolved). * Because of this service name resoltion, we can offer "auto-connect", * where we connect to a service without specifying a destination address. * * Note : by consulting "errno", the user space caller may learn the cause * of the failure. Most of them are visible in the function, others may come * from subroutines called and are listed here : * o EBUSY : already processing a connect * o EHOSTUNREACH : bad addr->sir_addr argument * o EADDRNOTAVAIL : bad addr->sir_name argument * o ENOTUNIQ : more than one node has addr->sir_name (auto-connect) * o ENETUNREACH : no node found on the network (auto-connect) */static int irda_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags){ struct sock *sk = sock->sk; struct sockaddr_irda *addr = (struct sockaddr_irda *) uaddr; struct irda_sock *self; int err; IRDA_DEBUG(2, __FUNCTION__ "()\n"); self = sk->protinfo.irda; /* Don't allow connect for Ultra sockets */ if ((sk->type == SOCK_DGRAM) && (sk->protocol == IRDAPROTO_ULTRA)) return -ESOCKTNOSUPPORT; if (sk->state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) { sock->state = SS_CONNECTED; return 0; /* Connect completed during a ERESTARTSYS event */ } if (sk->state == TCP_CLOSE && sock->state == SS_CONNECTING) { sock->state = SS_UNCONNECTED; return -ECONNREFUSED; } if (sk->state == TCP_ESTABLISHED) return -EISCONN; /* No reconnect on a seqpacket socket */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -