📄 af_irda.c
字号:
* */static int irda_sendmsg_dgram(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm){ struct sock *sk = sock->sk; struct irda_sock *self; struct sk_buff *skb; unsigned char *asmptr; int err; IRDA_DEBUG(4, __FUNCTION__ "(), len=%d\n", len); if (msg->msg_flags & ~MSG_DONTWAIT) return -EINVAL; if (sk->shutdown & SEND_SHUTDOWN) { send_sig(SIGPIPE, current, 0); return -EPIPE; } if (sk->state != TCP_ESTABLISHED) return -ENOTCONN; self = sk->protinfo.irda; ASSERT(self != NULL, return -1;); /* * Check that we don't send out to big frames. This is an unreliable * service, so we have no fragmentation and no coalescence */ if (len > self->max_data_size) { IRDA_DEBUG(0, __FUNCTION__ "(), Warning to much data! " "Chopping frame from %d to %d bytes!\n", len, self->max_data_size); len = self->max_data_size; } skb = sock_alloc_send_skb(sk, len + self->max_header_size, 0, msg->msg_flags & MSG_DONTWAIT, &err); if (!skb) return -ENOBUFS; skb_reserve(skb, self->max_header_size); IRDA_DEBUG(4, __FUNCTION__ "(), appending user data\n"); asmptr = skb->h.raw = skb_put(skb, len); memcpy_fromiovec(asmptr, msg->msg_iov, len); /* * Just send the message to TinyTP, and let it deal with possible * errors. No need to duplicate all that here */ err = irttp_udata_request(self->tsap, skb); if (err) { IRDA_DEBUG(0, __FUNCTION__ "(), err=%d\n", err); return err; } return len;}/* * Function irda_sendmsg_ultra (sock, msg, len, scm) * * Send message down to IrLMP for the unreliable Ultra * packet service... */#ifdef CONFIG_IRDA_ULTRAstatic int irda_sendmsg_ultra(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm){ struct sock *sk = sock->sk; struct irda_sock *self; struct sk_buff *skb; unsigned char *asmptr; int err; IRDA_DEBUG(4, __FUNCTION__ "(), len=%d\n", len); if (msg->msg_flags & ~MSG_DONTWAIT) return -EINVAL; if (sk->shutdown & SEND_SHUTDOWN) { send_sig(SIGPIPE, current, 0); return -EPIPE; } self = sk->protinfo.irda; ASSERT(self != NULL, return -1;); /* * Check that we don't send out to big frames. This is an unreliable * service, so we have no fragmentation and no coalescence */ if (len > self->max_data_size) { IRDA_DEBUG(0, __FUNCTION__ "(), Warning to much data! " "Chopping frame from %d to %d bytes!\n", len, self->max_data_size); len = self->max_data_size; } skb = sock_alloc_send_skb(sk, len + self->max_header_size, 0, msg->msg_flags & MSG_DONTWAIT, &err); if (!skb) return -ENOBUFS; skb_reserve(skb, self->max_header_size); IRDA_DEBUG(4, __FUNCTION__ "(), appending user data\n"); asmptr = skb->h.raw = skb_put(skb, len); memcpy_fromiovec(asmptr, msg->msg_iov, len); err = irlmp_connless_data_request(self->lsap, skb); if (err) { IRDA_DEBUG(0, __FUNCTION__ "(), err=%d\n", err); return err; } return len;}#endif /* CONFIG_IRDA_ULTRA *//* * Function irda_shutdown (sk, how) * * * */static int irda_shutdown(struct socket *sock, int how){ struct irda_sock *self; struct sock *sk = sock->sk; IRDA_DEBUG(0, __FUNCTION__ "()\n"); self = sk->protinfo.irda; ASSERT(self != NULL, return -1;); sk->state = TCP_CLOSE; sk->shutdown |= SEND_SHUTDOWN; sk->state_change(sk); if (self->iriap) { iriap_close(self->iriap); self->iriap = NULL; } if (self->tsap) { irttp_disconnect_request(self->tsap, NULL, P_NORMAL); irttp_close_tsap(self->tsap); self->tsap = NULL; } /* A few cleanup so the socket look as good as new... */ self->rx_flow = self->tx_flow = FLOW_START; /* needed ??? */ self->daddr = DEV_ADDR_ANY; /* Until we get re-connected */ self->saddr = 0x0; /* so IrLMP assign us any link */ return 0;}/* * Function irda_poll (file, sock, wait) * * * */static unsigned int irda_poll(struct file * file, struct socket *sock, poll_table *wait){ struct sock *sk = sock->sk; unsigned int mask; IRDA_DEBUG(4, __FUNCTION__ "()\n"); poll_wait(file, sk->sleep, wait); mask = 0; /* exceptional events? */ if (sk->err) mask |= POLLERR; if (sk->shutdown & RCV_SHUTDOWN) mask |= POLLHUP; /* readable? */ if (!skb_queue_empty(&sk->receive_queue)) { IRDA_DEBUG(4, "Socket is readable\n"); mask |= POLLIN | POLLRDNORM; } /* Connection-based need to check for termination and startup */ if (sk->type == SOCK_STREAM && sk->state==TCP_CLOSE) mask |= POLLHUP; /* * we set writable also when the other side has shut down the * connection. This prevents stuck sockets. */ if (sk->sndbuf - (int)atomic_read(&sk->wmem_alloc) >= SOCK_MIN_WRITE_SPACE) mask |= POLLOUT | POLLWRNORM | POLLWRBAND; return mask;}/* * Function irda_ioctl (sock, cmd, arg) * * * */static int irda_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg){ struct sock *sk = sock->sk; IRDA_DEBUG(4, __FUNCTION__ "(), cmd=%#x\n", cmd); switch (cmd) { case TIOCOUTQ: { long amount; amount = sk->sndbuf - atomic_read(&sk->wmem_alloc); if (amount < 0) amount = 0; if (put_user(amount, (unsigned int *)arg)) return -EFAULT; return 0; } case TIOCINQ: { struct sk_buff *skb; long amount = 0L; /* These two are safe on a single CPU system as only user tasks fiddle here */ if ((skb = skb_peek(&sk->receive_queue)) != NULL) amount = skb->len; if (put_user(amount, (unsigned int *)arg)) return -EFAULT; return 0; } case SIOCGSTAMP: if (sk != NULL) { if (sk->stamp.tv_sec == 0) return -ENOENT; if (copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval))) return -EFAULT; return 0; } return -EINVAL; case SIOCGIFADDR: case SIOCSIFADDR: case SIOCGIFDSTADDR: case SIOCSIFDSTADDR: case SIOCGIFBRDADDR: case SIOCSIFBRDADDR: case SIOCGIFNETMASK: case SIOCSIFNETMASK: case SIOCGIFMETRIC: case SIOCSIFMETRIC: return -EINVAL; default: IRDA_DEBUG(1, __FUNCTION__ "(), doing device ioctl!\n"); return dev_ioctl(cmd, (void *) arg); } /*NOTREACHED*/ return 0;}/* * Function irda_setsockopt (sock, level, optname, optval, optlen) * * Set some options for the socket * */static int irda_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen){ struct sock *sk = sock->sk; struct irda_sock *self; struct irda_ias_set ias_opt; struct ias_object *ias_obj; struct ias_attrib * ias_attr; /* Attribute in IAS object */ int opt; self = sk->protinfo.irda; ASSERT(self != NULL, return -1;); if (level != SOL_IRLMP) return -ENOPROTOOPT; switch (optname) { case IRLMP_IAS_SET: /* The user want to add an attribute to an existing IAS object * (in the IAS database) or to create a new object with this * attribute. * We first query IAS to know if the object exist, and then * create the right attribute... */ if (optlen != sizeof(struct irda_ias_set)) return -EINVAL; /* Copy query to the driver. */ if (copy_from_user(&ias_opt, (char *)optval, optlen)) return -EFAULT; /* Find the object we target */ ias_obj = irias_find_object(ias_opt.irda_class_name); if(ias_obj == (struct ias_object *) NULL) { /* Create a new object */ ias_obj = irias_new_object(ias_opt.irda_class_name, jiffies); } /* Do we have it already ? */ if(irias_find_attrib(ias_obj, ias_opt.irda_attrib_name)) return -EINVAL; /* Look at the type */ switch(ias_opt.irda_attrib_type) { case IAS_INTEGER: /* Add an integer attribute */ irias_add_integer_attrib( ias_obj, ias_opt.irda_attrib_name, ias_opt.attribute.irda_attrib_int, IAS_USER_ATTR); break; case IAS_OCT_SEQ: /* Check length */ if(ias_opt.attribute.irda_attrib_octet_seq.len > IAS_MAX_OCTET_STRING) return -EINVAL; /* Add an octet sequence attribute */ irias_add_octseq_attrib( ias_obj, ias_opt.irda_attrib_name, ias_opt.attribute.irda_attrib_octet_seq.octet_seq, ias_opt.attribute.irda_attrib_octet_seq.len, IAS_USER_ATTR); break; case IAS_STRING: /* Should check charset & co */ /* Check length */ if(ias_opt.attribute.irda_attrib_string.len > IAS_MAX_STRING) return -EINVAL; /* NULL terminate the string (avoid troubles) */ ias_opt.attribute.irda_attrib_string.string[ias_opt.attribute.irda_attrib_string.len] = '\0'; /* Add a string attribute */ irias_add_string_attrib( ias_obj, ias_opt.irda_attrib_name, ias_opt.attribute.irda_attrib_string.string, IAS_USER_ATTR); break; default : return -EINVAL; } irias_insert_object(ias_obj); break; case IRLMP_IAS_DEL: /* The user want to delete an object from our local IAS * database. We just need to query the IAS, check is the * object is not owned by the kernel and delete it. */ if (optlen != sizeof(struct irda_ias_set)) return -EINVAL; /* Copy query to the driver. */ if (copy_from_user(&ias_opt, (char *)optval, optlen)) return -EFAULT; /* Find the object we target */ ias_obj = irias_find_object(ias_opt.irda_class_name); if(ias_obj == (struct ias_object *) NULL) return -EINVAL; /* Find the attribute (in the object) we target */ ias_attr = irias_find_attrib(ias_obj, ias_opt.irda_attrib_name); if(ias_attr == (struct ias_attrib *) NULL) return -EINVAL; /* Check is the user space own the object */ if(ias_attr->value->owner != IAS_USER_ATTR) { IRDA_DEBUG(1, __FUNCTION__ "(), attempting to delete a kernel attribute\n"); return -EPERM; } /* Remove the attribute (and maybe the object) */ irias_delete_attrib(ias_obj, ias_attr); break; case IRLMP_MAX_SDU_SIZE: if (optlen < sizeof(int)) return -EINVAL; if (get_user(opt, (int *)optval)) return -EFAULT; /* Only possible for a seqpacket service (TTP with SAR) */ if (sk->type != SOCK_SEQPACKET) { IRDA_DEBUG(2, __FUNCTION__ "(), setting max_sdu_size = %d\n", opt); self->max_sdu_size_rx = opt; } else { WARNING(__FUNCTION__ "(), not allowed to set MAXSDUSIZE for this " "socket type!\n"); return -ENOPROTOOPT; } break; case IRLMP_HINTS_SET: if (optlen < sizeof(int)) return -EINVAL; if (get_user(opt, (int *)optval)) return -EFAULT; /* Unregister any old registration */ if (self->skey) irlmp_unregister_service(self->skey); self->skey = irlmp_register_service((__u16) opt); break; case IRLMP_HINT_MASK_SET: /* As opposed to the previous case which set the hint bits * that we advertise, this one set the filter we use when * making a discovery (nodes which don't match any hint * bit in the mask are not reported). */ if (optlen < sizeof(int)) return -EINVAL; if (get_user(opt, (int *)optval)) return -EFAULT; /* Set the new hint mask */ self->mask = (__u16) opt; /* Mask out extension bits */ self->mask &= 0x7f7f; /* Check if no bits */ if(!self->mask) self->mask = 0xFFFF; break; default: return -ENOPROTOOPT; } return 0;}/* * Function irda_extract_ias_value(ias_opt, ias_value) * * Translate internal IAS value structure to the user space representation * * The external representation of IAS values, as we exchange them with * user space program is quite different from the internal representation, * as stored in the IAS database (because we need a flat structure for * crossing kernel boundary). * This function transform the former in the latter. We also check * that the value type is valid. */static int irda_extract_ias_value(struct irda_ias_set *ias_opt, struct ias_value *ias_value){ /* Look at the type */ switch (ias_value->type) { case IAS_INTEGER: /* Copy the integer */ ias_opt->attribute.irda_attrib_int = ias_value->t.integer; break; case IAS_OCT_SEQ: /* Set length */ ias_opt->attribute.irda_attrib_octet_seq.len = ias_value->len; /* Copy over */ memcpy(ias_opt->attribute.irda_attrib_octet_seq.octet_seq, ias_value->t.oct_seq, ias_value->len); break; case IAS_STRING: /* Set length */ ias_opt->attribute.irda_attrib_string.len = ias_value->len; ias_opt->attribute.irda_attrib_string.charset = ias_value->charset; /* Copy over */ memcpy(ias_opt->attribute.irda_attrib_string.string,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -