Previously CAIF supported maximum transfer size of ~4050.
The transfer size is now calculated dynamically based on the
link layers mtu size.
Signed-off-by: Sjur Braendeland@stericsson.com
Signed-off-by: David S. Miller <davem@davemloft.net>
        dev->type = ARPHRD_CAIF;
        dev->flags = IFF_POINTOPOINT | IFF_NOARP;
        dev->mtu = CAIF_MAX_MTU;
-       dev->hard_header_len = CAIF_NEEDED_HEADROOM;
        dev->tx_queue_len = 0;
        dev->destructor = free_netdev;
        skb_queue_head_init(&serdev->head);
 
  * @client_layer:      User implementation of client layer. This layer
  *                     MUST have receive and control callback functions
  *                     implemented.
+ * @ifindex:           Link layer interface index used for this connection.
+ * @headroom:          Head room needed by CAIF protocol.
+ * @tailroom:          Tail room needed by CAIF protocol.
  *
  * This function connects a CAIF channel. The Client must implement
  * the struct cflayer. This layer represents the Client layer and holds
  * E.g. CAIF Socket will call this function for each socket it connects
  * and have one client_layer instance for each socket.
  */
-int caif_connect_client(struct caif_connect_request *config,
-                          struct cflayer *client_layer);
+int caif_connect_client(struct caif_connect_request *conn_req,
+                       struct cflayer *client_layer, int *ifindex,
+                       int *headroom, int *tailroom);
 
 /**
  * caif_disconnect_client - Disconnects a client from the CAIF stack.
 
 struct caif_payload_info;
 struct caif_packet_funcs;
 
-#define CAIF_MAX_FRAMESIZE 4096
-#define CAIF_MAX_PAYLOAD_SIZE (4096 - 64)
-#define CAIF_NEEDED_HEADROOM (10)
-#define CAIF_NEEDED_TAILROOM (2)
 
 #define CAIF_LAYER_NAME_SZ 16
-#define CAIF_SUCCESS   1
-#define CAIF_FAILURE   0
 
 /**
  * caif_assert() - Assert function for CAIF.
 
 #ifndef CFCNFG_H_
 #define CFCNFG_H_
 #include <linux/spinlock.h>
+#include <linux/netdevice.h>
 #include <net/caif/caif_layer.h>
 #include <net/caif/cfctrl.h>
 
 
 void
 cfcnfg_add_phy_layer(struct cfcnfg *cnfg, enum cfcnfg_phy_type phy_type,
-                    void *dev, struct cflayer *phy_layer, u16 *phyid,
-                    enum cfcnfg_phy_preference pref,
+                    struct net_device *dev, struct cflayer *phy_layer,
+                    u16 *phyid, enum cfcnfg_phy_preference pref,
                     bool fcs, bool stx);
 
 /**
  * @param:             Link setup parameters.
  * @adap_layer:                Specify the adaptation layer; the receive and
  *                     flow-control functions MUST be set in the structure.
- *
+ * @ifindex:           Link layer interface index used for this connection.
+ * @proto_head:                Protocol head-space needed by CAIF protocol,
+ *                     excluding link layer.
+ * @proto_tail:                Protocol tail-space needed by CAIF protocol,
+ *                     excluding link layer.
  */
 int cfcnfg_add_adaptation_layer(struct cfcnfg *cnfg,
                            struct cfctrl_link_param *param,
-                           struct cflayer *adap_layer);
+                           struct cflayer *adap_layer,
+                           int *ifindex,
+                           int *proto_head,
+                           int *proto_tail);
 
 /**
  * cfcnfg_get_phyid() - Get physical ID, given type.
 
                        pref = CFPHYPREF_HIGH_BW;
                        break;
                }
-
+               dev_hold(dev);
                cfcnfg_add_phy_layer(get_caif_conf(),
                                     phy_type,
                                     dev,
                caifd->layer.up->ctrlcmd(caifd->layer.up,
                                         _CAIF_CTRLCMD_PHYIF_DOWN_IND,
                                         caifd->layer.id);
+               might_sleep();
                res = wait_event_interruptible_timeout(caifd->event,
                                        atomic_read(&caifd->in_use) == 0,
                                        TIMEOUT);
                                   "Unregistering an active CAIF device: %s\n",
                                   __func__, dev->name);
                cfcnfg_del_phy_layer(get_caif_conf(), &caifd->layer);
+               dev_put(dev);
                atomic_set(&caifd->state, what);
                break;
 
 EXPORT_SYMBOL(get_caif_conf);
 
 int caif_connect_client(struct caif_connect_request *conn_req,
-                          struct cflayer *client_layer)
+                       struct cflayer *client_layer, int *ifindex,
+                       int *headroom, int *tailroom)
 {
        struct cfctrl_link_param param;
        int ret;
        if (ret)
                return ret;
        /* Hook up the adaptation layer. */
