/** * pdcspath_layer_write - This function handles extended layer modifying. * @entry: An allocated and populated pdscpath_entry struct. * @buf: The input buffer to read from. * @count: The number of bytes to be read. * * We will call this function to change the current layer value. * Layers are to be given '.'-delimited, without brackets. * XXX beware we are far less checky WRT input data provided than for hwpath. * Potential harm can be done, since there's no way to check the validity of * the layer fields. */ static ssize_t pdcspath_layer_write(struct pdcspath_entry *entry, const char *buf, size_t count) { unsigned int layers[6]; /* device-specific info (ctlr#, unit#, ...) */ unsigned short i; char in[64], *temp; if (!entry || !buf || !count) return -EINVAL; /* We'll use a local copy of buf */ count = min_t(size_t, count, sizeof(in)-1); strncpy(in, buf, count); in[count] = '\0'; /* Let's clean up the target. 0 is a blank pattern */ memset(&layers, 0, sizeof(layers)); /* First, pick the first layer */ if (unlikely(!isdigit(*in))) return -EINVAL; layers[0] = simple_strtoul(in, NULL, 10); DPRINTK("%s: layer[0]: %d\n", __func__, layers[0]); temp = in; for (i=1; ((temp = strchr(temp, '.'))) && (likely(i<6)); i++) { if (unlikely(!isdigit(*(++temp)))) return -EINVAL; layers[i] = simple_strtoul(temp, NULL, 10); DPRINTK("%s: layer[%d]: %d\n", __func__, i, layers[i]); } /* So far so good, let's get in deep */ write_lock(&entry->rw_lock); /* First, overwrite the current layers with the new ones, not touching the hardware path. */ memcpy(&entry->devpath.layers, &layers, sizeof(layers)); /* Now, dive in. Write back to the hardware */ pdcspath_store(entry); write_unlock(&entry->rw_lock); printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" layers to \"%s\"\n", entry->name, buf); return count; }
static ssize_t pdcspath_layer_write(struct pdcspath_entry *entry, const char *buf, size_t count) { unsigned int layers[6]; unsigned short i; char in[count+1], *temp; if (!entry || !buf || !count) return -EINVAL; memset(in, 0, count+1); strncpy(in, buf, count); memset(&layers, 0, sizeof(layers)); if (unlikely(!isdigit(*in))) return -EINVAL; layers[0] = simple_strtoul(in, NULL, 10); DPRINTK("%s: layer[0]: %d\n", __func__, layers[0]); temp = in; for (i=1; ((temp = strchr(temp, '.'))) && (likely(i<6)); i++) { if (unlikely(!isdigit(*(++temp)))) return -EINVAL; layers[i] = simple_strtoul(temp, NULL, 10); DPRINTK("%s: layer[%d]: %d\n", __func__, i, layers[i]); } write_lock(&entry->rw_lock); memcpy(&entry->devpath.layers, &layers, sizeof(layers)); pdcspath_store(entry); write_unlock(&entry->rw_lock); printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" layers to \"%s\"\n", entry->name, buf); return count; }
/** * pdcs_auto_write - This function handles autoboot/search flag modifying. * @buf: The input buffer to read from. * @count: The number of bytes to be read. * @knob: The PF_AUTOBOOT or PF_AUTOSEARCH flag * * We will call this function to change the current autoboot flag. * We expect a precise syntax: * \"n\" (n == 0 or 1) to toggle AutoBoot Off or On */ static ssize_t pdcs_auto_write(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count, int knob) { struct pdcspath_entry *pathentry; unsigned char flags; char in[count+1], *temp; char c; if (!capable(CAP_SYS_ADMIN)) return -EACCES; if (!buf || !count) return -EINVAL; /* We'll use a local copy of buf */ memset(in, 0, count+1); strncpy(in, buf, count); /* Current flags are stored in primary boot path entry */ pathentry = &pdcspath_entry_primary; /* Be nice to the existing flag record */ read_lock(&pathentry->rw_lock); flags = pathentry->devpath.flags; read_unlock(&pathentry->rw_lock); DPRINTK("%s: flags before: 0x%X\n", __func__, flags); temp = in; while (*temp && isspace(*temp)) temp++; c = *temp++ - '0'; if ((c != 0) && (c != 1)) goto parse_error; if (c == 0) flags &= ~knob; else flags |= knob; DPRINTK("%s: flags after: 0x%X\n", __func__, flags); /* So far so good, let's get in deep */ write_lock(&pathentry->rw_lock); /* Change the path entry flags first */ pathentry->devpath.flags = flags; /* Now, dive in. Write back to the hardware */ pdcspath_store(pathentry); write_unlock(&pathentry->rw_lock); printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" to \"%s\"\n", (knob & PF_AUTOBOOT) ? "autoboot" : "autosearch", (flags & knob) ? "On" : "Off"); return count; parse_error: printk(KERN_WARNING "%s: Parse error: expect \"n\" (n == 0 or 1)\n", __func__); return -EINVAL; }
/** * pdcspath_hwpath_write - This function handles hardware path modifying. * @entry: An allocated and populated pdscpath_entry struct. * @buf: The input buffer to read from. * @count: The number of bytes to be read. * * We will call this function to change the current hardware path. * Hardware paths are to be given '/'-delimited, without brackets. * We make sure that the provided path actually maps to an existing * device, BUT nothing would prevent some foolish user to set the path to some * PCI bridge or even a CPU... * A better work around would be to make sure we are at the end of a device tree * for instance, but it would be IMHO beyond the simple scope of that driver. * The aim is to provide a facility. Data correctness is left to userland. */ static ssize_t pdcspath_hwpath_write(struct pdcspath_entry *entry, const char *buf, size_t count) { struct hardware_path hwpath; unsigned short i; char in[count+1], *temp; struct device *dev; int ret; if (!entry || !buf || !count) return -EINVAL; /* We'll use a local copy of buf */ memset(in, 0, count+1); strncpy(in, buf, count); /* Let's clean up the target. 0xff is a blank pattern */ memset(&hwpath, 0xff, sizeof(hwpath)); /* First, pick the mod field (the last one of the input string) */ if (!(temp = strrchr(in, '/'))) return -EINVAL; hwpath.mod = simple_strtoul(temp+1, NULL, 10); in[temp-in] = '\0'; /* truncate the remaining string. just precaution */ DPRINTK("%s: mod: %d\n", __func__, hwpath.mod); /* Then, loop for each delimiter, making sure we don't have too many. we write the bc fields in a down-top way. No matter what, we stop before writing the last field. If there are too many fields anyway, then the user is a moron and it'll be caught up later when we'll check the consistency of the given hwpath. */ for (i=5; ((temp = strrchr(in, '/'))) && (temp-in > 0) && (likely(i)); i--) { hwpath.bc[i] = simple_strtoul(temp+1, NULL, 10); in[temp-in] = '\0'; DPRINTK("%s: bc[%d]: %d\n", __func__, i, hwpath.bc[i]); } /* Store the final field */ hwpath.bc[i] = simple_strtoul(in, NULL, 10); DPRINTK("%s: bc[%d]: %d\n", __func__, i, hwpath.bc[i]); /* Now we check that the user isn't trying to lure us */ if (!(dev = hwpath_to_device((struct hardware_path *)&hwpath))) { printk(KERN_WARNING "%s: attempt to set invalid \"%s\" " "hardware path: %s\n", __func__, entry->name, buf); return -EINVAL; } /* So far so good, let's get in deep */ write_lock(&entry->rw_lock); entry->ready = 0; entry->dev = dev; /* Now, dive in. Write back to the hardware */ pdcspath_store(entry); /* Update the symlink to the real device */ sysfs_remove_link(&entry->kobj, "device"); ret = sysfs_create_link(&entry->kobj, &entry->dev->kobj, "device"); WARN_ON(ret); write_unlock(&entry->rw_lock); printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" path to \"%s\"\n", entry->name, buf); return count; }
static ssize_t pdcs_auto_write(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count, int knob) { struct pdcspath_entry *pathentry; unsigned char flags; char in[count+1], *temp; char c; if (!capable(CAP_SYS_ADMIN)) return -EACCES; if (!buf || !count) return -EINVAL; memset(in, 0, count+1); strncpy(in, buf, count); pathentry = &pdcspath_entry_primary; read_lock(&pathentry->rw_lock); flags = pathentry->devpath.flags; read_unlock(&pathentry->rw_lock); DPRINTK("%s: flags before: 0x%X\n", __func__, flags); temp = skip_spaces(in); c = *temp++ - '0'; if ((c != 0) && (c != 1)) goto parse_error; if (c == 0) flags &= ~knob; else flags |= knob; DPRINTK("%s: flags after: 0x%X\n", __func__, flags); write_lock(&pathentry->rw_lock); pathentry->devpath.flags = flags; pdcspath_store(pathentry); write_unlock(&pathentry->rw_lock); printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" to \"%s\"\n", (knob & PF_AUTOBOOT) ? "autoboot" : "autosearch", (flags & knob) ? "On" : "Off"); return count; parse_error: printk(KERN_WARNING "%s: Parse error: expect \"n\" (n == 0 or 1)\n", __func__); return -EINVAL; }
static ssize_t pdcspath_hwpath_write(struct pdcspath_entry *entry, const char *buf, size_t count) { struct hardware_path hwpath; unsigned short i; char in[count+1], *temp; struct device *dev; int ret; if (!entry || !buf || !count) return -EINVAL; memset(in, 0, count+1); strncpy(in, buf, count); memset(&hwpath, 0xff, sizeof(hwpath)); if (!(temp = strrchr(in, '/'))) return -EINVAL; hwpath.mod = simple_strtoul(temp+1, NULL, 10); in[temp-in] = '\0'; DPRINTK("%s: mod: %d\n", __func__, hwpath.mod); for (i=5; ((temp = strrchr(in, '/'))) && (temp-in > 0) && (likely(i)); i--) { hwpath.bc[i] = simple_strtoul(temp+1, NULL, 10); in[temp-in] = '\0'; DPRINTK("%s: bc[%d]: %d\n", __func__, i, hwpath.bc[i]); } hwpath.bc[i] = simple_strtoul(in, NULL, 10); DPRINTK("%s: bc[%d]: %d\n", __func__, i, hwpath.bc[i]); if (!(dev = hwpath_to_device((struct hardware_path *)&hwpath))) { printk(KERN_WARNING "%s: attempt to set invalid \"%s\" " "hardware path: %s\n", __func__, entry->name, buf); return -EINVAL; } write_lock(&entry->rw_lock); entry->ready = 0; entry->dev = dev; pdcspath_store(entry); sysfs_remove_link(&entry->kobj, "device"); ret = sysfs_create_link(&entry->kobj, &entry->dev->kobj, "device"); WARN_ON(ret); write_unlock(&entry->rw_lock); printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" path to \"%s\"\n", entry->name, buf); return count; }