[PATCH] ibmasm driver: redesign handling of remote control events
[linux-2.6.git] / 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  * dymamically 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 <asm/uaccess.h>
79 #include <asm/io.h>
80 #include "ibmasm.h"
81 #include "remote.h"
82 #include "dot_command.h"
83
84 #define IBMASMFS_MAGIC 0x66726f67
85
86 static LIST_HEAD(service_processors);
87
88 static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode);
89 static void ibmasmfs_create_files (struct super_block *sb, struct dentry *root);
90 static int ibmasmfs_fill_super (struct super_block *sb, void *data, int silent);
91
92
93 static struct super_block *ibmasmfs_get_super(struct file_system_type *fst,
94                         int flags, const char *name, void *data)
95 {
96         return get_sb_single(fst, flags, data, ibmasmfs_fill_super);
97 }
98
99 static struct super_operations ibmasmfs_s_ops = {
100         .statfs         = simple_statfs,
101         .drop_inode     = generic_delete_inode,
102 };
103
104 static struct file_operations *ibmasmfs_dir_ops = &simple_dir_operations;
105
106 static struct file_system_type ibmasmfs_type = {
107         .owner          = THIS_MODULE,
108         .name           = "ibmasmfs",
109         .get_sb         = ibmasmfs_get_super,
110         .kill_sb        = kill_litter_super,
111 };
112
113 static int ibmasmfs_fill_super (struct super_block *sb, void *data, int silent)
114 {
115         struct inode *root;
116         struct dentry *root_dentry;
117
118         sb->s_blocksize = PAGE_CACHE_SIZE;
119         sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
120         sb->s_magic = IBMASMFS_MAGIC;
121         sb->s_op = &ibmasmfs_s_ops;
122         sb->s_time_gran = 1;
123
124         root = ibmasmfs_make_inode (sb, S_IFDIR | 0500);
125         if (!root)
126                 return -ENOMEM;
127
128         root->i_op = &simple_dir_inode_operations;
129         root->i_fop = ibmasmfs_dir_ops;
130
131         root_dentry = d_alloc_root(root);
132         if (!root_dentry) {
133                 iput(root);
134                 return -ENOMEM;
135         }
136         sb->s_root = root_dentry;
137
138         ibmasmfs_create_files(sb, root_dentry);
139         return 0;
140 }
141
142 static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode)
143 {
144         struct inode *ret = new_inode(sb);
145
146         if (ret) {
147                 ret->i_mode = mode;
148                 ret->i_uid = ret->i_gid = 0;
149                 ret->i_blksize = PAGE_CACHE_SIZE;
150                 ret->i_blocks = 0;
151                 ret->i_atime = ret->i_mtime = ret->i_ctime = CURRENT_TIME;
152         }
153         return ret;
154 }
155
156 static struct dentry *ibmasmfs_create_file (struct super_block *sb,
157                         struct dentry *parent,
158                         const char *name,
159                         struct file_operations *fops,
160                         void *data,
161                         int mode)
162 {
163         struct dentry *dentry;
164         struct inode *inode;
165
166         dentry = d_alloc_name(parent, name);
167         if (!dentry)
168                 return NULL;
169
170         inode = ibmasmfs_make_inode(sb, S_IFREG | mode);
171         if (!inode) {
172                 dput(dentry);
173                 return NULL;
174         }
175
176         inode->i_fop = fops;
177         inode->u.generic_ip = data;
178
179         d_add(dentry, inode);
180         return dentry;
181 }
182
183 static struct dentry *ibmasmfs_create_dir (struct super_block *sb,
184                                 struct dentry *parent,
185                                 const char *name)
186 {
187         struct dentry *dentry;
188         struct inode *inode;
189
190         dentry = d_alloc_name(parent, name);
191         if (!dentry)
192                 return NULL;
193
194         inode = ibmasmfs_make_inode(sb, S_IFDIR | 0500);
195         if (!inode) {
196                 dput(dentry);
197                 return NULL;
198         }
199
200         inode->i_op = &simple_dir_inode_operations;
201         inode->i_fop = ibmasmfs_dir_ops;
202
203         d_add(dentry, inode);
204         return dentry;
205 }
206
207 int ibmasmfs_register(void)
208 {
209         return register_filesystem(&ibmasmfs_type);
210 }
211
212 void ibmasmfs_unregister(void)
213 {
214         unregister_filesystem(&ibmasmfs_type);
215 }
216
217 void ibmasmfs_add_sp(struct service_processor *sp)
218 {
219         list_add(&sp->node, &service_processors);
220 }
221
222 /* struct to save state between command file operations */
223 struct ibmasmfs_command_data {
224         struct service_processor        *sp;
225         struct command                  *command;
226 };
227
228 /* struct to save state between event file operations */
229 struct ibmasmfs_event_data {
230         struct service_processor        *sp;
231         struct event_reader             reader;
232         int                             active;
233 };
234
235 /* struct to save state between reverse heartbeat file operations */
236 struct ibmasmfs_heartbeat_data {
237         struct service_processor        *sp;
238         struct reverse_heartbeat        heartbeat;
239         int                             active;
240 };
241
242 static int command_file_open(struct inode *inode, struct file *file)
243 {
244         struct ibmasmfs_command_data *command_data;
245
246         if (!inode->u.generic_ip)
247                 return -ENODEV;
248
249         command_data = kmalloc(sizeof(struct ibmasmfs_command_data), GFP_KERNEL);
250         if (!command_data)
251                 return -ENOMEM;
252
253         command_data->command = NULL;
254         command_data->sp = inode->u.generic_ip;
255         file->private_data = command_data;
256         return 0;
257 }
258
259 static int command_file_close(struct inode *inode, struct file *file)
260 {
261         struct ibmasmfs_command_data *command_data = file->private_data;
262
263         if (command_data->command)
264                 command_put(command_data->command);     
265
266         kfree(command_data);
267         return 0;
268 }
269
270 static ssize_t command_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
271 {
272         struct ibmasmfs_command_data *command_data = file->private_data;
273         struct command *cmd;
274         int len;
275         unsigned long flags;
276
277         if (*offset < 0)
278                 return -EINVAL;
279         if (count == 0 || count > IBMASM_CMD_MAX_BUFFER_SIZE)
280                 return 0;
281         if (*offset != 0)
282                 return 0;
283
284         spin_lock_irqsave(&command_data->sp->lock, flags);
285         cmd = command_data->command;
286         if (cmd == NULL) {
287                 spin_unlock_irqrestore(&command_data->sp->lock, flags);
288                 return 0;
289         }
290         command_data->command = NULL;
291         spin_unlock_irqrestore(&command_data->sp->lock, flags);
292
293         if (cmd->status != IBMASM_CMD_COMPLETE) {
294                 command_put(cmd);
295                 return -EIO;
296         }
297         len = min(count, cmd->buffer_size);
298         if (copy_to_user(buf, cmd->buffer, len)) {
299                 command_put(cmd);
300                 return -EFAULT;
301         }
302         command_put(cmd);
303
304         return len;
305 }
306
307 static ssize_t command_file_write(struct file *file, const char __user *ubuff, size_t count, loff_t *offset)
308 {
309         struct ibmasmfs_command_data *command_data = file->private_data;
310         struct command *cmd;
311         unsigned long flags;
312
313         if (*offset < 0)
314                 return -EINVAL;
315         if (count == 0 || count > IBMASM_CMD_MAX_BUFFER_SIZE)
316                 return 0;
317         if (*offset != 0)
318                 return 0;
319
320         /* commands are executed sequentially, only one command at a time */
321         if (command_data->command)
322                 return -EAGAIN;
323
324         cmd = ibmasm_new_command(count);
325         if (!cmd)
326                 return -ENOMEM;
327
328         if (copy_from_user(cmd->buffer, ubuff, count)) {
329                 command_put(cmd);
330                 return -EFAULT;
331         }
332
333         spin_lock_irqsave(&command_data->sp->lock, flags);
334         if (command_data->command) {
335                 spin_unlock_irqrestore(&command_data->sp->lock, flags);
336                 command_put(cmd);
337                 return -EAGAIN;
338         }
339         command_data->command = cmd;
340         spin_unlock_irqrestore(&command_data->sp->lock, flags);
341
342         ibmasm_exec_command(command_data->sp, cmd);
343         ibmasm_wait_for_response(cmd, get_dot_command_timeout(cmd->buffer));
344
345         return count;
346 }
347
348 static int event_file_open(struct inode *inode, struct file *file)
349 {
350         struct ibmasmfs_event_data *event_data;
351         struct service_processor *sp; 
352
353         if (!inode->u.generic_ip)
354                 return -ENODEV;
355
356         sp = inode->u.generic_ip;
357
358         event_data = kmalloc(sizeof(struct ibmasmfs_event_data), GFP_KERNEL);
359         if (!event_data)
360                 return -ENOMEM;
361
362         ibmasm_event_reader_register(sp, &event_data->reader);
363
364         event_data->sp = sp;
365         event_data->active = 0;
366         file->private_data = event_data;
367         return 0;
368 }
369
370 static int event_file_close(struct inode *inode, struct file *file)
371 {
372         struct ibmasmfs_event_data *event_data = file->private_data;
373
374         ibmasm_event_reader_unregister(event_data->sp, &event_data->reader);
375         kfree(event_data);
376         return 0;
377 }
378
379 static ssize_t event_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
380 {
381         struct ibmasmfs_event_data *event_data = file->private_data;
382         struct event_reader *reader = &event_data->reader;
383         struct service_processor *sp = event_data->sp;
384         int ret;
385         unsigned long flags;
386
387         if (*offset < 0)
388                 return -EINVAL;
389         if (count == 0 || count > IBMASM_EVENT_MAX_SIZE)
390                 return 0;
391         if (*offset != 0)
392                 return 0;
393
394         spin_lock_irqsave(&sp->lock, flags);
395         if (event_data->active) {
396                 spin_unlock_irqrestore(&sp->lock, flags);
397                 return -EBUSY;
398         }
399         event_data->active = 1;
400         spin_unlock_irqrestore(&sp->lock, flags);
401
402         ret = ibmasm_get_next_event(sp, reader);
403         if (ret <= 0)
404                 goto out;
405
406         if (count < reader->data_size) {
407                 ret = -EINVAL;
408                 goto out;
409         }
410
411         if (copy_to_user(buf, reader->data, reader->data_size)) {
412                 ret = -EFAULT;
413                 goto out;
414         }
415         ret = reader->data_size;
416
417 out:
418         event_data->active = 0;
419         return ret;
420 }
421
422 static ssize_t event_file_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
423 {
424         struct ibmasmfs_event_data *event_data = file->private_data;
425
426         if (*offset < 0)
427                 return -EINVAL;
428         if (count != 1)
429                 return 0;
430         if (*offset != 0)
431                 return 0;
432
433         ibmasm_cancel_next_event(&event_data->reader);
434         return 0;
435 }
436
437 static int r_heartbeat_file_open(struct inode *inode, struct file *file)
438 {
439         struct ibmasmfs_heartbeat_data *rhbeat;
440
441         if (!inode->u.generic_ip)
442                 return -ENODEV;
443
444         rhbeat = kmalloc(sizeof(struct ibmasmfs_heartbeat_data), GFP_KERNEL);
445         if (!rhbeat)
446                 return -ENOMEM;
447
448         rhbeat->sp = (struct service_processor *)inode->u.generic_ip;
449         rhbeat->active = 0;
450         ibmasm_init_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat);
451         file->private_data = rhbeat;
452         return 0;
453 }
454
455 static int r_heartbeat_file_close(struct inode *inode, struct file *file)
456 {
457         struct ibmasmfs_heartbeat_data *rhbeat = file->private_data;
458
459         kfree(rhbeat);
460         return 0;
461 }
462
463 static ssize_t r_heartbeat_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
464 {
465         struct ibmasmfs_heartbeat_data *rhbeat = file->private_data;
466         unsigned long flags;
467         int result;
468
469         if (*offset < 0)
470                 return -EINVAL;
471         if (count == 0 || count > 1024)
472                 return 0;
473         if (*offset != 0)
474                 return 0;
475
476         /* allow only one reverse heartbeat per process */
477         spin_lock_irqsave(&rhbeat->sp->lock, flags);
478         if (rhbeat->active) {
479                 spin_unlock_irqrestore(&rhbeat->sp->lock, flags);
480                 return -EBUSY;
481         }
482         rhbeat->active = 1;
483         spin_unlock_irqrestore(&rhbeat->sp->lock, flags);
484
485         result = ibmasm_start_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat);
486         rhbeat->active = 0;
487
488         return result;
489 }
490
491 static ssize_t r_heartbeat_file_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
492 {
493         struct ibmasmfs_heartbeat_data *rhbeat = file->private_data;
494
495         if (*offset < 0)
496                 return -EINVAL;
497         if (count != 1)
498                 return 0;
499         if (*offset != 0)
500                 return 0;
501
502         if (rhbeat->active)
503                 ibmasm_stop_reverse_heartbeat(&rhbeat->heartbeat);
504
505         return 1;
506 }
507
508 static int remote_settings_file_open(struct inode *inode, struct file *file)
509 {
510         file->private_data = inode->u.generic_ip;
511         return 0;
512 }
513
514 static int remote_settings_file_close(struct inode *inode, struct file *file)
515 {
516         return 0;
517 }
518
519 static ssize_t remote_settings_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
520 {
521         void __iomem *address = (void __iomem *)file->private_data;
522         unsigned char *page;
523         int retval;
524         int len = 0;
525         unsigned int value;
526
527         if (*offset < 0)
528                 return -EINVAL;
529         if (count == 0 || count > 1024)
530                 return 0;
531         if (*offset != 0)
532                 return 0;
533
534         page = (unsigned char *)__get_free_page(GFP_KERNEL);
535         if (!page)
536                 return -ENOMEM;
537
538         value = readl(address);
539         len = sprintf(page, "%d\n", value);
540
541         if (copy_to_user(buf, page, len)) {
542                 retval = -EFAULT;
543                 goto exit;
544         }
545         *offset += len;
546         retval = len;
547
548 exit:
549         free_page((unsigned long)page);
550         return retval;
551 }
552
553 static ssize_t remote_settings_file_write(struct file *file, const char __user *ubuff, size_t count, loff_t *offset)
554 {
555         void __iomem *address = (void __iomem *)file->private_data;
556         char *buff;
557         unsigned int value;
558
559         if (*offset < 0)
560                 return -EINVAL;
561         if (count == 0 || count > 1024)
562                 return 0;
563         if (*offset != 0)
564                 return 0;
565
566         buff = kmalloc (count + 1, GFP_KERNEL);
567         if (!buff)
568                 return -ENOMEM;
569
570         memset(buff, 0x0, count + 1);
571
572         if (copy_from_user(buff, ubuff, count)) {
573                 kfree(buff);
574                 return -EFAULT;
575         }
576         
577         value = simple_strtoul(buff, NULL, 10);
578         writel(value, address);
579         kfree(buff);
580
581         return count;
582 }
583
584 static struct file_operations command_fops = {
585         .open =         command_file_open,
586         .release =      command_file_close,
587         .read =         command_file_read,
588         .write =        command_file_write,
589 };
590
591 static struct file_operations event_fops = {
592         .open =         event_file_open,
593         .release =      event_file_close,
594         .read =         event_file_read,
595         .write =        event_file_write,
596 };
597
598 static struct file_operations r_heartbeat_fops = {
599         .open =         r_heartbeat_file_open,
600         .release =      r_heartbeat_file_close,
601         .read =         r_heartbeat_file_read,
602         .write =        r_heartbeat_file_write,
603 };
604
605 static struct file_operations remote_settings_fops = {
606         .open =         remote_settings_file_open,
607         .release =      remote_settings_file_close,
608         .read =         remote_settings_file_read,
609         .write =        remote_settings_file_write,
610 };
611
612
613 static void ibmasmfs_create_files (struct super_block *sb, struct dentry *root)
614 {
615         struct list_head *entry;
616         struct service_processor *sp;
617
618         list_for_each(entry, &service_processors) {
619                 struct dentry *dir;
620                 struct dentry *remote_dir;
621                 sp = list_entry(entry, struct service_processor, node);
622                 dir = ibmasmfs_create_dir(sb, root, sp->dirname);
623                 if (!dir)
624                         continue;
625
626                 ibmasmfs_create_file(sb, dir, "command", &command_fops, sp, S_IRUSR|S_IWUSR);
627                 ibmasmfs_create_file(sb, dir, "event", &event_fops, sp, S_IRUSR|S_IWUSR);
628                 ibmasmfs_create_file(sb, dir, "reverse_heartbeat", &r_heartbeat_fops, sp, S_IRUSR|S_IWUSR);
629
630                 remote_dir = ibmasmfs_create_dir(sb, dir, "remote_video");
631                 if (!remote_dir)
632                         continue;
633
634                 ibmasmfs_create_file(sb, remote_dir, "width", &remote_settings_fops, (void *)display_width(sp), S_IRUSR|S_IWUSR);
635                 ibmasmfs_create_file(sb, remote_dir, "height", &remote_settings_fops, (void *)display_height(sp), S_IRUSR|S_IWUSR);
636                 ibmasmfs_create_file(sb, remote_dir, "depth", &remote_settings_fops, (void *)display_depth(sp), S_IRUSR|S_IWUSR);
637         }
638 }