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