]> Pileus Git - ~andy/linux/blob - drivers/misc/ibmasm/ibmasmfs.c
Merge tag 'stable/for-linus-3.9-rc6-tag' of git://git.kernel.org/pub/scm/linux/kernel...
[~andy/linux] / drivers / misc / ibmasm / ibmasmfs.c
1 /*
2  * IBM ASM Service Processor Device Driver
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  *
18  * Copyright (C) IBM Corporation, 2004
19  *
20  * Author: Max Asböck <amax@us.ibm.com>
21  *
22  */
23
24 /*
25  * Parts of this code are based on an article by Jonathan Corbet
26  * that appeared in Linux Weekly News.
27  */
28
29
30 /*
31  * The IBMASM file virtual filesystem. It creates the following hierarchy
32  * dynamically when mounted from user space:
33  *
34  *    /ibmasm
35  *    |-- 0
36  *    |   |-- command
37  *    |   |-- event
38  *    |   |-- reverse_heartbeat
39  *    |   `-- remote_video
40  *    |       |-- depth
41  *    |       |-- height
42  *    |       `-- width
43  *    .
44  *    .
45  *    .
46  *    `-- n
47  *        |-- command
48  *        |-- event
49  *        |-- reverse_heartbeat
50  *        `-- remote_video
51  *            |-- depth
52  *            |-- height
53  *            `-- width
54  *
55  * For each service processor the following files are created:
56  *
57  * command: execute dot commands
58  *      write: execute a dot command on the service processor
59  *      read: return the result of a previously executed dot command
60  *
61  * events: listen for service processor events
62  *      read: sleep (interruptible) until an event occurs
63  *      write: wakeup sleeping event listener
64  *
65  * reverse_heartbeat: send a heartbeat to the service processor
66  *      read: sleep (interruptible) until the reverse heartbeat fails
67  *      write: wakeup sleeping heartbeat listener
68  *
69  * remote_video/width
70  * remote_video/height
71  * remote_video/width: control remote display settings
72  *      write: set value
73  *      read: read value
74  */
75
76 #include <linux/fs.h>
77 #include <linux/pagemap.h>
78 #include <linux/slab.h>
79 #include <asm/uaccess.h>
80 #include <asm/io.h>
81 #include "ibmasm.h"
82 #include "remote.h"
83 #include "dot_command.h"
84
85 #define IBMASMFS_MAGIC 0x66726f67
86
87 static LIST_HEAD(service_processors);
88
89 static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode);
90 static void ibmasmfs_create_files (struct super_block *sb);
91 static int ibmasmfs_fill_super (struct super_block *sb, void *data, int silent);
92
93
94 static struct dentry *ibmasmfs_mount(struct file_system_type *fst,
95                         int flags, const char *name, void *data)
96 {
97         return mount_single(fst, flags, data, ibmasmfs_fill_super);
98 }
99
100 static const struct super_operations ibmasmfs_s_ops = {
101         .statfs         = simple_statfs,
102         .drop_inode     = generic_delete_inode,
103 };
104
105 static const struct file_operations *ibmasmfs_dir_ops = &simple_dir_operations;
106
107 static struct file_system_type ibmasmfs_type = {
108         .owner          = THIS_MODULE,
109         .name           = "ibmasmfs",
110         .mount          = ibmasmfs_mount,
111         .kill_sb        = kill_litter_super,
112 };
113 MODULE_ALIAS_FS("ibmasmfs");
114
115 static int ibmasmfs_fill_super (struct super_block *sb, void *data, int silent)
116 {
117         struct inode *root;
118
119         sb->s_blocksize = PAGE_CACHE_SIZE;
120         sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
121         sb->s_magic = IBMASMFS_MAGIC;
122         sb->s_op = &ibmasmfs_s_ops;
123         sb->s_time_gran = 1;
124
125         root = ibmasmfs_make_inode (sb, S_IFDIR | 0500);
126         if (!root)
127                 return -ENOMEM;
128
129         root->i_op = &simple_dir_inode_operations;
130         root->i_fop = ibmasmfs_dir_ops;
131
132         sb->s_root = d_make_root(root);
133         if (!sb->s_root)
134                 return -ENOMEM;
135
136         ibmasmfs_create_files(sb);
137         return 0;
138 }
139
140 static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode)
141 {
142         struct inode *ret = new_inode(sb);
143
144         if (ret) {
145                 ret->i_ino = get_next_ino();
146                 ret->i_mode = mode;
147                 ret->i_atime = ret->i_mtime = ret->i_ctime = CURRENT_TIME;
148         }
149         return ret;
150 }
151
152 static struct dentry *ibmasmfs_create_file (struct super_block *sb,
153                         struct dentry *parent,
154                         const char *name,
155                         const struct file_operations *fops,
156                         void *data,
157                         int mode)
158 {
159         struct dentry *dentry;
160         struct inode *inode;
161
162         dentry = d_alloc_name(parent, name);
163         if (!dentry)
164                 return NULL;
165
166         inode = ibmasmfs_make_inode(sb, S_IFREG | mode);
167         if (!inode) {
168                 dput(dentry);
169                 return NULL;
170         }
171
172         inode->i_fop = fops;
173         inode->i_private = data;
174
175         d_add(dentry, inode);
176         return dentry;
177 }
178
179 static struct dentry *ibmasmfs_create_dir (struct super_block *sb,
180                                 struct dentry *parent,
181                                 const char *name)
182 {
183         struct dentry *dentry;
184         struct inode *inode;
185
186         dentry = d_alloc_name(parent, name);
187         if (!dentry)
188                 return NULL;
189
190         inode = ibmasmfs_make_inode(sb, S_IFDIR | 0500);
191         if (!inode) {
192                 dput(dentry);
193                 return NULL;
194         }
195
196         inode->i_op = &simple_dir_inode_operations;
197         inode->i_fop = ibmasmfs_dir_ops;
198
199         d_add(dentry, inode);
200         return dentry;
201 }
202
203 int ibmasmfs_register(void)
204 {
205         return register_filesystem(&ibmasmfs_type);
206 }
207
208 void ibmasmfs_unregister(void)
209 {
210         unregister_filesystem(&ibmasmfs_type);
211 }
212
213 void ibmasmfs_add_sp(struct service_processor *sp)
214 {
215         list_add(&sp->node, &service_processors);
216 }
217
218 /* struct to save state between command file operations */
219 struct ibmasmfs_command_data {
220         struct service_processor        *sp;
221         struct command                  *command;
222 };
223
224 /* struct to save state between event file operations */
225 struct ibmasmfs_event_data {
226         struct service_processor        *sp;
227         struct event_reader             reader;
228         int                             active;
229 };
230
231 /* struct to save state between reverse heartbeat file operations */
232 struct ibmasmfs_heartbeat_data {
233         struct service_processor        *sp;
234         struct reverse_heartbeat        heartbeat;
235         int                             active;
236 };
237
238 static int command_file_open(struct inode *inode, struct file *file)
239 {
240         struct ibmasmfs_command_data *command_data;
241
242         if (!inode->i_private)
243                 return -ENODEV;
244
245         command_data = kmalloc(sizeof(struct ibmasmfs_command_data), GFP_KERNEL);
246         if (!command_data)
247                 return -ENOMEM;
248
249         command_data->command = NULL;
250         command_data->sp = inode->i_private;
251         file->private_data = command_data;
252         return 0;
253 }
254
255 static int command_file_close(struct inode *inode, struct file *file)
256 {
257         struct ibmasmfs_command_data *command_data = file->private_data;
258
259         if (command_data->command)
260                 command_put(command_data->command);
261
262         kfree(command_data);
263         return 0;
264 }
265
266 static ssize_t command_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
267 {
268         struct ibmasmfs_command_data *command_data = file->private_data;
269         struct command *cmd;
270         int len;
271         unsigned long flags;
272
273         if (*offset < 0)
274                 return -EINVAL;
275         if (count == 0 || count > IBMASM_CMD_MAX_BUFFER_SIZE)
276                 return 0;
277         if (*offset != 0)
278                 return 0;
279
280         spin_lock_irqsave(&command_data->sp->lock, flags);
281         cmd = command_data->command;
282         if (cmd == NULL) {
283                 spin_unlock_irqrestore(&command_data->sp->lock, flags);
284                 return 0;
285         }
286         command_data->command = NULL;
287         spin_unlock_irqrestore(&command_data->sp->lock, flags);
288
289         if (cmd->status != IBMASM_CMD_COMPLETE) {
290                 command_put(cmd);
291                 return -EIO;
292         }
293         len = min(count, cmd->buffer_size);
294         if (copy_to_user(buf, cmd->buffer, len)) {
295                 command_put(cmd);
296                 return -EFAULT;
297         }
298         command_put(cmd);
299
300         return len;
301 }
302
303 static ssize_t command_file_write(struct file *file, const char __user *ubuff, size_t count, loff_t *offset)
304 {
305         struct ibmasmfs_command_data *command_data = file->private_data;
306         struct command *cmd;
307         unsigned long flags;
308
309         if (*offset < 0)
310                 return -EINVAL;
311         if (count == 0 || count > IBMASM_CMD_MAX_BUFFER_SIZE)
312                 return 0;
313         if (*offset != 0)
314                 return 0;
315
316         /* commands are executed sequentially, only one command at a time */
317         if (command_data->command)
318                 return -EAGAIN;
319
320         cmd = ibmasm_new_command(command_data->sp, count);
321         if (!cmd)
322                 return -ENOMEM;
323
324         if (copy_from_user(cmd->buffer, ubuff, count)) {
325                 command_put(cmd);
326                 return -EFAULT;
327         }
328
329         spin_lock_irqsave(&command_data->sp->lock, flags);
330         if (command_data->command) {
331                 spin_unlock_irqrestore(&command_data->sp->lock, flags);
332                 command_put(cmd);
333                 return -EAGAIN;
334         }
335         command_data->command = cmd;
336         spin_unlock_irqrestore(&command_data->sp->lock, flags);
337
338         ibmasm_exec_command(command_data->sp, cmd);
339         ibmasm_wait_for_response(cmd, get_dot_command_timeout(cmd->buffer));
340
341         return count;
342 }
343
344 static int event_file_open(struct inode *inode, struct file *file)
345 {
346         struct ibmasmfs_event_data *event_data;
347         struct service_processor *sp;
348
349         if (!inode->i_private)
350                 return -ENODEV;
351
352         sp = inode->i_private;
353
354         event_data = kmalloc(sizeof(struct ibmasmfs_event_data), GFP_KERNEL);
355         if (!event_data)
356                 return -ENOMEM;
357
358         ibmasm_event_reader_register(sp, &event_data->reader);
359
360         event_data->sp = sp;
361         event_data->active = 0;
362         file->private_data = event_data;
363         return 0;
364 }
365
366 static int event_file_close(struct inode *inode, struct file *file)
367 {
368         struct ibmasmfs_event_data *event_data = file->private_data;
369
370         ibmasm_event_reader_unregister(event_data->sp, &event_data->reader);
371         kfree(event_data);
372         return 0;
373 }
374
375 static ssize_t event_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
376 {
377         struct ibmasmfs_event_data *event_data = file->private_data;
378         struct event_reader *reader = &event_data->reader;
379         struct service_processor *sp = event_data->sp;
380         int ret;
381         unsigned long flags;
382
383         if (*offset < 0)
384                 return -EINVAL;
385         if (count == 0 || count > IBMASM_EVENT_MAX_SIZE)
386                 return 0;
387         if (*offset != 0)
388                 return 0;
389
390         spin_lock_irqsave(&sp->lock, flags);
391         if (event_data->active) {
392                 spin_unlock_irqrestore(&sp->lock, flags);
393                 return -EBUSY;
394         }
395         event_data->active = 1;
396         spin_unlock_irqrestore(&sp->lock, flags);
397
398         ret = ibmasm_get_next_event(sp, reader);
399         if (ret <= 0)
400                 goto out;
401
402         if (count < reader->data_size) {
403                 ret = -EINVAL;
404                 goto out;
405         }
406
407         if (copy_to_user(buf, reader->data, reader->data_size)) {
408                 ret = -EFAULT;
409                 goto out;
410         }
411         ret = reader->data_size;
412
413 out:
414         event_data->active = 0;
415         return ret;
416 }
417
418 static ssize_t event_file_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
419 {
420         struct ibmasmfs_event_data *event_data = file->private_data;
421
422         if (*offset < 0)
423                 return -EINVAL;
424         if (count != 1)
425                 return 0;
426         if (*offset != 0)
427                 return 0;
428
429         ibmasm_cancel_next_event(&event_data->reader);
430         return 0;
431 }
432
433 static int r_heartbeat_file_open(struct inode *inode, struct file *file)
434 {
435         struct ibmasmfs_heartbeat_data *rhbeat;
436
437         if (!inode->i_private)
438                 return -ENODEV;
439
440         rhbeat = kmalloc(sizeof(struct ibmasmfs_heartbeat_data), GFP_KERNEL);
441         if (!rhbeat)
442                 return -ENOMEM;
443
444         rhbeat->sp = inode->i_private;
445         rhbeat->active = 0;
446         ibmasm_init_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat);
447         file->private_data = rhbeat;
448         return 0;
449 }
450
451 static int r_heartbeat_file_close(struct inode *inode, struct file *file)
452 {
453         struct ibmasmfs_heartbeat_data *rhbeat = file->private_data;
454
455         kfree(rhbeat);
456         return 0;
457 }
458
459 static ssize_t r_heartbeat_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
460 {
461         struct ibmasmfs_heartbeat_data *rhbeat = file->private_data;
462         unsigned long flags;
463         int result;
464
465         if (*offset < 0)
466                 return -EINVAL;
467         if (count == 0 || count > 1024)
468                 return 0;
469         if (*offset != 0)
470                 return 0;
471
472         /* allow only one reverse heartbeat per process */
473         spin_lock_irqsave(&rhbeat->sp->lock, flags);
474         if (rhbeat->active) {
475                 spin_unlock_irqrestore(&rhbeat->sp->lock, flags);
476                 return -EBUSY;
477         }
478         rhbeat->active = 1;
479         spin_unlock_irqrestore(&rhbeat->sp->lock, flags);
480
481         result = ibmasm_start_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat);
482         rhbeat->active = 0;
483
484         return result;
485 }
486
487 static ssize_t r_heartbeat_file_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
488 {
489         struct ibmasmfs_heartbeat_data *rhbeat = file->private_data;
490
491         if (*offset < 0)
492                 return -EINVAL;
493         if (count != 1)
494                 return 0;
495         if (*offset != 0)
496                 return 0;
497
498         if (rhbeat->active)
499                 ibmasm_stop_reverse_heartbeat(&rhbeat->heartbeat);
500
501         return 1;
502 }
503
504 static int remote_settings_file_close(struct inode *inode, struct file *file)
505 {
506         return 0;
507 }
508
509 static ssize_t remote_settings_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
510 {
511         void __iomem *address = (void __iomem *)file->private_data;
512         unsigned char *page;
513         int retval;
514         int len = 0;
515         unsigned int value;
516
517         if (*offset < 0)
518                 return -EINVAL;
519         if (count == 0 || count > 1024)
520                 return 0;
521         if (*offset != 0)
522                 return 0;
523
524         page = (unsigned char *)__get_free_page(GFP_KERNEL);
525         if (!page)
526                 return -ENOMEM;
527
528         value = readl(address);
529         len = sprintf(page, "%d\n", value);
530
531         if (copy_to_user(buf, page, len)) {
532                 retval = -EFAULT;
533                 goto exit;
534         }
535         *offset += len;
536         retval = len;
537
538 exit:
539         free_page((unsigned long)page);
540         return retval;
541 }
542
543 static ssize_t remote_settings_file_write(struct file *file, const char __user *ubuff, size_t count, loff_t *offset)
544 {
545         void __iomem *address = (void __iomem *)file->private_data;
546         char *buff;
547         unsigned int value;
548
549         if (*offset < 0)
550                 return -EINVAL;
551         if (count == 0 || count > 1024)
552                 return 0;
553         if (*offset != 0)
554                 return 0;
555
556         buff = kzalloc (count + 1, GFP_KERNEL);
557         if (!buff)
558                 return -ENOMEM;
559
560
561         if (copy_from_user(buff, ubuff, count)) {
562                 kfree(buff);
563                 return -EFAULT;
564         }
565
566         value = simple_strtoul(buff, NULL, 10);
567         writel(value, address);
568         kfree(buff);
569
570         return count;
571 }
572
573 static const struct file_operations command_fops = {
574         .open =         command_file_open,
575         .release =      command_file_close,
576         .read =         command_file_read,
577         .write =        command_file_write,
578         .llseek =       generic_file_llseek,
579 };
580
581 static const struct file_operations event_fops = {
582         .open =         event_file_open,
583         .release =      event_file_close,
584         .read =         event_file_read,
585         .write =        event_file_write,
586         .llseek =       generic_file_llseek,
587 };
588
589 static const struct file_operations r_heartbeat_fops = {
590         .open =         r_heartbeat_file_open,
591         .release =      r_heartbeat_file_close,
592         .read =         r_heartbeat_file_read,
593         .write =        r_heartbeat_file_write,
594         .llseek =       generic_file_llseek,
595 };
596
597 static const struct file_operations remote_settings_fops = {
598         .open =         simple_open,
599         .release =      remote_settings_file_close,
600         .read =         remote_settings_file_read,
601         .write =        remote_settings_file_write,
602         .llseek =       generic_file_llseek,
603 };
604
605
606 static void ibmasmfs_create_files (struct super_block *sb)
607 {
608         struct list_head *entry;
609         struct service_processor *sp;
610
611         list_for_each(entry, &service_processors) {
612                 struct dentry *dir;
613                 struct dentry *remote_dir;
614                 sp = list_entry(entry, struct service_processor, node);
615                 dir = ibmasmfs_create_dir(sb, sb->s_root, sp->dirname);
616                 if (!dir)
617                         continue;
618
619                 ibmasmfs_create_file(sb, dir, "command", &command_fops, sp, S_IRUSR|S_IWUSR);
620                 ibmasmfs_create_file(sb, dir, "event", &event_fops, sp, S_IRUSR|S_IWUSR);
621                 ibmasmfs_create_file(sb, dir, "reverse_heartbeat", &r_heartbeat_fops, sp, S_IRUSR|S_IWUSR);
622
623                 remote_dir = ibmasmfs_create_dir(sb, dir, "remote_video");
624                 if (!remote_dir)
625                         continue;
626
627                 ibmasmfs_create_file(sb, remote_dir, "width", &remote_settings_fops, (void *)display_width(sp), S_IRUSR|S_IWUSR);
628                 ibmasmfs_create_file(sb, remote_dir, "height", &remote_settings_fops, (void *)display_height(sp), S_IRUSR|S_IWUSR);
629                 ibmasmfs_create_file(sb, remote_dir, "depth", &remote_settings_fops, (void *)display_depth(sp), S_IRUSR|S_IWUSR);
630         }
631 }