}
 EXPORT_SYMBOL_GPL(iscsi_conn_bind);
 
+
+int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
+                   enum iscsi_param param, char *buf, int buflen)
+{
+       struct iscsi_conn *conn = cls_conn->dd_data;
+       struct iscsi_session *session = conn->session;
+       uint32_t value;
+
+       switch(param) {
+       case ISCSI_PARAM_MAX_RECV_DLENGTH:
+               sscanf(buf, "%d", &conn->max_recv_dlength);
+               break;
+       case ISCSI_PARAM_MAX_XMIT_DLENGTH:
+               sscanf(buf, "%d", &conn->max_xmit_dlength);
+               break;
+       case ISCSI_PARAM_HDRDGST_EN:
+               sscanf(buf, "%d", &conn->hdrdgst_en);
+               break;
+       case ISCSI_PARAM_DATADGST_EN:
+               sscanf(buf, "%d", &conn->datadgst_en);
+               break;
+       case ISCSI_PARAM_INITIAL_R2T_EN:
+               sscanf(buf, "%d", &session->initial_r2t_en);
+               break;
+       case ISCSI_PARAM_MAX_R2T:
+               sscanf(buf, "%d", &session->max_r2t);
+               break;
+       case ISCSI_PARAM_IMM_DATA_EN:
+               sscanf(buf, "%d", &session->imm_data_en);
+               break;
+       case ISCSI_PARAM_FIRST_BURST:
+               sscanf(buf, "%d", &session->first_burst);
+               break;
+       case ISCSI_PARAM_MAX_BURST:
+               sscanf(buf, "%d", &session->max_burst);
+               break;
+       case ISCSI_PARAM_PDU_INORDER_EN:
+               sscanf(buf, "%d", &session->pdu_inorder_en);
+               break;
+       case ISCSI_PARAM_DATASEQ_INORDER_EN:
+               sscanf(buf, "%d", &session->dataseq_inorder_en);
+               break;
+       case ISCSI_PARAM_ERL:
+               sscanf(buf, "%d", &session->erl);
+               break;
+       case ISCSI_PARAM_IFMARKER_EN:
+               sscanf(buf, "%d", &value);
+               BUG_ON(value);
+               break;
+       case ISCSI_PARAM_OFMARKER_EN:
+               sscanf(buf, "%d", &value);
+               BUG_ON(value);
+               break;
+       case ISCSI_PARAM_EXP_STATSN:
+               sscanf(buf, "%u", &conn->exp_statsn);
+               break;
+       case ISCSI_PARAM_TARGET_NAME:
+               /* this should not change between logins */
+               if (session->targetname)
+                       break;
+
+               session->targetname = kstrdup(buf, GFP_KERNEL);
+               if (!session->targetname)
+                       return -ENOMEM;
+               break;
+       case ISCSI_PARAM_TPGT:
+               sscanf(buf, "%d", &session->tpgt);
+               break;
+       case ISCSI_PARAM_PERSISTENT_PORT:
+               sscanf(buf, "%d", &conn->persistent_port);
+               break;
+       case ISCSI_PARAM_PERSISTENT_ADDRESS:
+               /*
+                * this is the address returned in discovery so it should
+                * not change between logins.
+                */
+               if (conn->persistent_address)
+                       break;
+
+               conn->persistent_address = kstrdup(buf, GFP_KERNEL);
+               if (!conn->persistent_address)
+                       return -ENOMEM;
+               break;
+       default:
+               return -ENOSYS;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(iscsi_set_param);
+
+int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
+                           enum iscsi_param param, char *buf)
+{
+       struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+       struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+       int len;
+
+       switch(param) {
+       case ISCSI_PARAM_INITIAL_R2T_EN:
+               len = sprintf(buf, "%d\n", session->initial_r2t_en);
+               break;
+       case ISCSI_PARAM_MAX_R2T:
+               len = sprintf(buf, "%hu\n", session->max_r2t);
+               break;
+       case ISCSI_PARAM_IMM_DATA_EN:
+               len = sprintf(buf, "%d\n", session->imm_data_en);
+               break;
+       case ISCSI_PARAM_FIRST_BURST:
+               len = sprintf(buf, "%u\n", session->first_burst);
+               break;
+       case ISCSI_PARAM_MAX_BURST:
+               len = sprintf(buf, "%u\n", session->max_burst);
+               break;
+       case ISCSI_PARAM_PDU_INORDER_EN:
+               len = sprintf(buf, "%d\n", session->pdu_inorder_en);
+               break;
+       case ISCSI_PARAM_DATASEQ_INORDER_EN:
+               len = sprintf(buf, "%d\n", session->dataseq_inorder_en);
+               break;
+       case ISCSI_PARAM_ERL:
+               len = sprintf(buf, "%d\n", session->erl);
+               break;
+       case ISCSI_PARAM_TARGET_NAME:
+               len = sprintf(buf, "%s\n", session->targetname);
+               break;
+       case ISCSI_PARAM_TPGT:
+               len = sprintf(buf, "%d\n", session->tpgt);
+               break;
+       default:
+               return -ENOSYS;
+       }
+
+       return len;
+}
+EXPORT_SYMBOL_GPL(iscsi_session_get_param);
+
+int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
+                        enum iscsi_param param, char *buf)
+{
+       struct iscsi_conn *conn = cls_conn->dd_data;
+       int len;
+
+       switch(param) {
+       case ISCSI_PARAM_MAX_RECV_DLENGTH:
+               len = sprintf(buf, "%u\n", conn->max_recv_dlength);
+               break;
+       case ISCSI_PARAM_MAX_XMIT_DLENGTH:
+               len = sprintf(buf, "%u\n", conn->max_xmit_dlength);
+               break;
+       case ISCSI_PARAM_HDRDGST_EN:
+               len = sprintf(buf, "%d\n", conn->hdrdgst_en);
+               break;
+       case ISCSI_PARAM_DATADGST_EN:
+               len = sprintf(buf, "%d\n", conn->datadgst_en);
+               break;
+       case ISCSI_PARAM_IFMARKER_EN:
+               len = sprintf(buf, "%d\n", conn->ifmarker_en);
+               break;
+       case ISCSI_PARAM_OFMARKER_EN:
+               len = sprintf(buf, "%d\n", conn->ofmarker_en);
+               break;
+       case ISCSI_PARAM_EXP_STATSN:
+               len = sprintf(buf, "%u\n", conn->exp_statsn);
+               break;
+       case ISCSI_PARAM_PERSISTENT_PORT:
+               len = sprintf(buf, "%d\n", conn->persistent_port);
+               break;
+       case ISCSI_PARAM_PERSISTENT_ADDRESS:
+               len = sprintf(buf, "%s\n", conn->persistent_address);
+               break;
+       default:
+               return -ENOSYS;
+       }
+
+       return len;
+}
+EXPORT_SYMBOL_GPL(iscsi_conn_get_param);
+
 MODULE_AUTHOR("Mike Christie");
 MODULE_DESCRIPTION("iSCSI library functions");
 MODULE_LICENSE("GPL");
 
 
        shost = iscsi_session_to_shost(session);
        scsi_host_put(shost);
