+ int i, j, k;
+ char *p;
+ u32 *q;
+ void *b;
+ openprom_property *op;
+
+ if (*ppos >= 0xffffff || count >= 0xffffff)
+ return -EINVAL;
+ if (!filp->private_data) {
+ i = property_read (filp, NULL, 0, NULL);
+ if (i)
+ return i;
+ }
+ k = *ppos;
+ op = (openprom_property *)filp->private_data;
+ if (!(op->flag & OPP_STRING)) {
+ u32 *first, *last;
+ int first_off, last_cnt;
+ u32 mask, mask2;
+ char tmp [9];
+ int forcelen = 0;
+
+ j = k % 9;
+ for (i = 0; i < count; i++, j++) {
+ if (j == 9) j = 0;
+ if (!j) {
+ char ctmp;
+ if (get_user(ctmp, &buf[i]))
+ return -EFAULT;
+ if (ctmp != '.') {
+ if (ctmp != '\n') {
+ if (op->flag & OPP_BINARY)
+ return -EINVAL;
+ else
+ goto write_try_string;
+ } else {
+ count = i + 1;
+ forcelen = 1;
+ break;
+ }
+ }
+ } else {
+ char ctmp;
+ if (get_user(ctmp, &buf[i]))
+ return -EFAULT;
+ if (ctmp < '0' ||
+ (ctmp > '9' && ctmp < 'A') ||
+ (ctmp > 'F' && ctmp < 'a') ||
+ ctmp > 'f') {
+ if (op->flag & OPP_BINARY)
+ return -EINVAL;
+ else
+ goto write_try_string;
+ }
+ }
+ }
+ op->flag |= OPP_BINARY;
+ tmp [8] = 0;
+ i = ((count + k + 8) / 9) << 2;
+ if (op->alloclen <= i) {
+ b = kmalloc (sizeof (openprom_property) + 2 * i,
+ GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
+ memcpy (b, filp->private_data,
+ sizeof (openprom_property)
+ + strlen (op->name) + op->alloclen);
+ memset (((char *)b) + sizeof (openprom_property)
+ + strlen (op->name) + op->alloclen,
+ 0, 2 * i - op->alloclen);
+ op = (openprom_property *)b;
+ op->alloclen = 2*i;
+ b = filp->private_data;
+ filp->private_data = (void *)op;
+ kfree (b);
+ }
+ first = ((u32 *)op->value) + (k / 9);
+ first_off = k % 9;
+ last = (u32 *)(op->value + i);
+ last_cnt = (k + count) % 9;
+ if (first + 1 == last) {
+ memset (tmp, '0', 8);
+ if (copy_from_user(tmp + first_off, buf,
+ (count + first_off > 8) ?
+ 8 - first_off : count))
+ return -EFAULT;
+ mask = 0xffffffff;
+ mask2 = 0xffffffff;
+ for (j = 0; j < first_off; j++)
+ mask >>= 1;
+ for (j = 8 - count - first_off; j > 0; j--)
+ mask2 <<= 1;
+ mask &= mask2;
+ if (mask) {
+ *first &= ~mask;
+ *first |= simple_strtoul (tmp, NULL, 16);
+ op->flag |= OPP_DIRTY;
+ }
+ } else {
+ op->flag |= OPP_DIRTY;
+ for (q = first; q < last; q++) {
+ if (q == first) {
+ if (first_off < 8) {
+ memset (tmp, '0', 8);
+ if (copy_from_user(tmp + first_off,
+ buf,
+ 8 - first_off))
+ return -EFAULT;
+ mask = 0xffffffff;
+ for (j = 0; j < first_off; j++)
+ mask >>= 1;
+ *q &= ~mask;
+ *q |= simple_strtoul (tmp,NULL,16);
+ }
+ buf += 9;
+ } else if ((q == last - 1) && last_cnt
+ && (last_cnt < 8)) {
+ memset (tmp, '0', 8);
+ if (copy_from_user(tmp, buf, last_cnt))
+ return -EFAULT;
+ mask = 0xffffffff;
+ for (j = 0; j < 8 - last_cnt; j++)
+ mask <<= 1;
+ *q &= ~mask;
+ *q |= simple_strtoul (tmp, NULL, 16);
+ buf += last_cnt;
+ } else {
+ char tchars[17]; /* XXX yuck... */
+
+ if (copy_from_user(tchars, buf, 16))
+ return -EFAULT;
+ *q = simple_strtoul (tchars, NULL, 16);
+ buf += 9;
+ }
+ }
+ }
+ if (!forcelen) {
+ if (op->len < i)
+ op->len = i;
+ } else
+ op->len = i;
+ *ppos += count;
+ }
+write_try_string:
+ if (!(op->flag & OPP_BINARY)) {
+ if (!(op->flag & (OPP_QUOTED | OPP_NOTQUOTED))) {
+ char ctmp;
+
+ /* No way, if somebody starts writing from the middle,
+ * we don't know whether he uses quotes around or not
+ */
+ if (k > 0)
+ return -EINVAL;
+ if (get_user(ctmp, buf))
+ return -EFAULT;
+ if (ctmp == '\'') {
+ op->flag |= OPP_QUOTED;
+ buf++;
+ count--;
+ (*ppos)++;
+ if (!count) {
+ op->flag |= OPP_STRING;
+ return 1;
+ }
+ } else
+ op->flag |= OPP_NOTQUOTED;
+ }
+ op->flag |= OPP_STRING;
+ if (op->alloclen <= count + *ppos) {
+ b = kmalloc (sizeof (openprom_property)
+ + 2 * (count + *ppos), GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
+ memcpy (b, filp->private_data,
+ sizeof (openprom_property)
+ + strlen (op->name) + op->alloclen);
+ memset (((char *)b) + sizeof (openprom_property)
+ + strlen (op->name) + op->alloclen,
+ 0, 2*(count - *ppos) - op->alloclen);
+ op = (openprom_property *)b;
+ op->alloclen = 2*(count + *ppos);
+ b = filp->private_data;
+ filp->private_data = (void *)op;
+ kfree (b);
+ }
+ p = op->value + *ppos - ((op->flag & OPP_QUOTED) ? 1 : 0);
+ if (copy_from_user(p, buf, count))
+ return -EFAULT;
+ op->flag |= OPP_DIRTY;
+ for (i = 0; i < count; i++, p++)
+ if (*p == '\n') {
+ *p = 0;
+ break;
+ }
+ if (i < count) {
+ op->len = p - op->value;
+ *ppos += i + 1;
+ if ((p > op->value) && (op->flag & OPP_QUOTED)
+ && (*(p - 1) == '\''))
+ op->len--;
+ } else {
+ if (p - op->value > op->len)
+ op->len = p - op->value;
+ *ppos += count;
+ }
+ }
+ return *ppos - k;