diff options
Diffstat (limited to 'drivers/w1')
-rw-r--r-- | drivers/w1/w1.c | 138 |
1 files changed, 137 insertions, 1 deletions
diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index 32418d4e555a..f3be2991e6e8 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c | |||
@@ -56,6 +56,8 @@ module_param_named(slave_ttl, w1_max_slave_ttl, int, 0); | |||
56 | DEFINE_MUTEX(w1_mlock); | 56 | DEFINE_MUTEX(w1_mlock); |
57 | LIST_HEAD(w1_masters); | 57 | LIST_HEAD(w1_masters); |
58 | 58 | ||
59 | static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn); | ||
60 | |||
59 | static int w1_master_match(struct device *dev, struct device_driver *drv) | 61 | static int w1_master_match(struct device *dev, struct device_driver *drv) |
60 | { | 62 | { |
61 | return 1; | 63 | return 1; |
@@ -357,7 +359,8 @@ static ssize_t w1_master_attribute_show_slave_count(struct device *dev, struct d | |||
357 | return count; | 359 | return count; |
358 | } | 360 | } |
359 | 361 | ||
360 | static ssize_t w1_master_attribute_show_slaves(struct device *dev, struct device_attribute *attr, char *buf) | 362 | static ssize_t w1_master_attribute_show_slaves(struct device *dev, |
363 | struct device_attribute *attr, char *buf) | ||
361 | { | 364 | { |
362 | struct w1_master *md = dev_to_w1_master(dev); | 365 | struct w1_master *md = dev_to_w1_master(dev); |
363 | int c = PAGE_SIZE; | 366 | int c = PAGE_SIZE; |
@@ -382,6 +385,135 @@ static ssize_t w1_master_attribute_show_slaves(struct device *dev, struct device | |||
382 | return PAGE_SIZE - c; | 385 | return PAGE_SIZE - c; |
383 | } | 386 | } |
384 | 387 | ||
388 | static ssize_t w1_master_attribute_show_add(struct device *dev, | ||
389 | struct device_attribute *attr, char *buf) | ||
390 | { | ||
391 | int c = PAGE_SIZE; | ||
392 | c -= snprintf(buf+PAGE_SIZE - c, c, | ||
393 | "write device id xx-xxxxxxxxxxxx to add slave\n"); | ||
394 | return PAGE_SIZE - c; | ||
395 | } | ||
396 | |||
397 | static int w1_atoreg_num(struct device *dev, const char *buf, size_t count, | ||
398 | struct w1_reg_num *rn) | ||
399 | { | ||
400 | unsigned int family; | ||
401 | unsigned long long id; | ||
402 | int i; | ||
403 | u64 rn64_le; | ||
404 | |||
405 | /* The CRC value isn't read from the user because the sysfs directory | ||
406 | * doesn't include it and most messages from the bus search don't | ||
407 | * print it either. It would be unreasonable for the user to then | ||
408 | * provide it. | ||
409 | */ | ||
410 | const char *error_msg = "bad slave string format, expecting " | ||
411 | "ff-dddddddddddd\n"; | ||
412 | |||
413 | if (buf[2] != '-') { | ||
414 | dev_err(dev, "%s", error_msg); | ||
415 | return -EINVAL; | ||
416 | } | ||
417 | i = sscanf(buf, "%02x-%012llx", &family, &id); | ||
418 | if (i != 2) { | ||
419 | dev_err(dev, "%s", error_msg); | ||
420 | return -EINVAL; | ||
421 | } | ||
422 | rn->family = family; | ||
423 | rn->id = id; | ||
424 | |||
425 | rn64_le = cpu_to_le64(*(u64 *)rn); | ||
426 | rn->crc = w1_calc_crc8((u8 *)&rn64_le, 7); | ||
427 | |||
428 | #if 0 | ||
429 | dev_info(dev, "With CRC device is %02x.%012llx.%02x.\n", | ||
430 | rn->family, (unsigned long long)rn->id, rn->crc); | ||
431 | #endif | ||
432 | |||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | /* Searches the slaves in the w1_master and returns a pointer or NULL. | ||
437 | * Note: must hold the mutex | ||
438 | */ | ||
439 | static struct w1_slave *w1_slave_search_device(struct w1_master *dev, | ||
440 | struct w1_reg_num *rn) | ||
441 | { | ||
442 | struct w1_slave *sl; | ||
443 | list_for_each_entry(sl, &dev->slist, w1_slave_entry) { | ||
444 | if (sl->reg_num.family == rn->family && | ||
445 | sl->reg_num.id == rn->id && | ||
446 | sl->reg_num.crc == rn->crc) { | ||
447 | return sl; | ||
448 | } | ||
449 | } | ||
450 | return NULL; | ||
451 | } | ||
452 | |||
453 | static ssize_t w1_master_attribute_store_add(struct device *dev, | ||
454 | struct device_attribute *attr, | ||
455 | const char *buf, size_t count) | ||
456 | { | ||
457 | struct w1_master *md = dev_to_w1_master(dev); | ||
458 | struct w1_reg_num rn; | ||
459 | struct w1_slave *sl; | ||
460 | ssize_t result = count; | ||
461 | |||
462 | if (w1_atoreg_num(dev, buf, count, &rn)) | ||
463 | return -EINVAL; | ||
464 | |||
465 | mutex_lock(&md->mutex); | ||
466 | sl = w1_slave_search_device(md, &rn); | ||
467 | /* It would be nice to do a targeted search one the one-wire bus | ||
468 | * for the new device to see if it is out there or not. But the | ||
469 | * current search doesn't support that. | ||
470 | */ | ||
471 | if (sl) { | ||
472 | dev_info(dev, "Device %s already exists\n", sl->name); | ||
473 | result = -EINVAL; | ||
474 | } else { | ||
475 | w1_attach_slave_device(md, &rn); | ||
476 | } | ||
477 | mutex_unlock(&md->mutex); | ||
478 | |||
479 | return result; | ||
480 | } | ||
481 | |||
482 | static ssize_t w1_master_attribute_show_remove(struct device *dev, | ||
483 | struct device_attribute *attr, char *buf) | ||
484 | { | ||
485 | int c = PAGE_SIZE; | ||
486 | c -= snprintf(buf+PAGE_SIZE - c, c, | ||
487 | "write device id xx-xxxxxxxxxxxx to remove slave\n"); | ||
488 | return PAGE_SIZE - c; | ||
489 | } | ||
490 | |||
491 | static ssize_t w1_master_attribute_store_remove(struct device *dev, | ||
492 | struct device_attribute *attr, | ||
493 | const char *buf, size_t count) | ||
494 | { | ||
495 | struct w1_master *md = dev_to_w1_master(dev); | ||
496 | struct w1_reg_num rn; | ||
497 | struct w1_slave *sl; | ||
498 | ssize_t result = count; | ||
499 | |||
500 | if (w1_atoreg_num(dev, buf, count, &rn)) | ||
501 | return -EINVAL; | ||
502 | |||
503 | mutex_lock(&md->mutex); | ||
504 | sl = w1_slave_search_device(md, &rn); | ||
505 | if (sl) { | ||
506 | w1_slave_detach(sl); | ||
507 | } else { | ||
508 | dev_info(dev, "Device %02x-%012llx doesn't exists\n", rn.family, | ||
509 | (unsigned long long)rn.id); | ||
510 | result = -EINVAL; | ||
511 | } | ||
512 | mutex_unlock(&md->mutex); | ||
513 | |||
514 | return result; | ||
515 | } | ||
516 | |||
385 | #define W1_MASTER_ATTR_RO(_name, _mode) \ | 517 | #define W1_MASTER_ATTR_RO(_name, _mode) \ |
386 | struct device_attribute w1_master_attribute_##_name = \ | 518 | struct device_attribute w1_master_attribute_##_name = \ |
387 | __ATTR(w1_master_##_name, _mode, \ | 519 | __ATTR(w1_master_##_name, _mode, \ |
@@ -402,6 +534,8 @@ static W1_MASTER_ATTR_RO(timeout, S_IRUGO); | |||
402 | static W1_MASTER_ATTR_RO(pointer, S_IRUGO); | 534 | static W1_MASTER_ATTR_RO(pointer, S_IRUGO); |
403 | static W1_MASTER_ATTR_RW(search, S_IRUGO | S_IWUGO); | 535 | static W1_MASTER_ATTR_RW(search, S_IRUGO | S_IWUGO); |
404 | static W1_MASTER_ATTR_RW(pullup, S_IRUGO | S_IWUGO); | 536 | static W1_MASTER_ATTR_RW(pullup, S_IRUGO | S_IWUGO); |
537 | static W1_MASTER_ATTR_RW(add, S_IRUGO | S_IWUGO); | ||
538 | static W1_MASTER_ATTR_RW(remove, S_IRUGO | S_IWUGO); | ||
405 | 539 | ||
406 | static struct attribute *w1_master_default_attrs[] = { | 540 | static struct attribute *w1_master_default_attrs[] = { |
407 | &w1_master_attribute_name.attr, | 541 | &w1_master_attribute_name.attr, |
@@ -413,6 +547,8 @@ static struct attribute *w1_master_default_attrs[] = { | |||
413 | &w1_master_attribute_pointer.attr, | 547 | &w1_master_attribute_pointer.attr, |
414 | &w1_master_attribute_search.attr, | 548 | &w1_master_attribute_search.attr, |
415 | &w1_master_attribute_pullup.attr, | 549 | &w1_master_attribute_pullup.attr, |
550 | &w1_master_attribute_add.attr, | ||
551 | &w1_master_attribute_remove.attr, | ||
416 | NULL | 552 | NULL |
417 | }; | 553 | }; |
418 | 554 | ||