-       return cfcnfg_add_adaptation_layer(get_caif_conf(),
-                                               ¶m, client_layer);
+       return cfcnfg_add_adaptation_layer(get_caif_conf(), ¶m,
+                                       client_layer, ifindex,
+                                       headroom, tailroom);
 }
 EXPORT_SYMBOL(caif_connect_client);
 
 
 MODULE_LICENSE("GPL");
 MODULE_ALIAS_NETPROTO(AF_CAIF);
 
-#define CAIF_DEF_SNDBUF (CAIF_MAX_PAYLOAD_SIZE*10)
-#define CAIF_DEF_RCVBUF (CAIF_MAX_PAYLOAD_SIZE*100)
+#define CAIF_DEF_SNDBUF (4096*10)
+#define CAIF_DEF_RCVBUF (4096*100)
 
 /*
  * CAIF state is re-using the TCP socket states.
        struct caif_connect_request conn_req;
        struct mutex readlock;
        struct dentry *debugfs_socket_dir;
+       int headroom, tailroom, maxframe;
 };
 
 static int rx_flow_is_on(struct caifsock *cf_sk)
                goto err;
        noblock = msg->msg_flags & MSG_DONTWAIT;
 
-       buffer_size = len + CAIF_NEEDED_HEADROOM + CAIF_NEEDED_TAILROOM;
-
        timeo = sock_sndtimeo(sk, noblock);
        timeo = caif_wait_for_flow_on(container_of(sk, struct caifsock, sk),
                                1, timeo, &ret);
 
+       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)
+
+       if (!skb || skb_tailroom(skb) < buffer_size)
                goto err;
-       skb_reserve(skb, CAIF_NEEDED_HEADROOM);
+
+       skb_reserve(skb, cf_sk->headroom);
 
        ret = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
 
        long timeo;
 
        err = -EOPNOTSUPP;
-
        if (unlikely(msg->msg_flags&MSG_OOB))
                goto out_err;
 
 
                size = len-sent;
 
-               if (size > CAIF_MAX_PAYLOAD_SIZE)
-                       size = CAIF_MAX_PAYLOAD_SIZE;
+               if (size > cf_sk->maxframe)
+                       size = cf_sk->maxframe;
 
                /* If size is more than half of sndbuf, chop up message */
                if (size > ((sk->sk_sndbuf >> 1) - 64))
                        size = SKB_MAX_ALLOC;
 
                skb = sock_alloc_send_skb(sk,
-                                       size + CAIF_NEEDED_HEADROOM
-                                       + CAIF_NEEDED_TAILROOM,
+                                       size + cf_sk->headroom +
+                                       cf_sk->tailroom,
                                        msg->msg_flags&MSG_DONTWAIT,
                                        &err);
                if (skb == NULL)
                        goto out_err;
 
-               skb_reserve(skb, CAIF_NEEDED_HEADROOM);
+               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
        struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
        long timeo;
        int err;
+       int ifindex, headroom, tailroom;
+       struct net_device *dev;
+
        lock_sock(sk);
 
        err = -EAFNOSUPPORT;
        if (uaddr->sa_family != AF_CAIF)
                goto out;
 
-       err = -ESOCKTNOSUPPORT;
-       if (unlikely(!(sk->sk_type == SOCK_STREAM &&
-                      cf_sk->sk.sk_protocol == CAIFPROTO_AT) &&
-                      sk->sk_type != SOCK_SEQPACKET))
-               goto out;
        switch (sock->state) {
        case SS_UNCONNECTED:
                /* Normal case, a fresh connect */
        dbfs_atomic_inc(&cnt.num_connect_req);
        cf_sk->layer.receive = caif_sktrecv_cb;
        err = caif_connect_client(&cf_sk->conn_req,
-                               &cf_sk->layer);
+                               &cf_sk->layer, &ifindex, &headroom, &tailroom);
        if (err < 0) {
                cf_sk->sk.sk_socket->state = SS_UNCONNECTED;
                cf_sk->sk.sk_state = CAIF_DISCONNECTED;
                goto out;
        }
