Blame view

drivers/parisc/pdc_stable.c 30.2 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
  /* 
   *    Interfaces to retrieve and set PDC Stable options (firmware)
   *
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
4
   *    Copyright (C) 2005-2006 Thibaut VARENE <varenet@parisc-linux.org>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
5
6
   *
   *    This program is free software; you can redistribute it and/or modify
a81dd18eb   Thibaut VARENE   [PARISC] Clarify ...
7
8
   *    it under the terms of the GNU General Public License, version 2, as
   *    published by the Free Software Foundation.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
   *
   *    This program is distributed in the hope that it will be useful,
   *    but WITHOUT ANY WARRANTY; without even the implied warranty of
   *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *    GNU General Public License for more details.
   *
   *    You should have received a copy of the GNU General Public License
   *    along with this program; if not, write to the Free Software
   *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   *
   *
   *    DEV NOTE: the PDC Procedures reference states that:
   *    "A minimum of 96 bytes of Stable Storage is required. Providing more than
   *    96 bytes of Stable Storage is optional [...]. Failure to provide the
   *    optional locations from 96 to 192 results in the loss of certain
   *    functionality during boot."
   *
   *    Since locations between 96 and 192 are the various paths, most (if not
   *    all) PA-RISC machines should have them. Anyway, for safety reasons, the
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
28
   *    following code can deal with just 96 bytes of Stable Storage, and all
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29
30
   *    sizes between 96 and 192 bytes (provided they are multiple of struct
   *    device_path size, eg: 128, 160 and 192) to provide full information.
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
31
32
33
34
35
36
37
38
39
   *    One last word: there's one path we can always count on: the primary path.
   *    Anything above 224 bytes is used for 'osdep2' OS-dependent storage area.
   *
   *    The first OS-dependent area should always be available. Obviously, this is
   *    not true for the other one. Also bear in mind that reading/writing from/to
   *    osdep2 is much more expensive than from/to osdep1.
   *    NOTE: We do not handle the 2 bytes OS-dep area at 0x5D, nor the first
   *    2 bytes of storage available right after OSID. That's a total of 4 bytes
   *    sacrificed: -ETOOLAZY :P
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
40
41
42
43
44
45
46
47
   *
   *    The current policy wrt file permissions is:
   *	- write: root only
   *	- read: (reading triggers PDC calls) ? root only : everyone
   *    The rationale is that PDC calls could hog (DoS) the machine.
   *
   *	TODO:
   *	- timer/fastsize write calls
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
49
50
51
52
53
54
55
56
57
58
   */
  
  #undef PDCS_DEBUG
  #ifdef PDCS_DEBUG
  #define DPRINTK(fmt, args...)	printk(KERN_DEBUG fmt, ## args)
  #else
  #define DPRINTK(fmt, args...)
  #endif
  
  #include <linux/module.h>
  #include <linux/init.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
59
60
  #include <linux/kernel.h>
  #include <linux/string.h>
c59ede7b7   Randy.Dunlap   [PATCH] move capa...
61
  #include <linux/capability.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
63
64
65
66
  #include <linux/ctype.h>
  #include <linux/sysfs.h>
  #include <linux/kobject.h>
  #include <linux/device.h>
  #include <linux/errno.h>
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
67
  #include <linux/spinlock.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
69
70
71
72
  
  #include <asm/pdc.h>
  #include <asm/page.h>
  #include <asm/uaccess.h>
  #include <asm/hardware.h>
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
73
  #define PDCS_VERSION	"0.30"
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
74
  #define PDCS_PREFIX	"PDC Stable Storage"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
76
77
  
  #define PDCS_ADDR_PPRI	0x00
  #define PDCS_ADDR_OSID	0x40
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
78
79
  #define PDCS_ADDR_OSD1	0x48
  #define PDCS_ADDR_DIAG	0x58
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
81
82
83
  #define PDCS_ADDR_FSIZ	0x5C
  #define PDCS_ADDR_PCON	0x60
  #define PDCS_ADDR_PALT	0x80
  #define PDCS_ADDR_PKBD	0xA0
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
84
  #define PDCS_ADDR_OSD2	0xE0
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
86
87
88
89
  
  MODULE_AUTHOR("Thibaut VARENE <varenet@parisc-linux.org>");
  MODULE_DESCRIPTION("sysfs interface to HP PDC Stable Storage data");
  MODULE_LICENSE("GPL");
  MODULE_VERSION(PDCS_VERSION);
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
90
  /* holds Stable Storage size. Initialized once and for all, no lock needed */
8039de10a   Helge Deller   [PARISC] Add __re...
91
  static unsigned long pdcs_size __read_mostly;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92

3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
93
94
  /* holds OS ID. Initialized once and for all, hopefully to 0x0006 */
  static u16 pdcs_osid __read_mostly;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95
96
  /* This struct defines what we need to deal with a parisc pdc path entry */
  struct pdcspath_entry {
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
97
  	rwlock_t rw_lock;		/* to protect path entry access */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
  	short ready;			/* entry record is valid if != 0 */
  	unsigned long addr;		/* entry address in stable storage */
  	char *name;			/* entry name */
  	struct device_path devpath;	/* device path in parisc representation */
  	struct device *dev;		/* corresponding device */
  	struct kobject kobj;
  };
  
  struct pdcspath_attribute {
  	struct attribute attr;
  	ssize_t (*show)(struct pdcspath_entry *entry, char *buf);
  	ssize_t (*store)(struct pdcspath_entry *entry, const char *buf, size_t count);
  };
  
  #define PDCSPATH_ENTRY(_addr, _name) \
  struct pdcspath_entry pdcspath_entry_##_name = { \
  	.ready = 0, \
  	.addr = _addr, \
  	.name = __stringify(_name), \
  };
  
  #define PDCS_ATTR(_name, _mode, _show, _store) \
