MODULE_DESCRIPTION("ST-Ericsson CAIF modem protocol socket support (AF_CAIF)");
MODULE_LICENSE("GPL");
MODULE_ALIAS_NETPROTO(AF_CAIF);
/* * CAIF state is re-using the TCP socket states. * caif_states stored in sk_state reflect the state as reported by * the CAIF stack, while sk_socket->state is the state of the socket.
*/ enum caif_states {
CAIF_CONNECTED = TCP_ESTABLISHED,
CAIF_CONNECTING = TCP_SYN_SENT,
CAIF_DISCONNECTED = TCP_CLOSE
};
#define TX_FLOW_ON_BIT 1 #define RX_FLOW_ON_BIT 2
struct caifsock { struct sock sk; /* must be first member */ struct cflayer layer; unsignedlong flow_state; struct caif_connect_request conn_req; struct mutex readlock; struct dentry *debugfs_socket_dir; int headroom, tailroom, maxframe;
};
/* Packet Control Callback function called from CAIF */ staticvoid caif_ctrl_cb(struct cflayer *layr, enum caif_ctrlcmd flow, int phyid)
{ struct caifsock *cf_sk = container_of(layr, struct caifsock, layer); switch (flow) { case CAIF_CTRLCMD_FLOW_ON_IND: /* OK from modem to start sending again */
set_tx_flow_on(cf_sk);
cf_sk->sk.sk_state_change(&cf_sk->sk); break;
case CAIF_CTRLCMD_FLOW_OFF_IND: /* Modem asks us to shut up */
set_tx_flow_off(cf_sk);
cf_sk->sk.sk_state_change(&cf_sk->sk); break;
case CAIF_CTRLCMD_INIT_RSP: /* We're now connected */
caif_client_register_refcnt(&cf_sk->layer,
cfsk_hold, cfsk_put);
cf_sk->sk.sk_state = CAIF_CONNECTED;
set_tx_flow_on(cf_sk);
cf_sk->sk.sk_shutdown = 0;
cf_sk->sk.sk_state_change(&cf_sk->sk); break;
case CAIF_CTRLCMD_DEINIT_RSP: /* We're now disconnected */
cf_sk->sk.sk_state = CAIF_DISCONNECTED;
cf_sk->sk.sk_state_change(&cf_sk->sk); break;
case CAIF_CTRLCMD_INIT_FAIL_RSP: /* Connect request failed */
cf_sk->sk.sk_err = ECONNREFUSED;
cf_sk->sk.sk_state = CAIF_DISCONNECTED;
cf_sk->sk.sk_shutdown = SHUTDOWN_MASK; /* * Socket "standards" seems to require POLLOUT to * be set at connect failure.
*/
set_tx_flow_on(cf_sk);
cf_sk->sk.sk_state_change(&cf_sk->sk); break;
case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND: /* Modem has closed this connection, or device is down. */
cf_sk->sk.sk_shutdown = SHUTDOWN_MASK;
cf_sk->sk.sk_err = ECONNRESET;
set_rx_flow_on(cf_sk);
sk_error_report(&cf_sk->sk); break;
/* Mark read part of skb as used */ if (!(flags & MSG_PEEK)) {
skb_pull(skb, chunk);
/* put the skb back if we didn't use it up. */ if (skb->len) {
skb_queue_head(&sk->sk_receive_queue, skb); break;
}
kfree_skb(skb);
} else { /* * It is questionable, see note in unix_dgram_recvmsg.
*/ /* put message back and return */
skb_queue_head(&sk->sk_receive_queue, skb); break;
}
} while (size);
caif_read_unlock(sk);
out: return copied ? : err;
}
/* * Copied from sock.c:sock_wait_for_wmem, but change to wait for * CAIF flow-on and sock_writable.
*/ staticlong caif_wait_for_flow_on(struct caifsock *cf_sk, int wait_writeable, long timeo, int *err)
{ struct sock *sk = &cf_sk->sk;
DEFINE_WAIT(wait); for (;;) {
*err = 0; if (tx_flow_is_on(cf_sk) &&
(!wait_writeable || sock_writeable(&cf_sk->sk))) break;
*err = -ETIMEDOUT; if (!timeo) break;
*err = -ERESTARTSYS; if (signal_pending(current)) break;
prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
*err = -ECONNRESET; if (sk->sk_shutdown & SHUTDOWN_MASK) break;
*err = -sk->sk_err; if (sk->sk_err) break;
*err = -EPIPE; if (cf_sk->sk.sk_state != CAIF_CONNECTED) break;
timeo = schedule_timeout(timeo);
}
finish_wait(sk_sleep(sk), &wait); return timeo;
}
/* * Transmit a SKB. The device may temporarily request re-transmission * by returning EAGAIN.
*/ staticint transmit_skb(struct sk_buff *skb, struct caifsock *cf_sk, int noblock, long timeo)
{ struct cfpkt *pkt;
if (ret) goto err;
ret = -EPIPE; if (cf_sk->sk.sk_state != CAIF_CONNECTED ||
sock_flag(sk, SOCK_DEAD) ||
(sk->sk_shutdown & RCV_SHUTDOWN)) goto err;
/* Error if trying to write more than maximum frame size. */
ret = -EMSGSIZE; if (len > cf_sk->maxframe && cf_sk->sk.sk_protocol != CAIFPROTO_RFM) goto err;
buffer_size = len + cf_sk->headroom + cf_sk->tailroom;
ret = -ENOMEM;
skb = sock_alloc_send_skb(sk, buffer_size, noblock, &ret);
if (!skb || skb_tailroom(skb) < buffer_size) goto err;
skb_reserve(skb, cf_sk->headroom);
ret = memcpy_from_msg(skb_put(skb, len), msg, len);
if (ret) goto err;
ret = transmit_skb(skb, cf_sk, noblock, timeo); if (ret < 0) /* skb is already freed */ return ret;
return len;
err:
kfree_skb(skb); return ret;
}
/* * Copied from unix_stream_sendmsg and adapted to CAIF: * Changed removed permission handling and added waiting for flow on * and other minor adaptations.
*/ staticint caif_stream_sendmsg(struct socket *sock, struct msghdr *msg,
size_t len)
{ struct sock *sk = sock->sk; struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); int err, size; struct sk_buff *skb; int sent = 0; long timeo;
err = -EOPNOTSUPP; if (unlikely(msg->msg_flags&MSG_OOB)) goto out_err;
skb_reserve(skb, cf_sk->headroom); /* * If you pass two values to the sock_alloc_send_skb * it tries to grab the large buffer with GFP_NOFS * (which can fail easily), and if it fails grab the * fallback size buffer which is under a page and will * succeed. [Alan]
*/
size = min_t(int, size, skb_tailroom(skb));
pipe_err: if (sent == 0 && !(msg->msg_flags&MSG_NOSIGNAL))
send_sig(SIGPIPE, current, 0);
err = -EPIPE;
out_err: return sent ? : err;
}
staticint setsockopt(struct socket *sock, int lvl, int opt, sockptr_t ov, unsignedint ol)
{ struct sock *sk = sock->sk; struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); int linksel;
if (cf_sk->sk.sk_socket->state != SS_UNCONNECTED) return -ENOPROTOOPT;
switch (opt) { case CAIFSO_LINK_SELECT: if (ol < sizeof(int)) return -EINVAL; if (lvl != SOL_CAIF) goto bad_sol; if (copy_from_sockptr(&linksel, ov, sizeof(int))) return -EINVAL;
lock_sock(&(cf_sk->sk));
cf_sk->conn_req.link_selector = linksel;
release_sock(&cf_sk->sk); return 0;
case CAIFSO_REQ_PARAM: if (lvl != SOL_CAIF) goto bad_sol; if (cf_sk->sk.sk_protocol != CAIFPROTO_UTIL) return -ENOPROTOOPT;
lock_sock(&(cf_sk->sk)); if (ol > sizeof(cf_sk->conn_req.param.data) ||
copy_from_sockptr(&cf_sk->conn_req.param.data, ov, ol)) {
release_sock(&cf_sk->sk); return -EINVAL;
}
cf_sk->conn_req.param.size = ol;
release_sock(&cf_sk->sk); return 0;
default: return -ENOPROTOOPT;
}
return 0;
bad_sol: return -ENOPROTOOPT;
}
/* * caif_connect() - Connect a CAIF Socket * Copied and modified af_irda.c:irda_connect(). * * 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 -EAFNOSUPPORT: bad socket family or type. * o -ESOCKTNOSUPPORT: bad socket type or protocol * o -EINVAL: bad socket address, or CAIF link type * o -ECONNREFUSED: remote end refused the connection. * o -EINPROGRESS: connect request sent but timed out (or non-blocking) * o -EISCONN: already connected. * o -ETIMEDOUT: Connection timed out (send timeout) * o -ENODEV: No link layer to send request * o -ECONNRESET: Received Shutdown indication or lost link layer * o -ENOMEM: Out of memory * * State Strategy: * o sk_state: holds the CAIF_* protocol state, it's updated by * caif_ctrl_cb. * o sock->state: holds the SS_* socket state and is updated by connect and * disconnect.
*/ staticint caif_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
{ struct sock *sk = sock->sk; struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); long timeo; int err; int ifindex, headroom, tailroom; unsignedint mtu; struct net_device *dev;
/* Check priority value comming from socket */ /* if priority value is out of range it will be ajusted */ if (cf_sk->sk.sk_priority > CAIF_PRIO_MAX)
cf_sk->conn_req.priority = CAIF_PRIO_MAX; elseif (cf_sk->sk.sk_priority < CAIF_PRIO_MIN)
cf_sk->conn_req.priority = CAIF_PRIO_MIN; else
cf_sk->conn_req.priority = cf_sk->sk.sk_priority;
/*ifindex = id of the interface.*/
cf_sk->conn_req.ifindex = cf_sk->sk.sk_bound_dev_if;
/* * Ensure that packets are not queued after this point in time. * caif_queue_rcv_skb checks SOCK_DEAD holding the queue lock, * this ensures no packets when sock is dead.
*/
spin_lock_bh(&sk->sk_receive_queue.lock);
sock_set_flag(sk, SOCK_DEAD);
spin_unlock_bh(&sk->sk_receive_queue.lock);
sock->sk = NULL;
/* * we set writable also when the other side has shut down the * connection. This prevents stuck sockets.
*/ if (sock_writeable(sk) && tx_flow_is_on(cf_sk))
mask |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND;
if (!capable(CAP_SYS_ADMIN) && !capable(CAP_NET_ADMIN)) return -EPERM; /* * The sock->type specifies the socket type to use. * The CAIF socket is a packet stream in the sense * that it is packet based. CAIF trusts the reliability * of the link, no resending is implemented.
*/ if (sock->type == SOCK_SEQPACKET)
sock->ops = &caif_seqpacket_ops; elseif (sock->type == SOCK_STREAM)
sock->ops = &caif_stream_ops; else return -ESOCKTNOSUPPORT;
if (protocol < 0 || protocol >= CAIFPROTO_MAX) return -EPROTONOSUPPORT; /* * Set the socket state to unconnected. The socket state * is really not used at all in the net/core or socket.c but the * initialization makes sure that sock->state is not uninitialized.
*/
sk = sk_alloc(net, PF_CAIF, GFP_KERNEL, &prot, kern); if (!sk) return -ENOMEM;
cf_sk = container_of(sk, struct caifsock, sk);
/* Store the protocol */
sk->sk_protocol = (unsignedchar) protocol;
/* Initialize default priority for well-known cases */ switch (protocol) { case CAIFPROTO_AT:
sk->sk_priority = TC_PRIO_CONTROL; break; case CAIFPROTO_RFM:
sk->sk_priority = TC_PRIO_INTERACTIVE_BULK; break; default:
sk->sk_priority = TC_PRIO_BESTEFFORT;
}
/* * Lock in order to try to stop someone from opening the socket * too early.
*/
lock_sock(&(cf_sk->sk));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.