Blame view

drivers/parisc/pdc_stable.c 29.7 KB
450515395   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
  /* 
   *    Interfaces to retrieve and set PDC Stable options (firmware)
   *
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
5
   *    Copyright (C) 2005-2006 Thibaut VARENE <varenet@parisc-linux.org>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
6
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
7
8
9
10
11
12
13
14
   *    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...
15
   *    following code can deal with just 96 bytes of Stable Storage, and all
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
17
   *    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...
18
19
20
21
22
23
24
25
26
   *    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...
27
28
29
30
31
32
33
34
   *
   *    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
35
36
37
38
39
40
41
42
43
44
45
   */
  
  #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
46
47
  #include <linux/kernel.h>
  #include <linux/string.h>
c59ede7b7   Randy.Dunlap   [PATCH] move capa...
48
  #include <linux/capability.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
49
50
51
52
53
  #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...
54
  #include <linux/spinlock.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
55
56
57
  
  #include <asm/pdc.h>
  #include <asm/page.h>
7c0f6ba68   Linus Torvalds   Replace <asm/uacc...
58
  #include <linux/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
59
  #include <asm/hardware.h>
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
60
  #define PDCS_VERSION	"0.30"
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
61
  #define PDCS_PREFIX	"PDC Stable Storage"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
63
64
  
  #define PDCS_ADDR_PPRI	0x00
  #define PDCS_ADDR_OSID	0x40
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
65
66
  #define PDCS_ADDR_OSD1	0x48
  #define PDCS_ADDR_DIAG	0x58
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67
68
69
70
  #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...
71
  #define PDCS_ADDR_OSD2	0xE0
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
73
74
75
76
  
  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...
77
  /* holds Stable Storage size. Initialized once and for all, no lock needed */
8039de10a   Helge Deller   [PARISC] Add __re...
78
  static unsigned long pdcs_size __read_mostly;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79

3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
80
81
  /* 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
82
83
  /* This struct defines what we need to deal with a parisc pdc path entry */
  struct pdcspath_entry {
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
84
  	rwlock_t rw_lock;		/* to protect path entry access */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
  	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 ...
107
  struct kobj_attribute pdcs_attr_##_name = { \
7b595756e   Tejun Heo   sysfs: kill unnec...
108
  	.attr = {.name = __stringify(_name), .mode = _mode}, \
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
110
111
112
113
114
  	.show = _show, \
  	.store = _store, \
  };
  
  #define PATHS_ATTR(_name, _mode, _show, _store) \
  struct pdcspath_attribute paths_attr_##_name = { \
7b595756e   Tejun Heo   sysfs: kill unnec...
115
  	.attr = {.name = __stringify(_name), .mode = _mode}, \
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
117
118
119
120
121
122
123
124
125
126
127
  	.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...
128
   * you access the files provided by the facilities. We store a copy of the
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
130
131
   * 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...
132
133
   *
   * This function expects to be called with @entry->rw_lock write-hold.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134
135
136
137
138
139
140
141
142
143
144
145
146
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
   */
  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...
