w1: new driver. DS2431 chip
[safe/jmp/linux-2.6] / drivers / w1 / w1.c
index 730faa4..3b615d4 100644 (file)
@@ -56,6 +56,8 @@ module_param_named(slave_ttl, w1_max_slave_ttl, int, 0);
 DEFINE_MUTEX(w1_mlock);
 LIST_HEAD(w1_masters);
 
+static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn);
+
 static int w1_master_match(struct device *dev, struct device_driver *drv)
 {
        return 1;
@@ -79,10 +81,10 @@ static void w1_slave_release(struct device *dev)
 {
        struct w1_slave *sl = dev_to_w1_slave(dev);
 
-       printk("%s: Releasing %s.\n", __func__, sl->name);
+       dev_dbg(dev, "%s: Releasing %s.\n", __func__, sl->name);
 
        while (atomic_read(&sl->refcnt)) {
-               printk("Waiting for %s to become free: refcnt=%d.\n",
+               dev_dbg(dev, "Waiting for %s to become free: refcnt=%d.\n",
                                sl->name, atomic_read(&sl->refcnt));
                if (msleep_interruptible(1000))
                        flush_signals(current);
@@ -101,35 +103,20 @@ static ssize_t w1_slave_read_name(struct device *dev, struct device_attribute *a
        return sprintf(buf, "%s\n", sl->name);
 }
 
-static ssize_t w1_slave_read_id(struct kobject *kobj,
-                               struct bin_attribute *bin_attr,
-                               char *buf, loff_t off, size_t count)
+static ssize_t w1_slave_read_id(struct device *dev,
+       struct device_attribute *attr, char *buf)
 {
-       struct w1_slave *sl = kobj_to_w1_slave(kobj);
-
-       if (off > 8) {
-               count = 0;
-       } else {
-               if (off + count > 8)
-                       count = 8 - off;
-
-               memcpy(buf, (u8 *)&sl->reg_num, count);
-       }
+       struct w1_slave *sl = dev_to_w1_slave(dev);
+       ssize_t count = sizeof(sl->reg_num);
 
+       memcpy(buf, (u8 *)&sl->reg_num, count);
        return count;
 }
 
 static struct device_attribute w1_slave_attr_name =
        __ATTR(name, S_IRUGO, w1_slave_read_name, NULL);
-
-static struct bin_attribute w1_slave_attr_bin_id = {
-      .attr = {
-              .name = "id",
-              .mode = S_IRUGO,
-      },
-      .size = 8,
-      .read = w1_slave_read_id,
-};
+static struct device_attribute w1_slave_attr_id =
+       __ATTR(id, S_IRUGO, w1_slave_read_id, NULL);
 
 /* Default family */
 
@@ -246,11 +233,16 @@ static ssize_t w1_master_attribute_store_search(struct device * dev,
                                                struct device_attribute *attr,
                                                const char * buf, size_t count)
 {
+       long tmp;
        struct w1_master *md = dev_to_w1_master(dev);
 
+       if (strict_strtol(buf, 0, &tmp) == -EINVAL)
+               return -EINVAL;
+
        mutex_lock(&md->mutex);
-       md->search_count = simple_strtol(buf, NULL, 0);
+       md->search_count = tmp;
        mutex_unlock(&md->mutex);
+       wake_up_process(md->thread);
 
        return count;
 }
@@ -269,6 +261,38 @@ static ssize_t w1_master_attribute_show_search(struct device *dev,
        return count;
 }
 
+static ssize_t w1_master_attribute_store_pullup(struct device *dev,
+                                               struct device_attribute *attr,
+                                               const char *buf, size_t count)
+{
+       long tmp;
+       struct w1_master *md = dev_to_w1_master(dev);
+
+       if (strict_strtol(buf, 0, &tmp) == -EINVAL)
+               return -EINVAL;
+
+       mutex_lock(&md->mutex);
+       md->enable_pullup = tmp;
+       mutex_unlock(&md->mutex);
+       wake_up_process(md->thread);
+
+       return count;
+}
+
+static ssize_t w1_master_attribute_show_pullup(struct device *dev,
+                                              struct device_attribute *attr,
+                                              char *buf)
+{
+       struct w1_master *md = dev_to_w1_master(dev);
+       ssize_t count;
+
+       mutex_lock(&md->mutex);
+       count = sprintf(buf, "%d\n", md->enable_pullup);
+       mutex_unlock(&md->mutex);
+
+       return count;
+}
+
 static ssize_t w1_master_attribute_show_pointer(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct w1_master *md = dev_to_w1_master(dev);
@@ -320,7 +344,8 @@ static ssize_t w1_master_attribute_show_slave_count(struct device *dev, struct d
        return count;
 }
 
-static ssize_t w1_master_attribute_show_slaves(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t w1_master_attribute_show_slaves(struct device *dev,
+       struct device_attribute *attr, char *buf)
 {
        struct w1_master *md = dev_to_w1_master(dev);
        int c = PAGE_SIZE;
@@ -345,6 +370,135 @@ static ssize_t w1_master_attribute_show_slaves(struct device *dev, struct device
        return PAGE_SIZE - c;
 }
 
+static ssize_t w1_master_attribute_show_add(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       int c = PAGE_SIZE;
+       c -= snprintf(buf+PAGE_SIZE - c, c,
+               "write device id xx-xxxxxxxxxxxx to add slave\n");
+       return PAGE_SIZE - c;
+}
+
+static int w1_atoreg_num(struct device *dev, const char *buf, size_t count,
+       struct w1_reg_num *rn)
+{
+       unsigned int family;
+       unsigned long long id;
+       int i;
+       u64 rn64_le;
+
+       /* The CRC value isn't read from the user because the sysfs directory
+        * doesn't include it and most messages from the bus search don't
+        * print it either.  It would be unreasonable for the user to then
+        * provide it.
+        */
+       const char *error_msg = "bad slave string format, expecting "
+               "ff-dddddddddddd\n";
+
+       if (buf[2] != '-') {
+               dev_err(dev, "%s", error_msg);
+               return -EINVAL;
+       }
+       i = sscanf(buf, "%02x-%012llx", &family, &id);
+       if (i != 2) {
+               dev_err(dev, "%s", error_msg);
+               return -EINVAL;
+       }
+       rn->family = family;
+       rn->id = id;
+
+       rn64_le = cpu_to_le64(*(u64 *)rn);
+       rn->crc = w1_calc_crc8((u8 *)&rn64_le, 7);
+
+#if 0
+       dev_info(dev, "With CRC device is %02x.%012llx.%02x.\n",
+                 rn->family, (unsigned long long)rn->id, rn->crc);
+#endif
+
+       return 0;
+}
+
+/* Searches the slaves in the w1_master and returns a pointer or NULL.
+ * Note: must hold the mutex
+ */
+static struct w1_slave *w1_slave_search_device(struct w1_master *dev,
+       struct w1_reg_num *rn)
+{
+       struct w1_slave *sl;
+       list_for_each_entry(sl, &dev->slist, w1_slave_entry) {
+               if (sl->reg_num.family == rn->family &&
+                               sl->reg_num.id == rn->id &&
+                               sl->reg_num.crc == rn->crc) {
+                       return sl;
+               }
+       }
+       return NULL;
+}
+
+static ssize_t w1_master_attribute_store_add(struct device *dev,
+                                               struct device_attribute *attr,
+                                               const char *buf, size_t count)
+{
+       struct w1_master *md = dev_to_w1_master(dev);
+       struct w1_reg_num rn;
+       struct w1_slave *sl;
+       ssize_t result = count;
+
+       if (w1_atoreg_num(dev, buf, count, &rn))
+               return -EINVAL;
+
+       mutex_lock(&md->mutex);
+       sl = w1_slave_search_device(md, &rn);
+       /* It would be nice to do a targeted search one the one-wire bus
+        * for the new device to see if it is out there or not.  But the
+        * current search doesn't support that.
+        */
+       if (sl) {
+               dev_info(dev, "Device %s already exists\n", sl->name);
+               result = -EINVAL;
+       } else {
+               w1_attach_slave_device(md, &rn);
+       }
+       mutex_unlock(&md->mutex);
+
+       return result;
+}
+
+static ssize_t w1_master_attribute_show_remove(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       int c = PAGE_SIZE;
+       c -= snprintf(buf+PAGE_SIZE - c, c,
+               "write device id xx-xxxxxxxxxxxx to remove slave\n");
+       return PAGE_SIZE - c;
+}
+
+static ssize_t w1_master_attribute_store_remove(struct device *dev,
+                                               struct device_attribute *attr,
+                                               const char *buf, size_t count)
+{
+       struct w1_master *md = dev_to_w1_master(dev);
+       struct w1_reg_num rn;
+       struct w1_slave *sl;
+       ssize_t result = count;
+
+       if (w1_atoreg_num(dev, buf, count, &rn))
+               return -EINVAL;
+
+       mutex_lock(&md->mutex);
+       sl = w1_slave_search_device(md, &rn);
+       if (sl) {
+               w1_slave_detach(sl);
+       } else {
+               dev_info(dev, "Device %02x-%012llx doesn't exists\n", rn.family,
+                       (unsigned long long)rn.id);
+               result = -EINVAL;
+       }
+       mutex_unlock(&md->mutex);
+
+       return result;
+}
+
 #define W1_MASTER_ATTR_RO(_name, _mode)                                \
        struct device_attribute w1_master_attribute_##_name =   \
                __ATTR(w1_master_##_name, _mode,                \
@@ -364,6 +518,9 @@ static W1_MASTER_ATTR_RO(attempts, S_IRUGO);
 static W1_MASTER_ATTR_RO(timeout, S_IRUGO);
 static W1_MASTER_ATTR_RO(pointer, S_IRUGO);
 static W1_MASTER_ATTR_RW(search, S_IRUGO | S_IWUGO);
+static W1_MASTER_ATTR_RW(pullup, S_IRUGO | S_IWUGO);
+static W1_MASTER_ATTR_RW(add, S_IRUGO | S_IWUGO);
+static W1_MASTER_ATTR_RW(remove, S_IRUGO | S_IWUGO);
 
 static struct attribute *w1_master_default_attrs[] = {
        &w1_master_attribute_name.attr,
@@ -374,6 +531,9 @@ static struct attribute *w1_master_default_attrs[] = {
        &w1_master_attribute_timeout.attr,
        &w1_master_attribute_pointer.attr,
        &w1_master_attribute_search.attr,
+       &w1_master_attribute_pullup.attr,
+       &w1_master_attribute_add.attr,
+       &w1_master_attribute_remove.attr,
        NULL
 };
 
@@ -475,7 +635,7 @@ static int __w1_attach_slave_device(struct w1_slave *sl)
        }
 
        /* Create "id" entry */
-       err = sysfs_create_bin_file(&sl->dev.kobj, &w1_slave_attr_bin_id);
+       err = device_create_file(&sl->dev, &w1_slave_attr_id);
        if (err < 0) {
                dev_err(&sl->dev,
                        "sysfs file creation for [%s] failed. err=%d\n",
@@ -497,7 +657,7 @@ static int __w1_attach_slave_device(struct w1_slave *sl)
        return 0;
 
 out_rem2:
-       sysfs_remove_bin_file(&sl->dev.kobj, &w1_slave_attr_bin_id);
+       device_remove_file(&sl->dev, &w1_slave_attr_id);
 out_rem1:
        device_remove_file(&sl->dev, &w1_slave_attr_name);
 out_unreg:
@@ -579,7 +739,7 @@ void w1_slave_detach(struct w1_slave *sl)
        msg.type = W1_SLAVE_REMOVE;
        w1_netlink_send(sl->master, &msg);
 
-       sysfs_remove_bin_file(&sl->dev.kobj, &w1_slave_attr_bin_id);
+       device_remove_file(&sl->dev, &w1_slave_attr_id);
        device_remove_file(&sl->dev, &w1_slave_attr_name);
        device_unregister(&sl->dev);
 
@@ -670,9 +830,7 @@ void w1_reconnect_slaves(struct w1_family *f, int attach)
 
 static void w1_slave_found(struct w1_master *dev, u64 rn)
 {
-       int slave_count;
        struct w1_slave *sl;
-       struct list_head *ent;
        struct w1_reg_num *tmp;
        u64 rn_le = cpu_to_le64(rn);
 
@@ -680,24 +838,12 @@ static void w1_slave_found(struct w1_master *dev, u64 rn)
 
        tmp = (struct w1_reg_num *) &rn;
 
-       slave_count = 0;
-       list_for_each(ent, &dev->slist) {
-
-               sl = list_entry(ent, struct w1_slave, w1_slave_entry);
-
-               if (sl->reg_num.family == tmp->family &&
-                   sl->reg_num.id == tmp->id &&
-                   sl->reg_num.crc == tmp->crc) {
-                       set_bit(W1_SLAVE_ACTIVE, (long *)&sl->flags);
-                       break;
-               }
-
-               slave_count++;
-       }
-
-       if (slave_count == dev->slave_count &&
-               rn && ((rn >> 56) & 0xff) == w1_calc_crc8((u8 *)&rn_le, 7)) {
-               w1_attach_slave_device(dev, tmp);
+       sl = w1_slave_search_device(dev, tmp);
+       if (sl) {
+               set_bit(W1_SLAVE_ACTIVE, (long *)&sl->flags);
+       } else {
+               if (rn && tmp->crc == w1_calc_crc8((u8 *)&rn_le, 7))
+                       w1_attach_slave_device(dev, tmp);
        }
 
        atomic_dec(&dev->refcnt);
@@ -773,8 +919,8 @@ void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb
                        tmp64 = (triplet_ret >> 2);
                        rn |= (tmp64 << i);
 
-                       if (test_bit(W1_MASTER_NEED_EXIT, &dev->flags)) {
-                               printk(KERN_INFO "Abort w1_search (exiting)\n");
+                       if (kthread_should_stop()) {
+                               dev_dbg(&dev->dev, "Abort w1_search\n");
                                return;
                        }
                }
@@ -811,8 +957,12 @@ void w1_search_process(struct w1_master *dev, u8 search_type)
 int w1_process(void *data)
 {
        struct w1_master *dev = (struct w1_master *) data;
+       /* As long as w1_timeout is only set by a module parameter the sleep
+        * time can be calculated in jiffies once.
+        */
+       const unsigned long jtime = msecs_to_jiffies(w1_timeout * 1000);
 
-       while (!kthread_should_stop() && !test_bit(W1_MASTER_NEED_EXIT, &dev->flags)) {
+       while (!kthread_should_stop()) {
                if (dev->search_count) {
                        mutex_lock(&dev->mutex);
                        w1_search_process(dev, W1_SEARCH);
@@ -820,7 +970,16 @@ int w1_process(void *data)
                }
 
                try_to_freeze();
-               msleep_interruptible(w1_timeout * 1000);
+               __set_current_state(TASK_INTERRUPTIBLE);
+
+               if (kthread_should_stop())
+                       break;
+
+               /* Only sleep when the search is active. */
+               if (dev->search_count)
+                       schedule_timeout(jtime);
+               else
+                       schedule();
        }
 
        atomic_dec(&dev->refcnt);