+       dev = dev_get_by_index(sock_net(sk), ifindex);
+       cf_sk->headroom = LL_RESERVED_SPACE_EXTRA(dev, headroom);
+       cf_sk->tailroom = tailroom;
+       cf_sk->maxframe = dev->mtu - (headroom + tailroom);
+       dev_put(dev);
+       if (cf_sk->maxframe < 1) {
+               pr_warning("CAIF: %s(): CAIF Interface MTU too small (%d)\n",
+                       __func__, dev->mtu);
+               err = -ENODEV;
+               goto out;
+       }
 
        err = -EINPROGRESS;
 wait_connect:
 
 #include <linux/kernel.h>
 #include <linux/stddef.h>
 #include <linux/slab.h>
+#include <linux/netdevice.h>
 #include <net/caif/caif_layer.h>
 #include <net/caif/cfpkt.h>
 #include <net/caif/cfcnfg.h>
 
        /* Information about the physical device */
        struct dev_info dev_info;
+
+       /* Interface index */
+       int ifindex;
+
+       /* Use Start of frame extension */
+       bool use_stx;
+
+       /* Use Start of frame checksum */
+       bool use_fcs;
 };
 
 struct cfcnfg {
 {
 }
 
+int protohead[CFCTRL_SRV_MASK] = {
+       [CFCTRL_SRV_VEI] = 4,
+       [CFCTRL_SRV_DATAGRAM] = 7,
+       [CFCTRL_SRV_UTIL] = 4,
+       [CFCTRL_SRV_RFM] = 3,
+       [CFCTRL_SRV_DBG] = 3,
+};
+
 int cfcnfg_add_adaptation_layer(struct cfcnfg *cnfg,
                                struct cfctrl_link_param *param,
-                               struct cflayer *adap_layer)
+                               struct cflayer *adap_layer,
+                               int *ifindex,
+                               int *proto_head,
+                               int *proto_tail)
 {
        struct cflayer *frml;
        if (adap_layer == NULL) {
                     param->phyid);
        caif_assert(cnfg->phy_layers[param->phyid].phy_layer->id ==
                     param->phyid);
+
+       *ifindex = cnfg->phy_layers[param->phyid].ifindex;
+       *proto_head =
+               protohead[param->linktype]+
+               (cnfg->phy_layers[param->phyid].use_stx ? 1 : 0);
+
+       *proto_tail = 2;
+
        /* FIXME: ENUMERATE INITIALLY WHEN ACTIVATING PHYSICAL INTERFACE */
        cfctrl_enum_req(cnfg->ctrl, param->phyid);
        return cfctrl_linkup_request(cnfg->ctrl, param, adap_layer);
        struct cfcnfg *cnfg = container_obj(layer);
        struct cflayer *servicel = NULL;
        struct cfcnfg_phyinfo *phyinfo;
+       struct net_device *netdev;
+
        if (adapt_layer == NULL) {
                pr_debug("CAIF: %s(): link setup response "
                                "but no client exist, send linkdown back\n",
                servicel = cfdgml_create(channel_id, &phyinfo->dev_info);
                break;
        case CFCTRL_SRV_RFM:
+               netdev = phyinfo->dev_info.dev;
                servicel = cfrfml_create(channel_id, &phyinfo->dev_info,
-                                               RFM_FRAGMENT_SIZE);
+                                               netdev->mtu);
                break;
        case CFCTRL_SRV_UTIL:
                servicel = cfutill_create(channel_id, &phyinfo->dev_info);
 
 void
 cfcnfg_add_phy_layer(struct cfcnfg *cnfg, enum cfcnfg_phy_type phy_type,
-                    void *dev, struct cflayer *phy_layer, u16 *phyid,
-                    enum cfcnfg_phy_preference pref,
+                    struct net_device *dev, struct cflayer *phy_layer,
+                    u16 *phyid, enum cfcnfg_phy_preference pref,
                     bool fcs, bool stx)
 {
        struct cflayer *frml;
        cnfg->phy_layers[*phyid].dev_info.dev = dev;
        cnfg->phy_layers[*phyid].phy_layer = phy_layer;
        cnfg->phy_layers[*phyid].phy_ref_count = 0;
+       cnfg->phy_layers[*phyid].ifindex = dev->ifindex;
+       cnfg->phy_layers[*phyid].use_stx = stx;
+       cnfg->phy_layers[*phyid].use_fcs = fcs;
+
        phy_layer->type = phy_type;
        frml = cffrml_create(*phyid, fcs);
        if (!frml) {
 
 #ifdef CAIF_NO_LOOP
 static int handle_loop(struct cfctrl *ctrl,
                              int cmd, struct cfpkt *pkt){
-       return CAIF_FAILURE;
+       return -1;
 }
 #else
 static int handle_loop(struct cfctrl *ctrl,
        cmd = cmdrsp & CFCTRL_CMD_MASK;
        if (cmd != CFCTRL_CMD_LINK_ERR
            && CFCTRL_RSP_BIT != (CFCTRL_RSP_BIT & cmdrsp)) {
-               if (handle_loop(cfctrl, cmd, pkt) == CAIF_FAILURE)
+               if (handle_loop(cfctrl, cmd, pkt) != 0)
                        cmdrsp |= CFCTRL_ERR_BIT;
        }
 
        default:
                break;
        }
-       return CAIF_SUCCESS;
+       return 0;
 }
 #endif
 
 #define DGM_FLOW_OFF 0x81
 #define DGM_FLOW_ON  0x80
 #define DGM_CTRL_PKT_SIZE 1
+#define DGM_MTU 1500
 
 static int cfdgml_receive(struct cflayer *layr, struct cfpkt *pkt);
 static int cfdgml_transmit(struct cflayer *layr, struct cfpkt *pkt);
        if (!cfsrvl_ready(service, &ret))
                return ret;
 
+       /* STE Modem cannot handle more than 1500 bytes datagrams */
+       if (cfpkt_getlen(pkt) > DGM_MTU)
+               return -EMSGSIZE;
+
        cfpkt_add_head(pkt, &zero, 4);
 
        /* Add info for MUX-layer to route the packet out. */
 
 #include <linux/hardirq.h>
 #include <net/caif/cfpkt.h>
 
-#define PKT_PREFIX CAIF_NEEDED_HEADROOM
-#define PKT_POSTFIX CAIF_NEEDED_TAILROOM
+#define PKT_PREFIX  16
+#define PKT_POSTFIX 2
 #define PKT_LEN_WHEN_EXTENDING 128
 #define PKT_ERROR(pkt, errmsg) do {       \
     cfpkt_priv(pkt)->erronous = true;     \
 
 #define container_obj(layr) ((struct cfserl *) layr)
 
 #define CFSERL_STX 0x02
-#define CAIF_MINIUM_PACKET_SIZE 4
+#define SERIAL_MINIUM_PACKET_SIZE 4
+#define SERIAL_MAX_FRAMESIZE 4096
 struct cfserl {
        struct cflayer layer;
        struct cfpkt *incomplete_frm;
                /*
                 * Frame error handling
                 */
-               if (expectlen < CAIF_MINIUM_PACKET_SIZE
-                   || expectlen > CAIF_MAX_FRAMESIZE) {
+               if (expectlen < SERIAL_MINIUM_PACKET_SIZE
+                   || expectlen > SERIAL_MAX_FRAMESIZE) {
                        if (!layr->usestx) {
                                if (pkt != NULL)
                                        cfpkt_destroy(pkt);
 
 void cfsrvl_release(struct kref *kref)
 {
        struct cfsrvl *service = container_of(kref, struct cfsrvl, ref);
-       pr_info("CAIF: %s(): enter\n", __func__);
        kfree(service);
 }
 
 
        if (!cfsrvl_ready(service, &ret))
                return ret;
 
-       if (cfpkt_getlen(pkt) > CAIF_MAX_PAYLOAD_SIZE) {
-               pr_err("CAIF: %s(): packet too large size=%d\n",
-                       __func__, cfpkt_getlen(pkt));
-               return -EOVERFLOW;
-       }
-
        cfpkt_add_head(pkt, &zero, 1);
        /* Add info for MUX-layer to route the packet out. */
        info = cfpkt_info(pkt);
 
                return ret;
        caif_assert(layr->dn != NULL);
        caif_assert(layr->dn->transmit != NULL);
-       if (cfpkt_getlen(pkt) > CAIF_MAX_PAYLOAD_SIZE) {
-               pr_warning("CAIF: %s(): Packet too large - size=%d\n",
-                          __func__, cfpkt_getlen(pkt));
-               return -EOVERFLOW;
-       }
 
        if (cfpkt_add_head(pkt, &tmp, 1) < 0) {
                pr_err("CAIF: %s(): Packet is erroneous!\n", __func__);
 
 #include <net/caif/caif_dev.h>
 
 /* GPRS PDP connection has MTU to 1500 */
-#define SIZE_MTU 1500
+#define GPRS_PDP_MTU 1500
 /* 5 sec. connect timeout */
 #define CONNECT_TIMEOUT (5 * HZ)
 #define CAIF_NET_DEFAULT_QUEUE_LEN 500
 {
        struct chnl_net *priv = NULL;
        int result = -1;
+       int llifindex, headroom, tailroom, mtu;
+       struct net_device *lldev;
        ASSERT_RTNL();
        priv = netdev_priv(dev);
        if (!priv) {
 
        if (priv->state != CAIF_CONNECTING) {
                priv->state = CAIF_CONNECTING;
-               result = caif_connect_client(&priv->conn_req, &priv->chnl);
+               result = caif_connect_client(&priv->conn_req, &priv->chnl,
+                                       &llifindex, &headroom, &tailroom);
                if (result != 0) {
-                               priv->state = CAIF_DISCONNECTED;
                                pr_debug("CAIF: %s(): err: "
                                        "Unable to register and open device,"
                                        " Err:%d\n",
                                        __func__,
                                        result);
-                               return result;
+                               goto error;
+               }
+
+               lldev = dev_get_by_index(dev_net(dev), llifindex);
+
+               if (lldev == NULL) {
+                       pr_debug("CAIF: %s(): no interface?\n", __func__);
+                       result = -ENODEV;
+                       goto error;
+               }
+
+               dev->needed_tailroom = tailroom + lldev->needed_tailroom;
+               dev->hard_header_len = headroom + lldev->hard_header_len +
+                       lldev->needed_tailroom;
+
+               /*
+                * MTU, head-room etc is not know before we have a
+                * CAIF link layer device available. MTU calculation may
+                * override initial RTNL configuration.
+                * MTU is minimum of current mtu, link layer mtu pluss
+                * CAIF head and tail, and PDP GPRS contexts max MTU.
+                */
+               mtu = min_t(int, dev->mtu, lldev->mtu - (headroom + tailroom));
+               mtu = min_t(int, GPRS_PDP_MTU, mtu);
+               dev_set_mtu(dev, mtu);
+               dev_put(lldev);
+
+               if (mtu < 100) {
+                       pr_warning("CAIF: %s(): "
+                               "CAIF Interface MTU too small (%d)\n",
+                               __func__, mtu);
+                       result = -ENODEV;
+                       goto error;
                }
        }
 
+       rtnl_unlock();  /* Release RTNL lock during connect wait */
+
        result = wait_event_interruptible_timeout(priv->netmgmt_wq,
                                                priv->state != CAIF_CONNECTING,
                                                CONNECT_TIMEOUT);
 
+       rtnl_lock();
+
        if (result == -ERESTARTSYS) {
                pr_debug("CAIF: %s(): wait_event_interruptible"
                         " woken by a signal\n", __func__);
-               return -ERESTARTSYS;
+               result = -ERESTARTSYS;
+               goto error;
        }
+
        if (result == 0) {
                pr_debug("CAIF: %s(): connect timeout\n", __func__);
                caif_disconnect_client(&priv->chnl);
                priv->state = CAIF_DISCONNECTED;
                pr_debug("CAIF: %s(): state disconnected\n", __func__);
-               return -ETIMEDOUT;
+               result = -ETIMEDOUT;
+               goto error;
        }
 
        if (priv->state != CAIF_CONNECTED) {
                pr_debug("CAIF: %s(): connect failed\n", __func__);
-               return -ECONNREFUSED;
+               result = -ECONNREFUSED;
+               goto error;
        }
        pr_debug("CAIF: %s(): CAIF Netdevice connected\n", __func__);
        return 0;
+
+error:
+       caif_disconnect_client(&priv->chnl);
+       priv->state = CAIF_DISCONNECTED;
+       pr_debug("CAIF: %s(): state disconnected\n", __func__);
+       return result;
+
 }
 
 static int chnl_net_stop(struct net_device *dev)
        dev->destructor = free_netdev;
        dev->flags |= IFF_NOARP;
        dev->flags |= IFF_POINTOPOINT;
-       dev->needed_headroom = CAIF_NEEDED_HEADROOM;
-       dev->needed_tailroom = CAIF_NEEDED_TAILROOM;
-       dev->mtu = SIZE_MTU;
+       dev->mtu = GPRS_PDP_MTU;
        dev->tx_queue_len = CAIF_NET_DEFAULT_QUEUE_LEN;
 
        priv = netdev_priv(dev);