linux/drivers/w1/w1_netlink.c
<<
>>
Prefs
   1/*
   2 * w1_netlink.c
   3 *
   4 * Copyright (c) 2003 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
   5 *
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License as published by
   9 * the Free Software Foundation; either version 2 of the License, or
  10 * (at your option) any later version.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program; if not, write to the Free Software
  19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  20 */
  21
  22#include <linux/skbuff.h>
  23#include <linux/netlink.h>
  24#include <linux/connector.h>
  25
  26#include "w1.h"
  27#include "w1_log.h"
  28#include "w1_netlink.h"
  29
  30#if defined(CONFIG_W1_CON) && (defined(CONFIG_CONNECTOR) || (defined(CONFIG_CONNECTOR_MODULE) && defined(CONFIG_W1_MODULE)))
  31void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg)
  32{
  33        char buf[sizeof(struct cn_msg) + sizeof(struct w1_netlink_msg)];
  34        struct cn_msg *m = (struct cn_msg *)buf;
  35        struct w1_netlink_msg *w = (struct w1_netlink_msg *)(m+1);
  36
  37        memset(buf, 0, sizeof(buf));
  38
  39        m->id.idx = CN_W1_IDX;
  40        m->id.val = CN_W1_VAL;
  41
  42        m->seq = dev->seq++;
  43        m->len = sizeof(struct w1_netlink_msg);
  44
  45        memcpy(w, msg, sizeof(struct w1_netlink_msg));
  46
  47        cn_netlink_send(m, 0, GFP_KERNEL);
  48}
  49
  50static void w1_send_slave(struct w1_master *dev, u64 rn)
  51{
  52        struct cn_msg *msg = dev->priv;
  53        struct w1_netlink_msg *hdr = (struct w1_netlink_msg *)(msg + 1);
  54        struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)(hdr + 1);
  55        int avail;
  56
  57        avail = dev->priv_size - cmd->len;
  58
  59        if (avail > 8) {
  60                u64 *data = (void *)(cmd + 1) + cmd->len;
  61
  62                *data = rn;
  63                cmd->len += 8;
  64                hdr->len += 8;
  65                msg->len += 8;
  66                return;
  67        }
  68
  69        msg->ack++;
  70        cn_netlink_send(msg, 0, GFP_KERNEL);
  71
  72        msg->len = sizeof(struct w1_netlink_msg) + sizeof(struct w1_netlink_cmd);
  73        hdr->len = sizeof(struct w1_netlink_cmd);
  74        cmd->len = 0;
  75}
  76
  77static int w1_process_search_command(struct w1_master *dev, struct cn_msg *msg,
  78                unsigned int avail)
  79{
  80        struct w1_netlink_msg *hdr = (struct w1_netlink_msg *)(msg + 1);
  81        struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)(hdr + 1);
  82        int search_type = (cmd->cmd == W1_CMD_ALARM_SEARCH)?W1_ALARM_SEARCH:W1_SEARCH;
  83
  84        dev->priv = msg;
  85        dev->priv_size = avail;
  86
  87        w1_search_devices(dev, search_type, w1_send_slave);
  88
  89        msg->ack = 0;
  90        cn_netlink_send(msg, 0, GFP_KERNEL);
  91
  92        dev->priv = NULL;
  93        dev->priv_size = 0;
  94
  95        return 0;
  96}
  97
  98static int w1_send_read_reply(struct cn_msg *msg, struct w1_netlink_msg *hdr,
  99                struct w1_netlink_cmd *cmd)
 100{
 101        void *data;
 102        struct w1_netlink_msg *h;
 103        struct w1_netlink_cmd *c;
 104        struct cn_msg *cm;
 105        int err;
 106
 107        data = kzalloc(sizeof(struct cn_msg) +
 108                        sizeof(struct w1_netlink_msg) +
 109                        sizeof(struct w1_netlink_cmd) +
 110                        cmd->len, GFP_KERNEL);
 111        if (!data)
 112                return -ENOMEM;
 113
 114        cm = (struct cn_msg *)(data);
 115        h = (struct w1_netlink_msg *)(cm + 1);
 116        c = (struct w1_netlink_cmd *)(h + 1);
 117
 118        memcpy(cm, msg, sizeof(struct cn_msg));
 119        memcpy(h, hdr, sizeof(struct w1_netlink_msg));
 120        memcpy(c, cmd, sizeof(struct w1_netlink_cmd));
 121
 122        cm->ack = msg->seq+1;
 123        cm->len = sizeof(struct w1_netlink_msg) +
 124                sizeof(struct w1_netlink_cmd) + cmd->len;
 125
 126        h->len = sizeof(struct w1_netlink_cmd) + cmd->len;
 127
 128        memcpy(c->data, cmd->data, c->len);
 129
 130        err = cn_netlink_send(cm, 0, GFP_KERNEL);
 131
 132        kfree(data);
 133
 134        return err;
 135}
 136
 137static int w1_process_command_io(struct w1_master *dev, struct cn_msg *msg,
 138                struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd)
 139{
 140        int err = 0;
 141
 142        switch (cmd->cmd) {
 143        case W1_CMD_TOUCH:
 144                w1_touch_block(dev, cmd->data, cmd->len);
 145                w1_send_read_reply(msg, hdr, cmd);
 146                break;
 147        case W1_CMD_READ:
 148                w1_read_block(dev, cmd->data, cmd->len);
 149                w1_send_read_reply(msg, hdr, cmd);
 150                break;
 151        case W1_CMD_WRITE:
 152                w1_write_block(dev, cmd->data, cmd->len);
 153                break;
 154        default:
 155                err = -EINVAL;
 156                break;
 157        }
 158
 159        return err;
 160}
 161
 162static int w1_process_command_master(struct w1_master *dev, struct cn_msg *req_msg,
 163                struct w1_netlink_msg *req_hdr, struct w1_netlink_cmd *req_cmd)
 164{
 165        int err = -EINVAL;
 166        struct cn_msg *msg;
 167        struct w1_netlink_msg *hdr;
 168        struct w1_netlink_cmd *cmd;
 169
 170        msg = kzalloc(PAGE_SIZE, GFP_KERNEL);
 171        if (!msg)
 172                return -ENOMEM;
 173
 174        msg->id = req_msg->id;
 175        msg->seq = req_msg->seq;
 176        msg->ack = 0;
 177        msg->len = sizeof(struct w1_netlink_msg) + sizeof(struct w1_netlink_cmd);
 178
 179        hdr = (struct w1_netlink_msg *)(msg + 1);
 180        cmd = (struct w1_netlink_cmd *)(hdr + 1);
 181
 182        hdr->type = W1_MASTER_CMD;
 183        hdr->id = req_hdr->id;
 184        hdr->len = sizeof(struct w1_netlink_cmd);
 185
 186        cmd->cmd = req_cmd->cmd;
 187        cmd->len = 0;
 188
 189        switch (cmd->cmd) {
 190        case W1_CMD_SEARCH:
 191        case W1_CMD_ALARM_SEARCH:
 192                err = w1_process_search_command(dev, msg,
 193                                PAGE_SIZE - msg->len - sizeof(struct cn_msg));
 194                break;
 195        case W1_CMD_READ:
 196        case W1_CMD_WRITE:
 197        case W1_CMD_TOUCH:
 198                err = w1_process_command_io(dev, req_msg, req_hdr, req_cmd);
 199                break;
 200        case W1_CMD_RESET:
 201                err = w1_reset_bus(dev);
 202                break;
 203        default:
 204                err = -EINVAL;
 205                break;
 206        }
 207
 208        kfree(msg);
 209        return err;
 210}
 211
 212static int w1_process_command_slave(struct w1_slave *sl, struct cn_msg *msg,
 213                struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd)
 214{
 215        dev_dbg(&sl->master->dev, "%s: %02x.%012llx.%02x: cmd=%02x, len=%u.\n",
 216                __func__, sl->reg_num.family, (unsigned long long)sl->reg_num.id,
 217                sl->reg_num.crc, cmd->cmd, cmd->len);
 218
 219        return w1_process_command_io(sl->master, msg, hdr, cmd);
 220}
 221
 222static int w1_process_command_root(struct cn_msg *msg, struct w1_netlink_msg *mcmd)
 223{
 224        struct w1_master *m;
 225        struct cn_msg *cn;
 226        struct w1_netlink_msg *w;
 227        u32 *id;
 228
 229        if (mcmd->type != W1_LIST_MASTERS) {
 230                printk(KERN_NOTICE "%s: msg: %x.%x, wrong type: %u, len: %u.\n",
 231                        __func__, msg->id.idx, msg->id.val, mcmd->type, mcmd->len);
 232                return -EPROTO;
 233        }
 234
 235        cn = kmalloc(PAGE_SIZE, GFP_KERNEL);
 236        if (!cn)
 237                return -ENOMEM;
 238
 239        cn->id.idx = CN_W1_IDX;
 240        cn->id.val = CN_W1_VAL;
 241
 242        cn->seq = msg->seq;
 243        cn->ack = 1;
 244        cn->len = sizeof(struct w1_netlink_msg);
 245        w = (struct w1_netlink_msg *)(cn + 1);
 246
 247        w->type = W1_LIST_MASTERS;
 248        w->status = 0;
 249        w->len = 0;
 250        id = (u32 *)(w + 1);
 251
 252        mutex_lock(&w1_mlock);
 253        list_for_each_entry(m, &w1_masters, w1_master_entry) {
 254                if (cn->len + sizeof(*id) > PAGE_SIZE - sizeof(struct cn_msg)) {
 255                        cn_netlink_send(cn, 0, GFP_KERNEL);
 256                        cn->ack++;
 257                        cn->len = sizeof(struct w1_netlink_msg);
 258                        w->len = 0;
 259                        id = (u32 *)(w + 1);
 260                }
 261
 262                *id = m->id;
 263                w->len += sizeof(*id);
 264                cn->len += sizeof(*id);
 265                id++;
 266        }
 267        cn->ack = 0;
 268        cn_netlink_send(cn, 0, GFP_KERNEL);
 269        mutex_unlock(&w1_mlock);
 270
 271        kfree(cn);
 272        return 0;
 273}
 274
 275static int w1_netlink_send_error(struct cn_msg *rcmsg, struct w1_netlink_msg *rmsg,
 276                struct w1_netlink_cmd *rcmd, int error)
 277{
 278        struct cn_msg *cmsg;
 279        struct w1_netlink_msg *msg;
 280        struct w1_netlink_cmd *cmd;
 281
 282        cmsg = kzalloc(sizeof(*msg) + sizeof(*cmd) + sizeof(*cmsg), GFP_KERNEL);
 283        if (!cmsg)
 284                return -ENOMEM;
 285
 286        msg = (struct w1_netlink_msg *)(cmsg + 1);
 287        cmd = (struct w1_netlink_cmd *)(msg + 1);
 288
 289        memcpy(cmsg, rcmsg, sizeof(*cmsg));
 290        cmsg->len = sizeof(*msg);
 291
 292        memcpy(msg, rmsg, sizeof(*msg));
 293        msg->len = 0;
 294        msg->status = (short)-error;
 295
 296        if (rcmd) {
 297                memcpy(cmd, rcmd, sizeof(*cmd));
 298                cmd->len = 0;
 299                msg->len += sizeof(*cmd);
 300                cmsg->len += sizeof(*cmd);
 301        }
 302
 303        error = cn_netlink_send(cmsg, 0, GFP_KERNEL);
 304        kfree(cmsg);
 305
 306        return error;
 307}
 308
 309static void w1_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
 310{
 311        struct w1_netlink_msg *m = (struct w1_netlink_msg *)(msg + 1);
 312        struct w1_netlink_cmd *cmd;
 313        struct w1_slave *sl;
 314        struct w1_master *dev;
 315        int err = 0;
 316
 317        while (msg->len && !err) {
 318                struct w1_reg_num id;
 319                u16 mlen = m->len;
 320                u8 *cmd_data = m->data;
 321
 322                dev = NULL;
 323                sl = NULL;
 324                cmd = NULL;
 325
 326                memcpy(&id, m->id.id, sizeof(id));
 327#if 0
 328                printk("%s: %02x.%012llx.%02x: type=%02x, len=%u.\n",
 329                                __func__, id.family, (unsigned long long)id.id, id.crc, m->type, m->len);
 330#endif
 331                if (m->len + sizeof(struct w1_netlink_msg) > msg->len) {
 332                        err = -E2BIG;
 333                        break;
 334                }
 335
 336                if (m->type == W1_MASTER_CMD) {
 337                        dev = w1_search_master_id(m->id.mst.id);
 338                } else if (m->type == W1_SLAVE_CMD) {
 339                        sl = w1_search_slave(&id);
 340                        if (sl)
 341                                dev = sl->master;
 342                } else {
 343                        err = w1_process_command_root(msg, m);
 344                        goto out_cont;
 345                }
 346
 347                if (!dev) {
 348                        err = -ENODEV;
 349                        goto out_cont;
 350                }
 351
 352                err = 0;
 353                if (!mlen)
 354                        goto out_cont;
 355
 356                mutex_lock(&dev->mutex);
 357
 358                if (sl && w1_reset_select_slave(sl)) {
 359                        err = -ENODEV;
 360                        goto out_up;
 361                }
 362
 363                while (mlen) {
 364                        cmd = (struct w1_netlink_cmd *)cmd_data;
 365
 366                        if (cmd->len + sizeof(struct w1_netlink_cmd) > mlen) {
 367                                err = -E2BIG;
 368                                break;
 369                        }
 370
 371                        if (sl)
 372                                err = w1_process_command_slave(sl, msg, m, cmd);
 373                        else
 374                                err = w1_process_command_master(dev, msg, m, cmd);
 375
 376                        w1_netlink_send_error(msg, m, cmd, err);
 377                        err = 0;
 378
 379                        cmd_data += cmd->len + sizeof(struct w1_netlink_cmd);
 380                        mlen -= cmd->len + sizeof(struct w1_netlink_cmd);
 381                }
 382out_up:
 383                atomic_dec(&dev->refcnt);
 384                if (sl)
 385                        atomic_dec(&sl->refcnt);
 386                mutex_unlock(&dev->mutex);
 387out_cont:
 388                if (!cmd || err)
 389                        w1_netlink_send_error(msg, m, cmd, err);
 390                msg->len -= sizeof(struct w1_netlink_msg) + m->len;
 391                m = (struct w1_netlink_msg *)(((u8 *)m) + sizeof(struct w1_netlink_msg) + m->len);
 392
 393                /*
 394                 * Let's allow requests for nonexisting devices.
 395                 */
 396                if (err == -ENODEV)
 397                        err = 0;
 398        }
 399}
 400
 401int w1_init_netlink(void)
 402{
 403        struct cb_id w1_id = {.idx = CN_W1_IDX, .val = CN_W1_VAL};
 404
 405        return cn_add_callback(&w1_id, "w1", &w1_cn_callback);
 406}
 407
 408void w1_fini_netlink(void)
 409{
 410        struct cb_id w1_id = {.idx = CN_W1_IDX, .val = CN_W1_VAL};
 411
 412        cn_del_callback(&w1_id);
 413}
 414#else
 415void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg)
 416{
 417}
 418
 419int w1_init_netlink(void)
 420{
 421        return 0;
 422}
 423
 424void w1_fini_netlink(void)
 425{
 426}
 427#endif
 428