/** * pdcspath_fetch - This function populates the path entry structs. * @entry: A pointer to an allocated pdcspath_entry. * * The general idea is that you don't read from the Stable Storage every time * you access the files provided by the facilites. We store a copy of the * content of the stable storage WRT various paths in these structs. We read * these structs when reading the files, and we will write to these structs when * writing to the files, and only then write them back to the Stable Storage. * * This function expects to be called with @entry->rw_lock write-hold. */ static int pdcspath_fetch(struct pdcspath_entry *entry) { struct device_path *devpath; if (!entry) return -EINVAL; devpath = &entry->devpath; DPRINTK("%s: fetch: 0x%p, 0x%p, addr: 0x%lx\n", __func__, entry, devpath, entry->addr); /* addr, devpath and count must be word aligned */ if (pdc_stable_read(entry->addr, devpath, sizeof(*devpath)) != PDC_OK) return -EIO; /* Find the matching device. NOTE: hardware_path overlays with device_path, so the nice cast can be used */ entry->dev = hwpath_to_device((struct hardware_path *)devpath); entry->ready = 1; DPRINTK("%s: device: 0x%p\n", __func__, entry->dev); return 0; }
static int pdcspath_fetch(struct pdcspath_entry *entry) { struct device_path *devpath; if (!entry) return -EINVAL; devpath = &entry->devpath; DPRINTK("%s: fetch: 0x%p, 0x%p, addr: 0x%lx\n", __func__, entry, devpath, entry->addr); if (pdc_stable_read(entry->addr, devpath, sizeof(*devpath)) != PDC_OK) return -EIO; entry->dev = hwpath_to_device((struct hardware_path *)devpath); entry->ready = 1; DPRINTK("%s: device: 0x%p\n", __func__, entry->dev); return 0; }
/** * 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 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; }