7f5482178   Greg Kroah-Hartman   kobject: convert ...
120
  struct kobj_attribute pdcs_attr_##_name = { \
7b595756e   Tejun Heo   sysfs: kill unnec...
121
  	.attr = {.name = __stringify(_name), .mode = _mode}, \
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
122
123
124
125
126
127
  	.show = _show, \
  	.store = _store, \
  };
  
  #define PATHS_ATTR(_name, _mode, _show, _store) \
  struct pdcspath_attribute paths_attr_##_name = { \
7b595756e   Tejun Heo   sysfs: kill unnec...
128
  	.attr = {.name = __stringify(_name), .mode = _mode}, \
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
130
131
132
133
134
135
136
137
138
139
140
  	.show = _show, \
  	.store = _store, \
  };
  
  #define to_pdcspath_attribute(_attr) container_of(_attr, struct pdcspath_attribute, attr)
  #define to_pdcspath_entry(obj)  container_of(obj, struct pdcspath_entry, kobj)
  
  /**
   * 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
25985edce   Lucas De Marchi   Fix common misspe...
141
   * you access the files provided by the facilities. We store a copy of the
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142
143
144
   * 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.
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
145
146
   *
   * This function expects to be called with @entry->rw_lock write-hold.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
   */
  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
  ", __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
  ", __func__, entry->dev);
  	
  	return 0;
  }
  
  /**
   * pdcspath_store - This function writes a path to stable storage.
   * @entry: A pointer to an allocated pdcspath_entry.
   * 
   * It can be used in two ways: either by passing it a preset devpath struct
   * containing an already computed hardware path, or by passing it a device
   * pointer, from which it'll find out the corresponding hardware path.
   * For now we do not handle the case where there's an error in writing to the
   * Stable Storage area, so you'd better not mess up the data :P
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
188
189
   *
   * This function expects to be called with @entry->rw_lock write-hold.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
   */
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
191
  static void
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
193
194
  pdcspath_store(struct pdcspath_entry *entry)
  {
  	struct device_path *devpath;
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
195
  	BUG_ON(!entry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196
197
198
199
200
201
202
203
  
  	devpath = &entry->devpath;
  	
  	/* We expect the caller to set the ready flag to 0 if the hardware
  	   path struct provided is invalid, so that we know we have to fill it.
  	   First case, we don't have a preset hwpath... */
  	if (!entry->ready) {
  		/* ...but we have a device, map it */
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
204
205
  		BUG_ON(!entry->dev);
  		device_to_hwpath(entry->dev, (struct hardware_path *)devpath);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
206
207
208
209
210
211
212
213
214
  	}
  	/* else, we expect the provided hwpath to be valid. */
  	
  	DPRINTK("%s: store: 0x%p, 0x%p, addr: 0x%lx
  ", __func__,
  			entry, devpath, entry->addr);
  
  	/* addr, devpath and count must be word aligned */
  	if (pdc_stable_write(entry->addr, devpath, sizeof(*devpath)) != PDC_OK) {
25985edce   Lucas De Marchi   Fix common misspe...
215
216
  		printk(KERN_ERR "%s: an error occurred when writing to PDC.
  "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217
218
219
220
  				"It is likely that the Stable Storage data has been corrupted.
  "
  				"Please check it carefully upon next reboot.
  ", __func__);
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
221
  		WARN_ON(1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
223
  	}
  		
4b991da7f   Thibaut VARENE   [PARISC] pdc_stab...
224
225
  	/* kobject is already registered */
  	entry->ready = 2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
227
228
  	
  	DPRINTK("%s: device: 0x%p
  ", __func__, entry->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
230
231
232
233
234
235
236
237
238
239
240
241
242
  }
  
  /**
   * pdcspath_hwpath_read - This function handles hardware path pretty printing.
   * @entry: An allocated and populated pdscpath_entry struct.
   * @buf: The output buffer to write to.
   * 
   * We will call this function to format the output of the hwpath attribute file.
   */
  static ssize_t
  pdcspath_hwpath_read(struct pdcspath_entry *entry, char *buf)
  {
  	char *out = buf;
  	struct device_path *devpath;
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
243
  	short i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244
245
246
  
  	if (!entry || !buf)
  		return -EINVAL;
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
247
  	read_lock(&entry->rw_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
248
  	devpath = &entry->devpath;
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
249
250
  	i = entry->ready;
  	read_unlock(&entry->rw_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251

c74284222   Thibaut VARENE   [PARISC] pdc_stab...
252
  	if (!i)	/* entry is not ready */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
  		return -ENODATA;
  	
  	for (i = 0; i < 6; i++) {
  		if (devpath->bc[i] >= 128)
  			continue;
  		out += sprintf(out, "%u/", (unsigned char)devpath->bc[i]);
  	}
  	out += sprintf(out, "%u
  ", (unsigned char)devpath->mod);
  	
  	return out - buf;
  }
  
  /**
   * 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.
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
274
   * We make sure that the provided path actually maps to an existing
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
276
277
278
279
280
281
282
283
284
285
286
287
   * 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;
26f032492   Kyle McMartin   [PARISC] Quiet sy...
288
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
  
  	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
  ", __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
  ", __func__, i, hwpath.bc[i]);
  	}
  	
  	/* Store the final field */		
  	hwpath.bc[i] = simple_strtoul(in, NULL, 10);
  	DPRINTK("%s: bc[%d]: %d
  ", __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
  ", __func__, entry->name, buf);
  		return -EINVAL;
  	}
  	
  	/* So far so good, let's get in deep */
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
335
  	write_lock(&entry->rw_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
336
337
338
339
  	entry->ready = 0;
  	entry->dev = dev;
  	
  	/* Now, dive in. Write back to the hardware */
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
340
  	pdcspath_store(entry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
341
342
343
  	
  	/* Update the symlink to the real device */
  	sysfs_remove_link(&entry->kobj, "device");
26f032492   Kyle McMartin   [PARISC] Quiet sy...
344
345
  	ret = sysfs_create_link(&entry->kobj, &entry->dev->kobj, "device");
  	WARN_ON(ret);
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
346
  	write_unlock(&entry->rw_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
347
  	
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
348
349
  	printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" path to \"%s\"
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
  		entry->name, buf);
  	
  	return count;
  }
  
  /**
   * pdcspath_layer_read - Extended layer (eg. SCSI ids) pretty printing.
   * @entry: An allocated and populated pdscpath_entry struct.
   * @buf: The output buffer to write to.
   * 
   * We will call this function to format the output of the layer attribute file.
   */
  static ssize_t
  pdcspath_layer_read(struct pdcspath_entry *entry, char *buf)
  {
  	char *out = buf;
  	struct device_path *devpath;
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
367
  	short i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
369
370
371
  
  	if (!entry || !buf)
  		return -EINVAL;
  	
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
372
  	read_lock(&entry->rw_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
373
  	devpath = &entry->devpath;
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
374
375
  	i = entry->ready;
  	read_unlock(&entry->rw_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
376

c74284222   Thibaut VARENE   [PARISC] pdc_stab...
377
  	if (!i)	/* entry is not ready */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
378
379
  		return -ENODATA;
  	
447c233da   Roel Kluin   parisc: Fix read ...
380
  	for (i = 0; i < 6 && devpath->layers[i]; i++)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
  		out += sprintf(out, "%u ", devpath->layers[i]);
  
  	out += sprintf(out, "
  ");
  	
  	return out - buf;
  }
  
  /**
   * 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[count+1], *temp;
  
  	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. 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
  ", __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
  ", __func__, i, layers[i]);
  	}
  		
  	/* So far so good, let's get in deep */
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
435
  	write_lock(&entry->rw_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
436
437
438
439
440
441
  	
  	/* 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 */
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
442
443
  	pdcspath_store(entry);
  	write_unlock(&entry->rw_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
444
  	
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
445
446
  	printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" layers to \"%s\"
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
  		entry->name, buf);
  	
  	return count;
  }
  
  /**
   * pdcspath_attr_show - Generic read function call wrapper.
   * @kobj: The kobject to get info from.
   * @attr: The attribute looked upon.
   * @buf: The output buffer.
   */
  static ssize_t
  pdcspath_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
  {
  	struct pdcspath_entry *entry = to_pdcspath_entry(kobj);
  	struct pdcspath_attribute *pdcs_attr = to_pdcspath_attribute(attr);
  	ssize_t ret = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
  	if (pdcs_attr->show)
  		ret = pdcs_attr->show(entry, buf);
  
  	return ret;
  }
  
  /**
   * pdcspath_attr_store - Generic write function call wrapper.
   * @kobj: The kobject to write info to.
   * @attr: The attribute to be modified.
   * @buf: The input buffer.
   * @count: The size of the buffer.
   */
  static ssize_t
  pdcspath_attr_store(struct kobject *kobj, struct attribute *attr,
  			const char *buf, size_t count)
  {
  	struct pdcspath_entry *entry = to_pdcspath_entry(kobj);
  	struct pdcspath_attribute *pdcs_attr = to_pdcspath_attribute(attr);
  	ssize_t ret = 0;
  
  	if (!capable(CAP_SYS_ADMIN))
  		return -EACCES;
  
  	if (pdcs_attr->store)
  		ret = pdcs_attr->store(entry, buf, count);
  
  	return ret;
  }
52cf25d0a   Emese Revfy   Driver core: Cons...
493
  static const struct sysfs_ops pdcspath_attr_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
494
495
496
497
498
  	.show = pdcspath_attr_show,
  	.store = pdcspath_attr_store,
  };
  
  /* These are the two attributes of any PDC path. */
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
499
500
  static PATHS_ATTR(hwpath, 0644, pdcspath_hwpath_read, pdcspath_hwpath_write);
  static PATHS_ATTR(layer, 0644, pdcspath_layer_read, pdcspath_layer_write);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
  
  static struct attribute *paths_subsys_attrs[] = {
  	&paths_attr_hwpath.attr,
  	&paths_attr_layer.attr,
  	NULL,
  };
  
  /* Specific kobject type for our PDC paths */
  static struct kobj_type ktype_pdcspath = {
  	.sysfs_ops = &pdcspath_attr_ops,
  	.default_attrs = paths_subsys_attrs,
  };
  
  /* We hard define the 4 types of path we expect to find */
  static PDCSPATH_ENTRY(PDCS_ADDR_PPRI, primary);
  static PDCSPATH_ENTRY(PDCS_ADDR_PCON, console);
  static PDCSPATH_ENTRY(PDCS_ADDR_PALT, alternative);
  static PDCSPATH_ENTRY(PDCS_ADDR_PKBD, keyboard);
  
  /* An array containing all PDC paths we will deal with */
  static struct pdcspath_entry *pdcspath_entries[] = {
  	&pdcspath_entry_primary,
  	&pdcspath_entry_alternative,
  	&pdcspath_entry_console,
  	&pdcspath_entry_keyboard,
  	NULL,
  };
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
528
529
530
  
  /* For more insight of what's going on here, refer to PDC Procedures doc,
   * Section PDC_STABLE */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
531
  /**
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
532
   * pdcs_size_read - Stable Storage size output.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
533
   * @buf: The output buffer to write to.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
534
   */
7f5482178   Greg Kroah-Hartman   kobject: convert ...
535
536
537
  static ssize_t pdcs_size_read(struct kobject *kobj,
  			      struct kobj_attribute *attr,
  			      char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
538
539
  {
  	char *out = buf;
823bccfc4   Greg Kroah-Hartman   remove "struct su...
540

7f5482178   Greg Kroah-Hartman   kobject: convert ...
541
  	if (!buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
542
  		return -EINVAL;
823bccfc4   Greg Kroah-Hartman   remove "struct su...
543

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
544
  	/* show the size of the stable storage */
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
545
546
  	out += sprintf(out, "%ld
  ", pdcs_size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
547

c74284222   Thibaut VARENE   [PARISC] pdc_stab...
548
549
550
551
552
  	return out - buf;
  }
  
  /**
   * pdcs_auto_read - Stable Storage autoboot/search flag output.
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
553
554
555
   * @buf: The output buffer to write to.
   * @knob: The PF_AUTOBOOT or PF_AUTOSEARCH flag
   */
7f5482178   Greg Kroah-Hartman   kobject: convert ...
556
557
558
  static ssize_t pdcs_auto_read(struct kobject *kobj,
  			      struct kobj_attribute *attr,
  			      char *buf, int knob)
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
559
560
561
  {
  	char *out = buf;
  	struct pdcspath_entry *pathentry;
67a5a59d3   Helge Deller   [PARISC] Misc. ja...
562

7f5482178   Greg Kroah-Hartman   kobject: convert ...
563
  	if (!buf)
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
  		return -EINVAL;
  
  	/* Current flags are stored in primary boot path entry */
  	pathentry = &pdcspath_entry_primary;
  
  	read_lock(&pathentry->rw_lock);
  	out += sprintf(out, "%s
  ", (pathentry->devpath.flags & knob) ?
  					"On" : "Off");
  	read_unlock(&pathentry->rw_lock);
  
  	return out - buf;
  }
  
  /**
   * pdcs_autoboot_read - Stable Storage autoboot flag output.
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
580
581
   * @buf: The output buffer to write to.
   */
7f5482178   Greg Kroah-Hartman   kobject: convert ...
582
583
  static ssize_t pdcs_autoboot_read(struct kobject *kobj,
  				  struct kobj_attribute *attr, char *buf)
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
584
  {
7f5482178   Greg Kroah-Hartman   kobject: convert ...
585
  	return pdcs_auto_read(kobj, attr, buf, PF_AUTOBOOT);
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
586
587
588
589
  }
  
  /**
   * pdcs_autosearch_read - Stable Storage autoboot flag output.
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
590
591
   * @buf: The output buffer to write to.
   */
7f5482178   Greg Kroah-Hartman   kobject: convert ...
592
593
  static ssize_t pdcs_autosearch_read(struct kobject *kobj,
  				    struct kobj_attribute *attr, char *buf)
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
594
  {
7f5482178   Greg Kroah-Hartman   kobject: convert ...
595
  	return pdcs_auto_read(kobj, attr, buf, PF_AUTOSEARCH);
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
596
597
598
599
  }
  
  /**
   * pdcs_timer_read - Stable Storage timer count output (in seconds).
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
600
601
602
603
   * @buf: The output buffer to write to.
   *
   * The value of the timer field correponds to a number of seconds in powers of 2.
   */
7f5482178   Greg Kroah-Hartman   kobject: convert ...
604
605
  static ssize_t pdcs_timer_read(struct kobject *kobj,
  			       struct kobj_attribute *attr, char *buf)
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
606
607
608
  {
  	char *out = buf;
  	struct pdcspath_entry *pathentry;
7f5482178   Greg Kroah-Hartman   kobject: convert ...
609
  	if (!buf)
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
  		return -EINVAL;
  
  	/* Current flags are stored in primary boot path entry */
  	pathentry = &pdcspath_entry_primary;
  
  	/* print the timer value in seconds */
  	read_lock(&pathentry->rw_lock);
  	out += sprintf(out, "%u
  ", (pathentry->devpath.flags & PF_TIMER) ?
  				(1 << (pathentry->devpath.flags & PF_TIMER)) : 0);
  	read_unlock(&pathentry->rw_lock);
  
  	return out - buf;
  }
  
  /**
   * pdcs_osid_read - Stable Storage OS ID register output.
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
627
628
   * @buf: The output buffer to write to.
   */
7f5482178   Greg Kroah-Hartman   kobject: convert ...
629
630
  static ssize_t pdcs_osid_read(struct kobject *kobj,
  			      struct kobj_attribute *attr, char *buf)
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
631
632
  {
  	char *out = buf;
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
633

7f5482178   Greg Kroah-Hartman   kobject: convert ...
634
  	if (!buf)
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
635
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
636

67a061a19   Kyle McMartin   [PARISC] Add os_i...
637
638
639
  	out += sprintf(out, "%s dependent data (0x%.4x)
  ",
  		os_id_to_string(pdcs_osid), pdcs_osid);
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
640
641
642
643
644
645
  
  	return out - buf;
  }
  
  /**
   * pdcs_osdep1_read - Stable Storage OS-Dependent data area 1 output.
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
646
647
648
649
   * @buf: The output buffer to write to.
   *
   * This can hold 16 bytes of OS-Dependent data.
   */
7f5482178   Greg Kroah-Hartman   kobject: convert ...
650
651
  static ssize_t pdcs_osdep1_read(struct kobject *kobj,
  				struct kobj_attribute *attr, char *buf)
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
652
653
654
  {
  	char *out = buf;
  	u32 result[4];
7f5482178   Greg Kroah-Hartman   kobject: convert ...
655
  	if (!buf)
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
  		return -EINVAL;
  
  	if (pdc_stable_read(PDCS_ADDR_OSD1, &result, sizeof(result)) != PDC_OK)
  		return -EIO;
  
  	out += sprintf(out, "0x%.8x
  ", result[0]);
  	out += sprintf(out, "0x%.8x
  ", result[1]);
  	out += sprintf(out, "0x%.8x
  ", result[2]);
  	out += sprintf(out, "0x%.8x
  ", result[3]);
  
  	return out - buf;
  }
  
  /**
   * pdcs_diagnostic_read - Stable Storage Diagnostic register output.
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
675
676
677
678
   * @buf: The output buffer to write to.
   *
   * I have NFC how to interpret the content of that register ;-).
   */
7f5482178   Greg Kroah-Hartman   kobject: convert ...
679
680
  static ssize_t pdcs_diagnostic_read(struct kobject *kobj,
  				    struct kobj_attribute *attr, char *buf)
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
681
682
683
  {
  	char *out = buf;
  	u32 result;
7f5482178   Greg Kroah-Hartman   kobject: convert ...
684
  	if (!buf)
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
685
686
687
688
689
690
691
692
  		return -EINVAL;
  
  	/* get diagnostic */
  	if (pdc_stable_read(PDCS_ADDR_DIAG, &result, sizeof(result)) != PDC_OK)
  		return -EIO;
  
  	out += sprintf(out, "0x%.4x
  ", (result >> 16));
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
693
694
695
696
697
698
  
  	return out - buf;
  }
  
  /**
   * pdcs_fastsize_read - Stable Storage FastSize register output.
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
699
700
701
702
   * @buf: The output buffer to write to.
   *
   * This register holds the amount of system RAM to be tested during boot sequence.
   */
7f5482178   Greg Kroah-Hartman   kobject: convert ...
703
704
  static ssize_t pdcs_fastsize_read(struct kobject *kobj,
  				  struct kobj_attribute *attr, char *buf)
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
705
706
  {
  	char *out = buf;
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
707
  	u32 result;
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
708

7f5482178   Greg Kroah-Hartman   kobject: convert ...
709
  	if (!buf)
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
710
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
711
712
713
714
  
  	/* get fast-size */
  	if (pdc_stable_read(PDCS_ADDR_FSIZ, &result, sizeof(result)) != PDC_OK)
  		return -EIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
715
  	if ((result & 0x0F) < 0x0E)
abff75439   Randolph Chung   [PARISC] Avoid us...
716
  		out += sprintf(out, "%d kB", (1<<(result & 0x0F))*256);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
717
718
719
720
721
722
723
724
725
  	else
  		out += sprintf(out, "All");
  	out += sprintf(out, "
  ");
  	
  	return out - buf;
  }
  
  /**
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
726
   * pdcs_osdep2_read - Stable Storage OS-Dependent data area 2 output.
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
727
728
729
730
   * @buf: The output buffer to write to.
   *
   * This can hold pdcs_size - 224 bytes of OS-Dependent data, when available.
   */
7f5482178   Greg Kroah-Hartman   kobject: convert ...
731
732
  static ssize_t pdcs_osdep2_read(struct kobject *kobj,
  				struct kobj_attribute *attr, char *buf)
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
733
734
735
736
737
738
739
740
741
742
  {
  	char *out = buf;
  	unsigned long size;
  	unsigned short i;
  	u32 result;
  
  	if (unlikely(pdcs_size <= 224))
  		return -ENODATA;
  
  	size = pdcs_size - 224;
7f5482178   Greg Kroah-Hartman   kobject: convert ...
743
  	if (!buf)
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
744
745
746
747
748
749
750
751
752
753
754
755
756
757
  		return -EINVAL;
  
  	for (i=0; i<size; i+=4) {
  		if (unlikely(pdc_stable_read(PDCS_ADDR_OSD2 + i, &result,
  					sizeof(result)) != PDC_OK))
  			return -EIO;
  		out += sprintf(out, "0x%.8x
  ", result);
  	}
  
  	return out - buf;
  }
  
  /**
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
758
   * pdcs_auto_write - This function handles autoboot/search flag modifying.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
759
760
   * @buf: The input buffer to read from.
   * @count: The number of bytes to be read.
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
761
   * @knob: The PF_AUTOBOOT or PF_AUTOSEARCH flag
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
762
   * 
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
763
   * We will call this function to change the current autoboot flag.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
764
   * We expect a precise syntax:
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
765
   *	\"n\" (n == 0 or 1) to toggle AutoBoot Off or On
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
766
   */
7f5482178   Greg Kroah-Hartman   kobject: convert ...
767
768
769
  static ssize_t pdcs_auto_write(struct kobject *kobj,
  			       struct kobj_attribute *attr, const char *buf,
  			       size_t count, int knob)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
770
771
772
773
774
775
776
777
  {
  	struct pdcspath_entry *pathentry;
  	unsigned char flags;
  	char in[count+1], *temp;
  	char c;
  
  	if (!capable(CAP_SYS_ADMIN))
  		return -EACCES;
7f5482178   Greg Kroah-Hartman   kobject: convert ...
778
  	if (!buf || !count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
779
780
781
782
783
784
785
786
787
788
  		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 */
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
789
  	read_lock(&pathentry->rw_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
790
  	flags = pathentry->devpath.flags;
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
791
  	read_unlock(&pathentry->rw_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
792
793
794
  	
  	DPRINTK("%s: flags before: 0x%X
  ", __func__, flags);
e7d2860b6   André Goddard Rosa   tree-wide: conver...
795
796
  
  	temp = skip_spaces(in);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
797
798
799
800
  	c = *temp++ - '0';
  	if ((c != 0) && (c != 1))
  		goto parse_error;
  	if (c == 0)
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
801
  		flags &= ~knob;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
802
  	else
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
803
  		flags |= knob;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
804
805
806
807
808
  	
  	DPRINTK("%s: flags after: 0x%X
  ", __func__, flags);
  		
  	/* So far so good, let's get in deep */
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
809
  	write_lock(&pathentry->rw_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
810
811
812
813
814
  	
  	/* Change the path entry flags first */
  	pathentry->devpath.flags = flags;
  		
  	/* Now, dive in. Write back to the hardware */
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
815
816
  	pdcspath_store(pathentry);
  	write_unlock(&pathentry->rw_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
817
  	
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
818
819
820
821
  	printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" to \"%s\"
  ",
  		(knob & PF_AUTOBOOT) ? "autoboot" : "autosearch",
  		(flags & knob) ? "On" : "Off");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
822
823
824
825
  	
  	return count;
  
  parse_error:
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
826
827
  	printk(KERN_WARNING "%s: Parse error: expect \"n\" (n == 0 or 1)
  ", __func__);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
828
829
  	return -EINVAL;
  }
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
830
831
  /**
   * pdcs_autoboot_write - This function handles autoboot flag modifying.
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
832
833
834
835
836
837
838
   * @buf: The input buffer to read from.
   * @count: The number of bytes to be read.
   *
   * We will call this function to change the current boot flags.
   * We expect a precise syntax:
   *	\"n\" (n == 0 or 1) to toggle AutoSearch Off or On
   */
7f5482178   Greg Kroah-Hartman   kobject: convert ...
839
840
841
  static ssize_t pdcs_autoboot_write(struct kobject *kobj,
  				   struct kobj_attribute *attr,
  				   const char *buf, size_t count)
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
842
  {
ff451d705   Joel Soete   [PARISC] pdc_stab...
843
  	return pdcs_auto_write(kobj, attr, buf, count, PF_AUTOBOOT);
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
844
845
846
847
  }
  
  /**
   * pdcs_autosearch_write - This function handles autosearch flag modifying.
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
848
849
850
851
852
853
854
   * @buf: The input buffer to read from.
   * @count: The number of bytes to be read.
   *
   * We will call this function to change the current boot flags.
   * We expect a precise syntax:
   *	\"n\" (n == 0 or 1) to toggle AutoSearch Off or On
   */
7f5482178   Greg Kroah-Hartman   kobject: convert ...
855
856
857
  static ssize_t pdcs_autosearch_write(struct kobject *kobj,
  				     struct kobj_attribute *attr,
  				     const char *buf, size_t count)
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
858
  {
ff451d705   Joel Soete   [PARISC] pdc_stab...
859
  	return pdcs_auto_write(kobj, attr, buf, count, PF_AUTOSEARCH);
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
860
  }
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
861
862
  /**
   * pdcs_osdep1_write - Stable Storage OS-Dependent data area 1 input.
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
863
864
865
866
867
868
869
   * @buf: The input buffer to read from.
   * @count: The number of bytes to be read.
   *
   * This can store 16 bytes of OS-Dependent data. We use a byte-by-byte
   * write approach. It's up to userspace to deal with it when constructing
   * its input buffer.
   */
7f5482178   Greg Kroah-Hartman   kobject: convert ...
870
871
872
  static ssize_t pdcs_osdep1_write(struct kobject *kobj,
  				 struct kobj_attribute *attr,
  				 const char *buf, size_t count)
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
873
874
875
876
877
  {
  	u8 in[16];
  
  	if (!capable(CAP_SYS_ADMIN))
  		return -EACCES;
7f5482178   Greg Kroah-Hartman   kobject: convert ...
878
  	if (!buf || !count)
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
879
  		return -EINVAL;
ec1fdc24c   Kyle McMartin   [PARISC] OS_ID_LI...
880
  	if (unlikely(pdcs_osid != OS_ID_LINUX))
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
  		return -EPERM;
  
  	if (count > 16)
  		return -EMSGSIZE;
  
  	/* We'll use a local copy of buf */
  	memset(in, 0, 16);
  	memcpy(in, buf, count);
  
  	if (pdc_stable_write(PDCS_ADDR_OSD1, &in, sizeof(in)) != PDC_OK)
  		return -EIO;
  
  	return count;
  }
  
  /**
   * pdcs_osdep2_write - Stable Storage OS-Dependent data area 2 input.
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
898
899
900
901
902
903
904
   * @buf: The input buffer to read from.
   * @count: The number of bytes to be read.
   *
   * This can store pdcs_size - 224 bytes of OS-Dependent data. We use a
   * byte-by-byte write approach. It's up to userspace to deal with it when
   * constructing its input buffer.
   */
7f5482178   Greg Kroah-Hartman   kobject: convert ...
905
906
907
  static ssize_t pdcs_osdep2_write(struct kobject *kobj,
  				 struct kobj_attribute *attr,
  				 const char *buf, size_t count)
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
908
909
910
911
912
913
914
  {
  	unsigned long size;
  	unsigned short i;
  	u8 in[4];
  
  	if (!capable(CAP_SYS_ADMIN))
  		return -EACCES;
7f5482178   Greg Kroah-Hartman   kobject: convert ...
915
  	if (!buf || !count)
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
916
917
918
919
  		return -EINVAL;
  
  	if (unlikely(pdcs_size <= 224))
  		return -ENOSYS;
ec1fdc24c   Kyle McMartin   [PARISC] OS_ID_LI...
920
  	if (unlikely(pdcs_osid != OS_ID_LINUX))
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
  		return -EPERM;
  
  	size = pdcs_size - 224;
  
  	if (count > size)
  		return -EMSGSIZE;
  
  	/* We'll use a local copy of buf */
  
  	for (i=0; i<count; i+=4) {
  		memset(in, 0, 4);
  		memcpy(in, buf+i, (count-i < 4) ? count-i : 4);
  		if (unlikely(pdc_stable_write(PDCS_ADDR_OSD2 + i, &in,
  					sizeof(in)) != PDC_OK))
  			return -EIO;
  	}
  
  	return count;
  }
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
940
941
942
943
944
  /* The remaining attributes. */
  static PDCS_ATTR(size, 0444, pdcs_size_read, NULL);
  static PDCS_ATTR(autoboot, 0644, pdcs_autoboot_read, pdcs_autoboot_write);
  static PDCS_ATTR(autosearch, 0644, pdcs_autosearch_read, pdcs_autosearch_write);
  static PDCS_ATTR(timer, 0444, pdcs_timer_read, NULL);
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
945
946
947
  static PDCS_ATTR(osid, 0444, pdcs_osid_read, NULL);
  static PDCS_ATTR(osdep1, 0600, pdcs_osdep1_read, pdcs_osdep1_write);
  static PDCS_ATTR(diagnostic, 0400, pdcs_diagnostic_read, NULL);
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
948
  static PDCS_ATTR(fastsize, 0400, pdcs_fastsize_read, NULL);
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
949
  static PDCS_ATTR(osdep2, 0600, pdcs_osdep2_read, pdcs_osdep2_write);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
950

7f5482178   Greg Kroah-Hartman   kobject: convert ...
951
952
953
954
955
956
957
958
959
960
  static struct attribute *pdcs_subsys_attrs[] = {
  	&pdcs_attr_size.attr,
  	&pdcs_attr_autoboot.attr,
  	&pdcs_attr_autosearch.attr,
  	&pdcs_attr_timer.attr,
  	&pdcs_attr_osid.attr,
  	&pdcs_attr_osdep1.attr,
  	&pdcs_attr_diagnostic.attr,
  	&pdcs_attr_fastsize.attr,
  	&pdcs_attr_osdep2.attr,
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
961
  	NULL,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
962
  };
7f5482178   Greg Kroah-Hartman   kobject: convert ...
963
964
965
  static struct attribute_group pdcs_attr_group = {
  	.attrs = pdcs_subsys_attrs,
  };
c829a5b49   Greg Kroah-Hartman   kobject: convert ...
966
  static struct kobject *stable_kobj;
4443d07fc   Greg Kroah-Hartman   kset: convert par...
967
  static struct kset *paths_kset;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
  
  /**
   * pdcs_register_pathentries - Prepares path entries kobjects for sysfs usage.
   * 
   * It creates kobjects corresponding to each path entry with nice sysfs
   * links to the real device. This is where the magic takes place: when
   * registering the subsystem attributes during module init, each kobject hereby
   * created will show in the sysfs tree as a folder containing files as defined
   * by path_subsys_attr[].
   */
  static inline int __init
  pdcs_register_pathentries(void)
  {
  	unsigned short i;
  	struct pdcspath_entry *entry;
4b991da7f   Thibaut VARENE   [PARISC] pdc_stab...
983
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
984
  	
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
985
986
987
  	/* Initialize the entries rw_lock before anything else */
  	for (i = 0; (entry = pdcspath_entries[i]); i++)
  		rwlock_init(&entry->rw_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
988
  	for (i = 0; (entry = pdcspath_entries[i]); i++) {
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
989
990
991
992
993
  		write_lock(&entry->rw_lock);
  		err = pdcspath_fetch(entry);
  		write_unlock(&entry->rw_lock);
  
  		if (err < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
994
  			continue;
4443d07fc   Greg Kroah-Hartman   kset: convert par...
995
  		entry->kobj.kset = paths_kset;
73f368cf6   Greg Kroah-Hartman   Kobject: change d...
996
997
998
  		err = kobject_init_and_add(&entry->kobj, &ktype_pdcspath, NULL,
  					   "%s", entry->name);
  		if (err)
4b991da7f   Thibaut VARENE   [PARISC] pdc_stab...
999
  			return err;
73f368cf6   Greg Kroah-Hartman   Kobject: change d...
1000

4b991da7f   Thibaut VARENE   [PARISC] pdc_stab...
1001
  		/* kobject is now registered */
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
1002
  		write_lock(&entry->rw_lock);
4b991da7f   Thibaut VARENE   [PARISC] pdc_stab...
1003
1004
  		entry->ready = 2;
  		
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1005
  		/* Add a nice symlink to the real device */
26f032492   Kyle McMartin   [PARISC] Quiet sy...
1006
1007
1008
1009
  		if (entry->dev) {
  			err = sysfs_create_link(&entry->kobj, &entry->dev->kobj, "device");
  			WARN_ON(err);
  		}
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
1010
1011
  
  		write_unlock(&entry->rw_lock);
73f368cf6   Greg Kroah-Hartman   Kobject: change d...
1012
  		kobject_uevent(&entry->kobj, KOBJ_ADD);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1013
1014
1015
1016
1017
1018
1019
1020
  	}
  	
  	return 0;
  }
  
  /**
   * pdcs_unregister_pathentries - Routine called when unregistering the module.
   */
4b991da7f   Thibaut VARENE   [PARISC] pdc_stab...
1021
  static inline void
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1022
1023
1024
1025
1026
  pdcs_unregister_pathentries(void)
  {
  	unsigned short i;
  	struct pdcspath_entry *entry;
  	
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
1027
1028
  	for (i = 0; (entry = pdcspath_entries[i]); i++) {
  		read_lock(&entry->rw_lock);
4b991da7f   Thibaut VARENE   [PARISC] pdc_stab...
1029
  		if (entry->ready >= 2)
c10997f65   Greg Kroah-Hartman   Kobject: convert ...
1030
  			kobject_put(&entry->kobj);
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
1031
1032
  		read_unlock(&entry->rw_lock);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1033
1034
1035
  }
  
  /*
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
1036
1037
   * For now we register the stable subsystem with the firmware subsystem
   * and the paths subsystem with the stable subsystem
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1038
1039
1040
1041
   */
  static int __init
  pdc_stable_init(void)
  {
7f5482178   Greg Kroah-Hartman   kobject: convert ...
1042
  	int rc = 0, error = 0;
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
1043
  	u32 result;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1044
1045
1046
1047
  
  	/* find the size of the stable storage */
  	if (pdc_stable_get_size(&pdcs_size) != PDC_OK) 
  		return -ENODEV;
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
1048
1049
1050
1051
1052
1053
  	/* make sure we have enough data */
  	if (pdcs_size < 96)
  		return -ENODATA;
  
  	printk(KERN_INFO PDCS_PREFIX " facility v%s
  ", PDCS_VERSION);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1054

3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
1055
1056
1057
1058
1059
1060
  	/* get OSID */
  	if (pdc_stable_read(PDCS_ADDR_OSID, &result, sizeof(result)) != PDC_OK)
  		return -EIO;
  
  	/* the actual result is 16 bits away */
  	pdcs_osid = (u16)(result >> 16);
c829a5b49   Greg Kroah-Hartman   kobject: convert ...
1061
1062
1063
  	/* For now we'll register the directory at /sys/firmware/stable */
  	stable_kobj = kobject_create_and_add("stable", firmware_kobj);
  	if (!stable_kobj) {
4443d07fc   Greg Kroah-Hartman   kset: convert par...
1064
  		rc = -ENOMEM;
4b991da7f   Thibaut VARENE   [PARISC] pdc_stab...
1065
  		goto fail_firmreg;
4443d07fc   Greg Kroah-Hartman   kset: convert par...
1066
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1067

c74284222   Thibaut VARENE   [PARISC] pdc_stab...
1068
  	/* Don't forget the root entries */
ff451d705   Joel Soete   [PARISC] pdc_stab...
1069
  	error = sysfs_create_group(stable_kobj, &pdcs_attr_group);
7f5482178   Greg Kroah-Hartman   kobject: convert ...
1070

4443d07fc   Greg Kroah-Hartman   kset: convert par...
1071
  	/* register the paths kset as a child of the stable kset */
c829a5b49   Greg Kroah-Hartman   kobject: convert ...
1072
  	paths_kset = kset_create_and_add("paths", NULL, stable_kobj);
4443d07fc   Greg Kroah-Hartman   kset: convert par...
1073
1074
1075
1076
  	if (!paths_kset) {
  		rc = -ENOMEM;
  		goto fail_ksetreg;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1077

4443d07fc   Greg Kroah-Hartman   kset: convert par...
1078
  	/* now we create all "files" for the paths kset */
4b991da7f   Thibaut VARENE   [PARISC] pdc_stab...
1079
1080
1081
1082
  	if ((rc = pdcs_register_pathentries()))
  		goto fail_pdcsreg;
  
  	return rc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1083
  	
4b991da7f   Thibaut VARENE   [PARISC] pdc_stab...
1084
1085
  fail_pdcsreg:
  	pdcs_unregister_pathentries();
4443d07fc   Greg Kroah-Hartman   kset: convert par...
1086
  	kset_unregister(paths_kset);
4b991da7f   Thibaut VARENE   [PARISC] pdc_stab...
1087
  	
4443d07fc   Greg Kroah-Hartman   kset: convert par...
1088
  fail_ksetreg:
c10997f65   Greg Kroah-Hartman   Kobject: convert ...
1089
  	kobject_put(stable_kobj);
4b991da7f   Thibaut VARENE   [PARISC] pdc_stab...
1090
1091
  	
  fail_firmreg:
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
1092
1093
  	printk(KERN_INFO PDCS_PREFIX " bailing out
  ");
4b991da7f   Thibaut VARENE   [PARISC] pdc_stab...
1094
  	return rc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1095
1096
1097
1098
1099
1100
  }
  
  static void __exit
  pdc_stable_exit(void)
  {
  	pdcs_unregister_pathentries();
4443d07fc   Greg Kroah-Hartman   kset: convert par...
1101
  	kset_unregister(paths_kset);
c10997f65   Greg Kroah-Hartman   Kobject: convert ...
1102
  	kobject_put(stable_kobj);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1103
1104
1105
1106
1107
  }
  
  
  module_init(pdc_stable_init);
  module_exit(pdc_stable_exit);