175
176
   *
   * This function expects to be called with @entry->rw_lock write-hold.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177
   */
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
178
  static void
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
179
180
181
  pdcspath_store(struct pdcspath_entry *entry)
  {
  	struct device_path *devpath;
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
182
  	BUG_ON(!entry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
184
185
186
187
188
189
190
  
  	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...
191
192
  		BUG_ON(!entry->dev);
  		device_to_hwpath(entry->dev, (struct hardware_path *)devpath);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
194
195
196
197
198
199
200
  	}
  	/* 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 */
93c3e913e   Julia Lawall   drivers/parisc/pd...
201
202
203
  	if (pdc_stable_write(entry->addr, devpath, sizeof(*devpath)) != PDC_OK)
  		WARN(1, KERN_ERR "%s: an error occurred when writing to PDC.
  "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
204
205
206
207
  				"It is likely that the Stable Storage data has been corrupted.
  "
  				"Please check it carefully upon next reboot.
  ", __func__);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208
  		
4b991da7f   Thibaut VARENE   [PARISC] pdc_stab...
209
210
  	/* kobject is already registered */
  	entry->ready = 2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
212
213
  	
  	DPRINTK("%s: device: 0x%p
  ", __func__, entry->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
215
216
217
218
219
220
221
222
223
224
225
226
227
  }
  
  /**
   * 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...
228
  	short i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
230
231
  
  	if (!entry || !buf)
  		return -EINVAL;
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
232
  	read_lock(&entry->rw_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
233
  	devpath = &entry->devpath;
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
234
235
  	i = entry->ready;
  	read_unlock(&entry->rw_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236

c74284222   Thibaut VARENE   [PARISC] pdc_stab...
237
  	if (!i)	/* entry is not ready */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
  		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...
259
   * We make sure that the provided path actually maps to an existing
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
261
262
263
264
265
266
267
268
269
270
   * 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;
c735483de   Helge Deller   parisc: pdc_stabl...
271
  	char in[64], *temp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
272
  	struct device *dev;
26f032492   Kyle McMartin   [PARISC] Quiet sy...
273
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
274
275
276
277
278
  
  	if (!entry || !buf || !count)
  		return -EINVAL;
  
  	/* We'll use a local copy of buf */
c735483de   Helge Deller   parisc: pdc_stabl...
279
  	count = min_t(size_t, count, sizeof(in)-1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
  	strncpy(in, buf, count);
c735483de   Helge Deller   parisc: pdc_stabl...
281
  	in[count] = '\0';
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
283
284
285
286
287
288
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
  	
  	/* 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...
321
  	write_lock(&entry->rw_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
322
323
324
325
  	entry->ready = 0;
  	entry->dev = dev;
  	
  	/* Now, dive in. Write back to the hardware */
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
326
  	pdcspath_store(entry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
327
328
329
  	
  	/* Update the symlink to the real device */
  	sysfs_remove_link(&entry->kobj, "device");
93964fd4e   James Bottomley   parisc: pdc_stabl...
330
  	write_unlock(&entry->rw_lock);
26f032492   Kyle McMartin   [PARISC] Quiet sy...
331
332
  	ret = sysfs_create_link(&entry->kobj, &entry->dev->kobj, "device");
  	WARN_ON(ret);
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
333
334
  	printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" path to \"%s\"
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
  		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...
352
  	short i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
353
354
355
356
  
  	if (!entry || !buf)
  		return -EINVAL;
  	
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
357
  	read_lock(&entry->rw_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358
  	devpath = &entry->devpath;
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
359
360
  	i = entry->ready;
  	read_unlock(&entry->rw_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361

c74284222   Thibaut VARENE   [PARISC] pdc_stab...
362
  	if (!i)	/* entry is not ready */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
363
364
  		return -ENODATA;
  	
447c233da   Roel Kluin   parisc: Fix read ...
365
  	for (i = 0; i < 6 && devpath->layers[i]; i++)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
  		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;
c735483de   Helge Deller   parisc: pdc_stabl...
391
  	char in[64], *temp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
392
393
394
395
396
  
  	if (!entry || !buf || !count)
  		return -EINVAL;
  
  	/* We'll use a local copy of buf */
c735483de   Helge Deller   parisc: pdc_stabl...
397
  	count = min_t(size_t, count, sizeof(in)-1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
398
  	strncpy(in, buf, count);
c735483de   Helge Deller   parisc: pdc_stabl...
399
  	in[count] = '\0';
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
  	
  	/* 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...
421
  	write_lock(&entry->rw_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
422
423
424
425
426
427
  	
  	/* 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...
428
429
  	pdcspath_store(entry);
  	write_unlock(&entry->rw_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
430
  	
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
431
432
  	printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" layers to \"%s\"
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
  		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
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
  	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...
479
  static const struct sysfs_ops pdcspath_attr_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
480
481
482
483
484
  	.show = pdcspath_attr_show,
  	.store = pdcspath_attr_store,
  };
  
  /* These are the two attributes of any PDC path. */
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
485
486
  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
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
  
  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...
514
515
516
  
  /* 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
517
  /**
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
518
   * pdcs_size_read - Stable Storage size output.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
519
   * @buf: The output buffer to write to.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
520
   */
7f5482178   Greg Kroah-Hartman   kobject: convert ...
521
522
523
  static ssize_t pdcs_size_read(struct kobject *kobj,
  			      struct kobj_attribute *attr,
  			      char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
524
525
  {
  	char *out = buf;
823bccfc4   Greg Kroah-Hartman   remove "struct su...
526

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

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

c74284222   Thibaut VARENE   [PARISC] pdc_stab...
534
535
536
537
538
  	return out - buf;
  }
  
  /**
   * pdcs_auto_read - Stable Storage autoboot/search flag output.
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
539
540
541
   * @buf: The output buffer to write to.
   * @knob: The PF_AUTOBOOT or PF_AUTOSEARCH flag
   */
7f5482178   Greg Kroah-Hartman   kobject: convert ...
542
543
544
  static ssize_t pdcs_auto_read(struct kobject *kobj,
  			      struct kobj_attribute *attr,
  			      char *buf, int knob)
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
545
546
547
  {
  	char *out = buf;
  	struct pdcspath_entry *pathentry;
67a5a59d3   Helge Deller   [PARISC] Misc. ja...
548

7f5482178   Greg Kroah-Hartman   kobject: convert ...
549
  	if (!buf)
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
  		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...
566
567
   * @buf: The output buffer to write to.
   */
7f5482178   Greg Kroah-Hartman   kobject: convert ...
568
569
  static ssize_t pdcs_autoboot_read(struct kobject *kobj,
  				  struct kobj_attribute *attr, char *buf)
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
570
  {
7f5482178   Greg Kroah-Hartman   kobject: convert ...
571
  	return pdcs_auto_read(kobj, attr, buf, PF_AUTOBOOT);
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
572
573
574
575
  }
  
  /**
   * pdcs_autosearch_read - Stable Storage autoboot flag output.
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
576
577
   * @buf: The output buffer to write to.
   */
7f5482178   Greg Kroah-Hartman   kobject: convert ...
578
579
  static ssize_t pdcs_autosearch_read(struct kobject *kobj,
  				    struct kobj_attribute *attr, char *buf)
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
580
  {
7f5482178   Greg Kroah-Hartman   kobject: convert ...
581
  	return pdcs_auto_read(kobj, attr, buf, PF_AUTOSEARCH);
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
582
583
584
585
  }
  
  /**
   * pdcs_timer_read - Stable Storage timer count output (in seconds).
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
586
587
588
589
   * @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 ...
590
591
  static ssize_t pdcs_timer_read(struct kobject *kobj,
  			       struct kobj_attribute *attr, char *buf)
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
592
593
594
  {
  	char *out = buf;
  	struct pdcspath_entry *pathentry;
7f5482178   Greg Kroah-Hartman   kobject: convert ...
595
  	if (!buf)
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
  		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...
613
614
   * @buf: The output buffer to write to.
   */
7f5482178   Greg Kroah-Hartman   kobject: convert ...
615
616
  static ssize_t pdcs_osid_read(struct kobject *kobj,
  			      struct kobj_attribute *attr, char *buf)
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
617
618
  {
  	char *out = buf;
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
619

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

67a061a19   Kyle McMartin   [PARISC] Add os_i...
623
624
625
  	out += sprintf(out, "%s dependent data (0x%.4x)
  ",
  		os_id_to_string(pdcs_osid), pdcs_osid);
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
626
627
628
629
630
631
  
  	return out - buf;
  }
  
  /**
   * pdcs_osdep1_read - Stable Storage OS-Dependent data area 1 output.
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
632
633
634
635
   * @buf: The output buffer to write to.
   *
   * This can hold 16 bytes of OS-Dependent data.
   */
7f5482178   Greg Kroah-Hartman   kobject: convert ...
636
637
  static ssize_t pdcs_osdep1_read(struct kobject *kobj,
  				struct kobj_attribute *attr, char *buf)
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
638
639
640
  {
  	char *out = buf;
  	u32 result[4];
7f5482178   Greg Kroah-Hartman   kobject: convert ...
641
  	if (!buf)
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
  		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...
661
662
663
664
   * @buf: The output buffer to write to.
   *
   * I have NFC how to interpret the content of that register ;-).
   */
7f5482178   Greg Kroah-Hartman   kobject: convert ...
665
666
  static ssize_t pdcs_diagnostic_read(struct kobject *kobj,
  				    struct kobj_attribute *attr, char *buf)
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
667
668
669
  {
  	char *out = buf;
  	u32 result;
7f5482178   Greg Kroah-Hartman   kobject: convert ...
670
  	if (!buf)
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
671
672
673
674
675
676
677
678
  		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...
679
680
681
682
683
684
  
  	return out - buf;
  }
  
  /**
   * pdcs_fastsize_read - Stable Storage FastSize register output.
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
685
686
687
688
   * @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 ...
689
690
  static ssize_t pdcs_fastsize_read(struct kobject *kobj,
  				  struct kobj_attribute *attr, char *buf)
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
691
692
  {
  	char *out = buf;
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
693
  	u32 result;
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
694

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

7f5482178   Greg Kroah-Hartman   kobject: convert ...
938
939
940
941
942
943
944
945
946
947
  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...
948
  	NULL,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
949
  };
343fdfb7d   Arvind Yadav   parisc: pdc_stabl...
950
  static const struct attribute_group pdcs_attr_group = {
7f5482178   Greg Kroah-Hartman   kobject: convert ...
951
952
  	.attrs = pdcs_subsys_attrs,
  };
c829a5b49   Greg Kroah-Hartman   kobject: convert ...
953
  static struct kobject *stable_kobj;
4443d07fc   Greg Kroah-Hartman   kset: convert par...
954
  static struct kset *paths_kset;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
  
  /**
   * 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...
970
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
971
  	
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
972
973
974
  	/* 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
975
  	for (i = 0; (entry = pdcspath_entries[i]); i++) {
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
976
977
978
979
980
  		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
981
  			continue;
4443d07fc   Greg Kroah-Hartman   kset: convert par...
982
  		entry->kobj.kset = paths_kset;
73f368cf6   Greg Kroah-Hartman   Kobject: change d...
983
984
985
  		err = kobject_init_and_add(&entry->kobj, &ktype_pdcspath, NULL,
  					   "%s", entry->name);
  		if (err)
4b991da7f   Thibaut VARENE   [PARISC] pdc_stab...
986
  			return err;
73f368cf6   Greg Kroah-Hartman   Kobject: change d...
987

4b991da7f   Thibaut VARENE   [PARISC] pdc_stab...
988
  		/* kobject is now registered */
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
989
  		write_lock(&entry->rw_lock);
4b991da7f   Thibaut VARENE   [PARISC] pdc_stab...
990
  		entry->ready = 2;
93964fd4e   James Bottomley   parisc: pdc_stabl...
991
  		write_unlock(&entry->rw_lock);
4b991da7f   Thibaut VARENE   [PARISC] pdc_stab...
992
  		
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
993
  		/* Add a nice symlink to the real device */
26f032492   Kyle McMartin   [PARISC] Quiet sy...
994
995
996
997
  		if (entry->dev) {
  			err = sysfs_create_link(&entry->kobj, &entry->dev->kobj, "device");
  			WARN_ON(err);
  		}
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
998

73f368cf6   Greg Kroah-Hartman   Kobject: change d...
999
  		kobject_uevent(&entry->kobj, KOBJ_ADD);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1000
1001
1002
1003
1004
1005
1006
1007
  	}
  	
  	return 0;
  }
  
  /**
   * pdcs_unregister_pathentries - Routine called when unregistering the module.
   */
4b991da7f   Thibaut VARENE   [PARISC] pdc_stab...
1008
  static inline void
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1009
1010
1011
1012
1013
  pdcs_unregister_pathentries(void)
  {
  	unsigned short i;
  	struct pdcspath_entry *entry;
  	
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
1014
1015
  	for (i = 0; (entry = pdcspath_entries[i]); i++) {
  		read_lock(&entry->rw_lock);
4b991da7f   Thibaut VARENE   [PARISC] pdc_stab...
1016
  		if (entry->ready >= 2)
c10997f65   Greg Kroah-Hartman   Kobject: convert ...
1017
  			kobject_put(&entry->kobj);
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
1018
1019
  		read_unlock(&entry->rw_lock);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1020
1021
1022
  }
  
  /*
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
1023
1024
   * 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
1025
1026
1027
1028
   */
  static int __init
  pdc_stable_init(void)
  {
7f5482178   Greg Kroah-Hartman   kobject: convert ...
1029
  	int rc = 0, error = 0;
3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
1030
  	u32 result;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1031
1032
1033
1034
  
  	/* find the size of the stable storage */
  	if (pdc_stable_get_size(&pdcs_size) != PDC_OK) 
  		return -ENODEV;
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
1035
1036
1037
1038
1039
1040
  	/* 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
1041

3f9edb53f   Thibaut Varene   [PARISC] pdc_stab...
1042
1043
1044
1045
1046
1047
  	/* 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 ...
1048
1049
1050
  	/* 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...
1051
  		rc = -ENOMEM;
4b991da7f   Thibaut VARENE   [PARISC] pdc_stab...
1052
  		goto fail_firmreg;
4443d07fc   Greg Kroah-Hartman   kset: convert par...
1053
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1054

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

4443d07fc   Greg Kroah-Hartman   kset: convert par...
1058
  	/* register the paths kset as a child of the stable kset */
c829a5b49   Greg Kroah-Hartman   kobject: convert ...
1059
  	paths_kset = kset_create_and_add("paths", NULL, stable_kobj);
4443d07fc   Greg Kroah-Hartman   kset: convert par...
1060
1061
1062
1063
  	if (!paths_kset) {
  		rc = -ENOMEM;
  		goto fail_ksetreg;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1064

4443d07fc   Greg Kroah-Hartman   kset: convert par...
1065
  	/* now we create all "files" for the paths kset */
4b991da7f   Thibaut VARENE   [PARISC] pdc_stab...
1066
1067
1068
1069
  	if ((rc = pdcs_register_pathentries()))
  		goto fail_pdcsreg;
  
  	return rc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1070
  	
4b991da7f   Thibaut VARENE   [PARISC] pdc_stab...
1071
1072
  fail_pdcsreg:
  	pdcs_unregister_pathentries();
4443d07fc   Greg Kroah-Hartman   kset: convert par...
1073
  	kset_unregister(paths_kset);
4b991da7f   Thibaut VARENE   [PARISC] pdc_stab...
1074
  	
4443d07fc   Greg Kroah-Hartman   kset: convert par...
1075
  fail_ksetreg:
c10997f65   Greg Kroah-Hartman   Kobject: convert ...
1076
  	kobject_put(stable_kobj);
4b991da7f   Thibaut VARENE   [PARISC] pdc_stab...
1077
1078
  	
  fail_firmreg:
c74284222   Thibaut VARENE   [PARISC] pdc_stab...
1079
1080
  	printk(KERN_INFO PDCS_PREFIX " bailing out
  ");
4b991da7f   Thibaut VARENE   [PARISC] pdc_stab...
1081
  	return rc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1082
1083
1084
1085
1086
1087
  }
  
  static void __exit
  pdc_stable_exit(void)
  {
  	pdcs_unregister_pathentries();
4443d07fc   Greg Kroah-Hartman   kset: convert par...
1088
  	kset_unregister(paths_kset);
c10997f65   Greg Kroah-Hartman   Kobject: convert ...
1089
  	kobject_put(stable_kobj);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1090
1091
1092
1093
1094
  }
  
  
  module_init(pdc_stable_init);
  module_exit(pdc_stable_exit);