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))) 32 void 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 51 static 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 u64 *data; 58 59 avail = dev->priv_size - cmd->len; 60 61 if (avail < 8) { 62 msg->ack++; 63 cn_netlink_send(msg, 0, GFP_KERNEL); 64 65 msg->len = sizeof(struct w1_netlink_msg) + 66 sizeof(struct w1_netlink_cmd); 67 hdr->len = sizeof(struct w1_netlink_cmd); 68 cmd->len = 0; 69 } 70 71 data = (void *)(cmd + 1) + cmd->len; 72 73 *data = rn; 74 cmd->len += 8; 75 hdr->len += 8; 76 msg->len += 8; 77 } 78 79 static void w1_found_send_slave(struct w1_master *dev, u64 rn) 80 { 81 /* update kernel slave list */ 82 w1_slave_found(dev, rn); 83 84 w1_send_slave(dev, rn); 85 } 86 87 /* Get the current slave list, or search (with or without alarm) */ 88 static int w1_get_slaves(struct w1_master *dev, 89 struct cn_msg *req_msg, struct w1_netlink_msg *req_hdr, 90 struct w1_netlink_cmd *req_cmd) 91 { 92 struct cn_msg *msg; 93 struct w1_netlink_msg *hdr; 94 struct w1_netlink_cmd *cmd; 95 struct w1_slave *sl; 96 97 msg = kzalloc(PAGE_SIZE, GFP_KERNEL); 98 if (!msg) 99 return -ENOMEM; 100 101 msg->id = req_msg->id; 102 msg->seq = req_msg->seq; 103 msg->ack = 0; 104 msg->len = sizeof(struct w1_netlink_msg) + 105 sizeof(struct w1_netlink_cmd); 106 107 hdr = (struct w1_netlink_msg *)(msg + 1); 108 cmd = (struct w1_netlink_cmd *)(hdr + 1); 109 110 hdr->type = W1_MASTER_CMD; 111 hdr->id = req_hdr->id; 112 hdr->len = sizeof(struct w1_netlink_cmd); 113 114 cmd->cmd = req_cmd->cmd; 115 cmd->len = 0; 116 117 dev->priv = msg; 118 dev->priv_size = PAGE_SIZE - msg->len - sizeof(struct cn_msg); 119 120 if (req_cmd->cmd == W1_CMD_LIST_SLAVES) { 121 __u64 rn; 122 list_for_each_entry(sl, &dev->slist, w1_slave_entry) { 123 memcpy(&rn, &sl->reg_num, sizeof(rn)); 124 w1_send_slave(dev, rn); 125 } 126 } else { 127 w1_search_process_cb(dev, cmd->cmd == W1_CMD_ALARM_SEARCH ? 128 W1_ALARM_SEARCH : W1_SEARCH, w1_found_send_slave); 129 } 130 131 msg->ack = 0; 132 cn_netlink_send(msg, 0, GFP_KERNEL); 133 134 dev->priv = NULL; 135 dev->priv_size = 0; 136 137 kfree(msg); 138 139 return 0; 140 } 141 142 static int w1_send_read_reply(struct cn_msg *msg, struct w1_netlink_msg *hdr, 143 struct w1_netlink_cmd *cmd) 144 { 145 void *data; 146 struct w1_netlink_msg *h; 147 struct w1_netlink_cmd *c; 148 struct cn_msg *cm; 149 int err; 150 151 data = kzalloc(sizeof(struct cn_msg) + 152 sizeof(struct w1_netlink_msg) + 153 sizeof(struct w1_netlink_cmd) + 154 cmd->len, GFP_KERNEL); 155 if (!data) 156 return -ENOMEM; 157 158 cm = (struct cn_msg *)(data); 159 h = (struct w1_netlink_msg *)(cm + 1); 160 c = (struct w1_netlink_cmd *)(h + 1); 161 162 memcpy(cm, msg, sizeof(struct cn_msg)); 163 memcpy(h, hdr, sizeof(struct w1_netlink_msg)); 164 memcpy(c, cmd, sizeof(struct w1_netlink_cmd)); 165 166 cm->ack = msg->seq+1; 167 cm->len = sizeof(struct w1_netlink_msg) + 168 sizeof(struct w1_netlink_cmd) + cmd->len; 169 170 h->len = sizeof(struct w1_netlink_cmd) + cmd->len; 171 172 memcpy(c->data, cmd->data, c->len); 173 174 err = cn_netlink_send(cm, 0, GFP_KERNEL); 175 176 kfree(data); 177 178 return err; 179 } 180 181 static int w1_process_command_io(struct w1_master *dev, struct cn_msg *msg, 182 struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd) 183 { 184 int err = 0; 185 186 switch (cmd->cmd) { 187 case W1_CMD_TOUCH: 188 w1_touch_block(dev, cmd->data, cmd->len); 189 w1_send_read_reply(msg, hdr, cmd); 190 break; 191 case W1_CMD_READ: 192 w1_read_block(dev, cmd->data, cmd->len); 193 w1_send_read_reply(msg, hdr, cmd); 194 break; 195 case W1_CMD_WRITE: 196 w1_write_block(dev, cmd->data, cmd->len); 197 break; 198 default: 199 err = -EINVAL; 200 break; 201 } 202 203 return err; 204 } 205 206 static int w1_process_command_addremove(struct w1_master *dev, 207 struct cn_msg *msg, struct w1_netlink_msg *hdr, 208 struct w1_netlink_cmd *cmd) 209 { 210 struct w1_slave *sl; 211 int err = 0; 212 struct w1_reg_num *id; 213 214 if (cmd->len != 8) 215 return -EINVAL; 216 217 id = (struct w1_reg_num *)cmd->data; 218 219 sl = w1_slave_search_device(dev, id); 220 switch (cmd->cmd) { 221 case W1_CMD_SLAVE_ADD: 222 if (sl) 223 err = -EINVAL; 224 else 225 err = w1_attach_slave_device(dev, id); 226 break; 227 case W1_CMD_SLAVE_REMOVE: 228 if (sl) 229 w1_slave_detach(sl); 230 else 231 err = -EINVAL; 232 break; 233 default: 234 err = -EINVAL; 235 break; 236 } 237 238 return err; 239 } 240 241 static int w1_process_command_master(struct w1_master *dev, 242 struct cn_msg *req_msg, struct w1_netlink_msg *req_hdr, 243 struct w1_netlink_cmd *req_cmd) 244 { 245 int err = -EINVAL; 246 247 switch (req_cmd->cmd) { 248 case W1_CMD_SEARCH: 249 case W1_CMD_ALARM_SEARCH: 250 case W1_CMD_LIST_SLAVES: 251 err = w1_get_slaves(dev, req_msg, req_hdr, req_cmd); 252 break; 253 case W1_CMD_READ: 254 case W1_CMD_WRITE: 255 case W1_CMD_TOUCH: 256 err = w1_process_command_io(dev, req_msg, req_hdr, req_cmd); 257 break; 258 case W1_CMD_RESET: 259 err = w1_reset_bus(dev); 260 break; 261 case W1_CMD_SLAVE_ADD: 262 case W1_CMD_SLAVE_REMOVE: 263 err = w1_process_command_addremove(dev, req_msg, req_hdr, 264 req_cmd); 265 break; 266 default: 267 err = -EINVAL; 268 break; 269 } 270 271 return err; 272 } 273 274 static int w1_process_command_slave(struct w1_slave *sl, struct cn_msg *msg, 275 struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd) 276 { 277 dev_dbg(&sl->master->dev, "%s: %02x.%012llx.%02x: cmd=%02x, len=%u.\n", 278 __func__, sl->reg_num.family, (unsigned long long)sl->reg_num.id, 279 sl->reg_num.crc, cmd->cmd, cmd->len); 280 281 return w1_process_command_io(sl->master, msg, hdr, cmd); 282 } 283 284 static int w1_process_command_root(struct cn_msg *msg, struct w1_netlink_msg *mcmd) 285 { 286 struct w1_master *m; 287 struct cn_msg *cn; 288 struct w1_netlink_msg *w; 289 u32 *id; 290 291 if (mcmd->type != W1_LIST_MASTERS) { 292 printk(KERN_NOTICE "%s: msg: %x.%x, wrong type: %u, len: %u.\n", 293 __func__, msg->id.idx, msg->id.val, mcmd->type, mcmd->len); 294 return -EPROTO; 295 } 296 297 cn = kmalloc(PAGE_SIZE, GFP_KERNEL); 298 if (!cn) 299 return -ENOMEM; 300 301 cn->id.idx = CN_W1_IDX; 302 cn->id.val = CN_W1_VAL; 303 304 cn->seq = msg->seq; 305 cn->ack = 1; 306 cn->len = sizeof(struct w1_netlink_msg); 307 w = (struct w1_netlink_msg *)(cn + 1); 308 309 w->type = W1_LIST_MASTERS; 310 w->status = 0; 311 w->len = 0; 312 id = (u32 *)(w + 1); 313 314 mutex_lock(&w1_mlock); 315 list_for_each_entry(m, &w1_masters, w1_master_entry) { 316 if (cn->len + sizeof(*id) > PAGE_SIZE - sizeof(struct cn_msg)) { 317 cn_netlink_send(cn, 0, GFP_KERNEL); 318 cn->ack++; 319 cn->len = sizeof(struct w1_netlink_msg); 320 w->len = 0; 321 id = (u32 *)(w + 1); 322 } 323 324 *id = m->id; 325 w->len += sizeof(*id); 326 cn->len += sizeof(*id); 327 id++; 328 } 329 cn->ack = 0; 330 cn_netlink_send(cn, 0, GFP_KERNEL); 331 mutex_unlock(&w1_mlock); 332 333 kfree(cn); 334 return 0; 335 } 336 337 static int w1_netlink_send_error(struct cn_msg *rcmsg, struct w1_netlink_msg *rmsg, 338 struct w1_netlink_cmd *rcmd, int error) 339 { 340 struct cn_msg *cmsg; 341 struct w1_netlink_msg *msg; 342 struct w1_netlink_cmd *cmd; 343 344 cmsg = kzalloc(sizeof(*msg) + sizeof(*cmd) + sizeof(*cmsg), GFP_KERNEL); 345 if (!cmsg) 346 return -ENOMEM; 347 348 msg = (struct w1_netlink_msg *)(cmsg + 1); 349 cmd = (struct w1_netlink_cmd *)(msg + 1); 350 351 memcpy(cmsg, rcmsg, sizeof(*cmsg)); 352 cmsg->len = sizeof(*msg); 353 354 memcpy(msg, rmsg, sizeof(*msg)); 355 msg->len = 0; 356 msg->status = (short)-error; 357 358 if (rcmd) { 359 memcpy(cmd, rcmd, sizeof(*cmd)); 360 cmd->len = 0; 361 msg->len += sizeof(*cmd); 362 cmsg->len += sizeof(*cmd); 363 } 364 365 error = cn_netlink_send(cmsg, 0, GFP_KERNEL); 366 kfree(cmsg); 367 368 return error; 369 } 370 371 static void w1_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) 372 { 373 struct w1_netlink_msg *m = (struct w1_netlink_msg *)(msg + 1); 374 struct w1_netlink_cmd *cmd; 375 struct w1_slave *sl; 376 struct w1_master *dev; 377 int err = 0; 378 379 while (msg->len && !err) { 380 struct w1_reg_num id; 381 u16 mlen = m->len; 382 u8 *cmd_data = m->data; 383 384 dev = NULL; 385 sl = NULL; 386 cmd = NULL; 387 388 memcpy(&id, m->id.id, sizeof(id)); 389 #if 0 390 printk("%s: %02x.%012llx.%02x: type=%02x, len=%u.\n", 391 __func__, id.family, (unsigned long long)id.id, id.crc, m->type, m->len); 392 #endif 393 if (m->len + sizeof(struct w1_netlink_msg) > msg->len) { 394 err = -E2BIG; 395 break; 396 } 397 398 if (m->type == W1_MASTER_CMD) { 399 dev = w1_search_master_id(m->id.mst.id); 400 } else if (m->type == W1_SLAVE_CMD) { 401 sl = w1_search_slave(&id); 402 if (sl) 403 dev = sl->master; 404 } else { 405 err = w1_process_command_root(msg, m); 406 goto out_cont; 407 } 408 409 if (!dev) { 410 err = -ENODEV; 411 goto out_cont; 412 } 413 414 err = 0; 415 if (!mlen) 416 goto out_cont; 417 418 mutex_lock(&dev->mutex); 419 420 if (sl && w1_reset_select_slave(sl)) { 421 err = -ENODEV; 422 goto out_up; 423 } 424 425 while (mlen) { 426 cmd = (struct w1_netlink_cmd *)cmd_data; 427 428 if (cmd->len + sizeof(struct w1_netlink_cmd) > mlen) { 429 err = -E2BIG; 430 break; 431 } 432 433 if (sl) 434 err = w1_process_command_slave(sl, msg, m, cmd); 435 else 436 err = w1_process_command_master(dev, msg, m, cmd); 437 438 w1_netlink_send_error(msg, m, cmd, err); 439 err = 0; 440 441 cmd_data += cmd->len + sizeof(struct w1_netlink_cmd); 442 mlen -= cmd->len + sizeof(struct w1_netlink_cmd); 443 } 444 out_up: 445 atomic_dec(&dev->refcnt); 446 if (sl) 447 atomic_dec(&sl->refcnt); 448 mutex_unlock(&dev->mutex); 449 out_cont: 450 if (!cmd || err) 451 w1_netlink_send_error(msg, m, cmd, err); 452 msg->len -= sizeof(struct w1_netlink_msg) + m->len; 453 m = (struct w1_netlink_msg *)(((u8 *)m) + sizeof(struct w1_netlink_msg) + m->len); 454 455 /* 456 * Let's allow requests for nonexisting devices. 457 */ 458 if (err == -ENODEV) 459 err = 0; 460 } 461 } 462 463 int w1_init_netlink(void) 464 { 465 struct cb_id w1_id = {.idx = CN_W1_IDX, .val = CN_W1_VAL}; 466 467 return cn_add_callback(&w1_id, "w1", &w1_cn_callback); 468 } 469 470 void w1_fini_netlink(void) 471 { 472 struct cb_id w1_id = {.idx = CN_W1_IDX, .val = CN_W1_VAL}; 473 474 cn_del_callback(&w1_id); 475 } 476 #else 477 void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg) 478 { 479 } 480 481 int w1_init_netlink(void) 482 { 483 return 0; 484 } 485 486 void w1_fini_netlink(void) 487 { 488 } 489 #endif 490