-       kfree(session->targetname);
        kfree(session);
        module_put(transport->owner);
 }
        struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev);
        struct device *parent = conn->dev.parent;
 
-       kfree(conn->persistent_address);
        kfree(conn);
        put_device(parent);
 }
        return 0;
 }
 
-static void
-iscsi_copy_param(struct iscsi_uevent *ev, uint32_t *value, char *data)
-{
-       if (ev->u.set_param.len != sizeof(uint32_t))
-               BUG();
-       memcpy(value, data, min_t(uint32_t, sizeof(uint32_t),
-               ev->u.set_param.len));
-}
-
 static int
 iscsi_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev)
 {
        char *data = (char*)ev + sizeof(*ev);
        struct iscsi_cls_conn *conn;
        struct iscsi_cls_session *session;
-       int err = 0;
-       uint32_t value = 0;
+       int err = 0, value = 0;
 
        session = iscsi_session_lookup(ev->u.set_param.sid);
        conn = iscsi_conn_lookup(ev->u.set_param.sid, ev->u.set_param.cid);
 
        switch (ev->u.set_param.param) {
        case ISCSI_PARAM_SESS_RECOVERY_TMO:
-               iscsi_copy_param(ev, &value, data);
+               sscanf(data, "%d", &value);
                if (value != 0)
                        session->recovery_tmo = value;
                break;
-       case ISCSI_PARAM_TARGET_NAME:
-               /* this should not change between logins */
-               if (session->targetname)
-                       return 0;
-
-               session->targetname = kstrdup(data, GFP_KERNEL);
-               if (!session->targetname)
-                       return -ENOMEM;
-               break;
-       case ISCSI_PARAM_TPGT:
-               iscsi_copy_param(ev, &value, data);
-               session->tpgt = value;
-               break;
-       case ISCSI_PARAM_PERSISTENT_PORT:
-               iscsi_copy_param(ev, &value, data);
-               conn->persistent_port = value;
-               break;
-       case ISCSI_PARAM_PERSISTENT_ADDRESS:
-               /*
-                * this is the address returned in discovery so it should
-                * not change between logins.
-                */
-               if (conn->persistent_address)
-                       return 0;
-
-               conn->persistent_address = kstrdup(data, GFP_KERNEL);
-               if (!conn->persistent_address)
-                       return -ENOMEM;
-               break;
        default:
-               iscsi_copy_param(ev, &value, data);
-               err = transport->set_param(conn, ev->u.set_param.param, value);
+               err = transport->set_param(conn, ev->u.set_param.param,
+                                          data, ev->u.set_param.len);
        }
 
        return err;
 /*
  * iSCSI connection attrs
  */
-#define iscsi_conn_int_attr_show(param, format)                                \
+#define iscsi_conn_attr_show(param)                                    \
 static ssize_t                                                         \
-show_conn_int_param_##param(struct class_device *cdev, char *buf)      \
+show_conn_param_##param(struct class_device *cdev, char *buf)          \
 {                                                                      \
-       uint32_t value = 0;                                             \
        struct iscsi_cls_conn *conn = iscsi_cdev_to_conn(cdev);         \
        struct iscsi_transport *t = conn->transport;                    \
-                                                                       \
-       t->get_conn_param(conn, param, &value);                         \
-       return snprintf(buf, 20, format"\n", value);                    \
+       return t->get_conn_param(conn, param, buf);                     \
 }
 
-#define iscsi_conn_int_attr(field, param, format)                      \
-       iscsi_conn_int_attr_show(param, format)                         \
-static ISCSI_CLASS_ATTR(conn, field, S_IRUGO, show_conn_int_param_##param, \
+#define iscsi_conn_attr(field, param)                                  \
+       iscsi_conn_attr_show(param)                                     \
+static ISCSI_CLASS_ATTR(conn, field, S_IRUGO, show_conn_param_##param, \
                        NULL);
 
-iscsi_conn_int_attr(max_recv_dlength, ISCSI_PARAM_MAX_RECV_DLENGTH, "%u");
-iscsi_conn_int_attr(max_xmit_dlength, ISCSI_PARAM_MAX_XMIT_DLENGTH, "%u");
-iscsi_conn_int_attr(header_digest, ISCSI_PARAM_HDRDGST_EN, "%d");
-iscsi_conn_int_attr(data_digest, ISCSI_PARAM_DATADGST_EN, "%d");
-iscsi_conn_int_attr(ifmarker, ISCSI_PARAM_IFMARKER_EN, "%d");
-iscsi_conn_int_attr(ofmarker, ISCSI_PARAM_OFMARKER_EN, "%d");
-iscsi_conn_int_attr(persistent_port, ISCSI_PARAM_PERSISTENT_PORT, "%d");
-iscsi_conn_int_attr(port, ISCSI_PARAM_CONN_PORT, "%d");
-iscsi_conn_int_attr(exp_statsn, ISCSI_PARAM_EXP_STATSN, "%u");
-
-#define iscsi_conn_str_attr_show(param)                                        \
-static ssize_t                                                         \
-show_conn_str_param_##param(struct class_device *cdev, char *buf)      \
-{                                                                      \
-       struct iscsi_cls_conn *conn = iscsi_cdev_to_conn(cdev);         \
-       struct iscsi_transport *t = conn->transport;                    \
-       return t->get_conn_str_param(conn, param, buf);                 \
-}
-
-#define iscsi_conn_str_attr(field, param)                              \
-       iscsi_conn_str_attr_show(param)                                 \
-static ISCSI_CLASS_ATTR(conn, field, S_IRUGO, show_conn_str_param_##param, \
-                       NULL);
-
-iscsi_conn_str_attr(persistent_address, ISCSI_PARAM_PERSISTENT_ADDRESS);
-iscsi_conn_str_attr(address, ISCSI_PARAM_CONN_ADDRESS);
+iscsi_conn_attr(max_recv_dlength, ISCSI_PARAM_MAX_RECV_DLENGTH);
+iscsi_conn_attr(max_xmit_dlength, ISCSI_PARAM_MAX_XMIT_DLENGTH);
+iscsi_conn_attr(header_digest, ISCSI_PARAM_HDRDGST_EN);
+iscsi_conn_attr(data_digest, ISCSI_PARAM_DATADGST_EN);
+iscsi_conn_attr(ifmarker, ISCSI_PARAM_IFMARKER_EN);
+iscsi_conn_attr(ofmarker, ISCSI_PARAM_OFMARKER_EN);
+iscsi_conn_attr(persistent_port, ISCSI_PARAM_PERSISTENT_PORT);
+iscsi_conn_attr(port, ISCSI_PARAM_CONN_PORT);
+iscsi_conn_attr(exp_statsn, ISCSI_PARAM_EXP_STATSN);
+iscsi_conn_attr(persistent_address, ISCSI_PARAM_PERSISTENT_ADDRESS);
+iscsi_conn_attr(address, ISCSI_PARAM_CONN_ADDRESS);
 
 #define iscsi_cdev_to_session(_cdev) \
        iscsi_dev_to_session(_cdev->dev)
 /*
  * iSCSI session attrs
  */
-#define iscsi_session_int_attr_show(param, format)                     \
-static ssize_t                                                         \
-show_session_int_param_##param(struct class_device *cdev, char *buf)   \
-{                                                                      \
-       uint32_t value = 0;                                             \
-       struct iscsi_cls_session *session = iscsi_cdev_to_session(cdev);        \
-       struct iscsi_transport *t = session->transport;                 \
-                                                                       \
-       t->get_session_param(session, param, &value);                   \
-       return snprintf(buf, 20, format"\n", value);                    \
-}
-
-#define iscsi_session_int_attr(field, param, format)                   \
-       iscsi_session_int_attr_show(param, format)                      \
-static ISCSI_CLASS_ATTR(sess, field, S_IRUGO, show_session_int_param_##param, \
-                       NULL);
-
-iscsi_session_int_attr(initial_r2t, ISCSI_PARAM_INITIAL_R2T_EN, "%d");
-iscsi_session_int_attr(max_outstanding_r2t, ISCSI_PARAM_MAX_R2T, "%hu");
-iscsi_session_int_attr(immediate_data, ISCSI_PARAM_IMM_DATA_EN, "%d");
-iscsi_session_int_attr(first_burst_len, ISCSI_PARAM_FIRST_BURST, "%u");
-iscsi_session_int_attr(max_burst_len, ISCSI_PARAM_MAX_BURST, "%u");
-iscsi_session_int_attr(data_pdu_in_order, ISCSI_PARAM_PDU_INORDER_EN, "%d");
-iscsi_session_int_attr(data_seq_in_order, ISCSI_PARAM_DATASEQ_INORDER_EN, "%d");
-iscsi_session_int_attr(erl, ISCSI_PARAM_ERL, "%d");
-iscsi_session_int_attr(tpgt, ISCSI_PARAM_TPGT, "%d");
-
-#define iscsi_session_str_attr_show(param)                             \
+#define iscsi_session_attr_show(param)                                 \
 static ssize_t                                                         \
-show_session_str_param_##param(struct class_device *cdev, char *buf)   \
+show_session_param_##param(struct class_device *cdev, char *buf)       \
 {                                                                      \
        struct iscsi_cls_session *session = iscsi_cdev_to_session(cdev); \
        struct iscsi_transport *t = session->transport;                 \
-       return t->get_session_str_param(session, param, buf);           \
+       return t->get_session_param(session, param, buf);               \
 }
 
-#define iscsi_session_str_attr(field, param)                           \
-       iscsi_session_str_attr_show(param)                              \
-static ISCSI_CLASS_ATTR(sess, field, S_IRUGO, show_session_str_param_##param, \
+#define iscsi_session_attr(field, param)                               \
+       iscsi_session_attr_show(param)                                  \
+static ISCSI_CLASS_ATTR(sess, field, S_IRUGO, show_session_param_##param, \
                        NULL);
 
-iscsi_session_str_attr(targetname, ISCSI_PARAM_TARGET_NAME);
+iscsi_session_attr(targetname, ISCSI_PARAM_TARGET_NAME);
+iscsi_session_attr(initial_r2t, ISCSI_PARAM_INITIAL_R2T_EN);
+iscsi_session_attr(max_outstanding_r2t, ISCSI_PARAM_MAX_R2T);
+iscsi_session_attr(immediate_data, ISCSI_PARAM_IMM_DATA_EN);
+iscsi_session_attr(first_burst_len, ISCSI_PARAM_FIRST_BURST);
+iscsi_session_attr(max_burst_len, ISCSI_PARAM_MAX_BURST);
+iscsi_session_attr(data_pdu_in_order, ISCSI_PARAM_PDU_INORDER_EN);
+iscsi_session_attr(data_seq_in_order, ISCSI_PARAM_DATASEQ_INORDER_EN);
+iscsi_session_attr(erl, ISCSI_PARAM_ERL);
+iscsi_session_attr(tpgt, ISCSI_PARAM_TPGT);
 
-/*
- * Private session and conn attrs. userspace uses several iscsi values
- * to identify each session between reboots. Some of these values may not
- * be present in the iscsi_transport/LLD driver becuase userspace handles
- * login (and failback for login redirect) so for these type of drivers
- * the class manages the attrs and values for the iscsi_transport/LLD
- */
 #define iscsi_priv_session_attr_show(field, format)                    \
 static ssize_t                                                         \
-show_priv_session_##field(struct class_device *cdev, char *buf)        \
+show_priv_session_##field(struct class_device *cdev, char *buf)                \
 {                                                                      \
-       struct iscsi_cls_session *session = iscsi_cdev_to_session(cdev); \
+       struct iscsi_cls_session *session = iscsi_cdev_to_session(cdev);\
        return sprintf(buf, format"\n", session->field);                \
 }
 
        iscsi_priv_session_attr_show(field, format)                     \
 static ISCSI_CLASS_ATTR(priv_sess, field, S_IRUGO, show_priv_session_##field, \
                        NULL)
-iscsi_priv_session_attr(targetname, "%s");
-iscsi_priv_session_attr(tpgt, "%d");
 iscsi_priv_session_attr(recovery_tmo, "%d");
 
-#define iscsi_priv_conn_attr_show(field, format)                       \
-static ssize_t                                                         \
-show_priv_conn_##field(struct class_device *cdev, char *buf)           \
-{                                                                      \
-       struct iscsi_cls_conn *conn = iscsi_cdev_to_conn(cdev);         \
-       return sprintf(buf, format"\n", conn->field);                   \
-}
-
-#define iscsi_priv_conn_attr(field, format)                            \
-       iscsi_priv_conn_attr_show(field, format)                        \
-static ISCSI_CLASS_ATTR(priv_conn, field, S_IRUGO, show_priv_conn_##field, \
-                       NULL)
-iscsi_priv_conn_attr(persistent_address, "%s");
-iscsi_priv_conn_attr(persistent_port, "%d");
-
 #define SETUP_PRIV_SESSION_RD_ATTR(field)                              \
 do {                                                                   \
        priv->session_attrs[count] = &class_device_attr_priv_sess_##field; \
        count++;                                                        \
 } while (0)
 
+
 #define SETUP_SESSION_RD_ATTR(field, param_flag)                       \
 do {                                                                   \
        if (tt->param_mask & param_flag) {                              \
        }                                                               \
 } while (0)
 
-#define SETUP_PRIV_CONN_RD_ATTR(field)                                 \
-do {                                                                   \
-       priv->conn_attrs[count] = &class_device_attr_priv_conn_##field; \
-       count++;                                                        \
-} while (0)
-
 #define SETUP_CONN_RD_ATTR(field, param_flag)                          \
 do {                                                                   \
        if (tt->param_mask & param_flag) {                              \
        SETUP_CONN_RD_ATTR(address, ISCSI_CONN_ADDRESS);
        SETUP_CONN_RD_ATTR(port, ISCSI_CONN_PORT);
        SETUP_CONN_RD_ATTR(exp_statsn, ISCSI_EXP_STATSN);
-
-       if (tt->param_mask & ISCSI_PERSISTENT_ADDRESS)
-               SETUP_CONN_RD_ATTR(persistent_address, ISCSI_PERSISTENT_ADDRESS);
-       else
-               SETUP_PRIV_CONN_RD_ATTR(persistent_address);
-
-       if (tt->param_mask & ISCSI_PERSISTENT_PORT)
-               SETUP_CONN_RD_ATTR(persistent_port, ISCSI_PERSISTENT_PORT);
-       else
-               SETUP_PRIV_CONN_RD_ATTR(persistent_port);
+       SETUP_CONN_RD_ATTR(persistent_address, ISCSI_PERSISTENT_ADDRESS);
+       SETUP_CONN_RD_ATTR(persistent_port, ISCSI_PERSISTENT_PORT);
 
        BUG_ON(count > ISCSI_CONN_ATTRS);
        priv->conn_attrs[count] = NULL;
        SETUP_SESSION_RD_ATTR(data_pdu_in_order, ISCSI_PDU_INORDER_EN);
        SETUP_SESSION_RD_ATTR(data_seq_in_order, ISCSI_DATASEQ_INORDER_EN);
        SETUP_SESSION_RD_ATTR(erl, ISCSI_ERL);
+       SETUP_SESSION_RD_ATTR(targetname, ISCSI_TARGET_NAME);
+       SETUP_SESSION_RD_ATTR(tpgt, ISCSI_TPGT);
        SETUP_PRIV_SESSION_RD_ATTR(recovery_tmo);
 
-       if (tt->param_mask & ISCSI_TARGET_NAME)
-               SETUP_SESSION_RD_ATTR(targetname, ISCSI_TARGET_NAME);
-       else
-               SETUP_PRIV_SESSION_RD_ATTR(targetname);
-
-       if (tt->param_mask & ISCSI_TPGT)
-               SETUP_SESSION_RD_ATTR(tpgt, ISCSI_TPGT);
-       else
-               SETUP_PRIV_SESSION_RD_ATTR(tpgt);
-
        BUG_ON(count > ISCSI_SESSION_ATTRS);
        priv->session_attrs[count] = NULL;