1 diff -urN linux-2.6.1/drivers/misc/ibmasm/ibmasmfs.c linux-2.6.1-ibmasm/drivers/misc/ibmasm/ibmasmfs.c
2 --- linux-2.6.1/drivers/misc/ibmasm/ibmasmfs.c 1969-12-31 16:00:00.000000000 -0800
3 +++ linux-2.6.1-ibmasm/drivers/misc/ibmasm/ibmasmfs.c 2004-01-23 15:39:00.000000000 -0800
6 + * IBM ASM Service Processor Device Driver
8 + * This program is free software; you can redistribute it and/or modify
9 + * it under the terms of the GNU General Public License as published by
10 + * the Free Software Foundation; either version 2 of the License, or
11 + * (at your option) any later version.
13 + * This program is distributed in the hope that it will be useful,
14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 + * GNU General Public License for more details.
18 + * You should have received a copy of the GNU General Public License
19 + * along with this program; if not, write to the Free Software
20 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 + * Copyright (C) IBM Corporation, 2004
24 + * Author: Max Asböck <amax@us.ibm.com>
29 + * Parts of this code are based on an article by Jonathan Corbet
30 + * that appeared in Linux Weekly News.
35 + * The IBMASM file virtual filesystem. It creates the following hierarchy
36 + * dymamically when mounted from user space:
42 + * | |-- reverse_heartbeat
43 + * | `-- remote_video
55 + * |-- reverse_heartbeat
63 + * For each service processor the following files are created:
65 + * command: execute dot commands
66 + * write: execute a dot command on the service processor
67 + * read: return the result of a previously executed dot command
69 + * events: listen for service processor events
70 + * read: sleep (interruptible) until an event occurs
72 + * reverse_heartbeat: send a heartbeat to the service processor
73 + * read: sleep (interruptible) until the reverse heartbeat fails
75 + * remote_video/width
76 + * remote_video/height
77 + * remote_video/width: control remote display settings
81 + * remote_video/connected
82 + * read: return "1" if web browser VNC java applet is connected,
85 + * remote_video/events
86 + * read: sleep until a remote mouse or keyboard event occurs, then return
90 +#include <linux/fs.h>
91 +#include <linux/pagemap.h>
92 +#include <asm/uaccess.h>
96 +#include "dot_command.h"
98 +#define IBMASMFS_MAGIC 0x66726f67
100 +static LIST_HEAD(service_processors);
102 +static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode);
103 +static void ibmasmfs_create_files (struct super_block *sb, struct dentry *root);
104 +static int ibmasmfs_fill_super (struct super_block *sb, void *data, int silent);
107 +static struct super_block *ibmasmfs_get_super(struct file_system_type *fst,
108 + int flags, const char *name, void *data)
110 + return get_sb_single(fst, flags, data, ibmasmfs_fill_super);
113 +static struct super_operations ibmasmfs_s_ops = {
114 + .statfs = simple_statfs,
115 + .drop_inode = generic_delete_inode,
118 +static struct file_operations *ibmasmfs_dir_ops = &simple_dir_operations;
120 +static struct file_system_type ibmasmfs_type = {
121 + .owner = THIS_MODULE,
122 + .name = "ibmasmfs",
123 + .get_sb = ibmasmfs_get_super,
124 + .kill_sb = kill_litter_super,
127 +static int ibmasmfs_fill_super (struct super_block *sb, void *data, int silent)
129 + struct inode *root;
130 + struct dentry *root_dentry;
132 + sb->s_blocksize = PAGE_CACHE_SIZE;
133 + sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
134 + sb->s_magic = IBMASMFS_MAGIC;
135 + sb->s_op = &ibmasmfs_s_ops;
137 + root = ibmasmfs_make_inode (sb, S_IFDIR | 0500);
141 + root->i_op = &simple_dir_inode_operations;
142 + root->i_fop = ibmasmfs_dir_ops;
144 + root_dentry = d_alloc_root(root);
145 + if (!root_dentry) {
149 + sb->s_root = root_dentry;
151 + ibmasmfs_create_files(sb, root_dentry);
155 +static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode)
157 + struct inode *ret = new_inode(sb);
160 + ret->i_mode = mode;
161 + ret->i_uid = ret->i_gid = 0;
162 + ret->i_blksize = PAGE_CACHE_SIZE;
164 + ret->i_atime = ret->i_mtime = ret->i_ctime = CURRENT_TIME;
169 +static struct dentry *ibmasmfs_create_file (struct super_block *sb,
170 + struct dentry *parent,
172 + struct file_operations *fops,
176 + struct dentry *dentry;
177 + struct inode *inode;
181 + qname.len = strlen (name);
182 + qname.hash = full_name_hash(name, qname.len);
184 + dentry = d_alloc(parent, &qname);
188 + inode = ibmasmfs_make_inode(sb, S_IFREG | mode);
194 + inode->i_fop = fops;
195 + inode->u.generic_ip = data;
197 + d_add(dentry, inode);
201 +static struct dentry *ibmasmfs_create_dir (struct super_block *sb,
202 + struct dentry *parent,
205 + struct dentry *dentry;
206 + struct inode *inode;
210 + qname.len = strlen (name);
211 + qname.hash = full_name_hash(name, qname.len);
212 + dentry = d_alloc(parent, &qname);
216 + inode = ibmasmfs_make_inode(sb, S_IFDIR | 0500);
222 + inode->i_op = &simple_dir_inode_operations;
223 + inode->i_fop = ibmasmfs_dir_ops;
225 + d_add(dentry, inode);
229 +int ibmasmfs_register()
231 + return register_filesystem(&ibmasmfs_type);
234 +void ibmasmfs_unregister()
236 + unregister_filesystem(&ibmasmfs_type);
239 +void ibmasmfs_add_sp(struct service_processor *sp)
241 + list_add(&sp->node, &service_processors);
244 +/* struct to save state between command file operations */
245 +struct ibmasmfs_command_data {
246 + struct service_processor *sp;
247 + struct command *command;
250 +/* struct to save state between event file operations */
251 +struct ibmasmfs_event_data {
252 + struct service_processor *sp;
253 + struct event_reader reader;
257 +/* struct to save state between reverse heartbeat file operations */
258 +struct ibmasmfs_heartbeat_data {
259 + struct service_processor *sp;
260 + struct reverse_heartbeat heartbeat;
265 +static int command_file_open(struct inode *inode, struct file *file)
267 + struct ibmasmfs_command_data *command_data;
269 + if ( !inode->u.generic_ip )
272 + command_data = kmalloc(sizeof(struct ibmasmfs_command_data), GFP_KERNEL);
273 + if ( !command_data )
276 + command_data->command = NULL;
277 + command_data->sp = inode->u.generic_ip;
278 + file->private_data = command_data;
282 +static int command_file_close(struct inode *inode, struct file *file)
284 + struct ibmasmfs_command_data *command_data = file->private_data;
286 + if (command_data->command)
287 + command_put(command_data->command);
289 + kfree(command_data);
293 +static ssize_t command_file_read(struct file *file, char *buf, size_t count, loff_t *offset)
295 + struct ibmasmfs_command_data *command_data = file->private_data;
296 + struct command *cmd;
298 + unsigned long flags;
302 + if (count == 0 || count > IBMASM_CMD_MAX_BUFFER_SIZE)
307 + spin_lock_irqsave(&command_data->sp->lock, flags);
308 + cmd = command_data->command;
310 + spin_unlock_irqrestore(&command_data->sp->lock, flags);
313 + command_data->command = NULL;
314 + spin_unlock_irqrestore(&command_data->sp->lock, flags);
316 + if (cmd->status != IBMASM_CMD_COMPLETE) {
320 + len = min(count, cmd->buffer_size);
321 + if ( copy_to_user(buf, cmd->buffer, len) ) {
330 +static ssize_t command_file_write(struct file *file, const char *ubuff, size_t count, loff_t *offset)
332 + struct ibmasmfs_command_data *command_data = file->private_data;
333 + struct command *cmd;
334 + unsigned long flags;
338 + if (count == 0 || count > IBMASM_CMD_MAX_BUFFER_SIZE)
343 + /* commands are executed sequentially, only one command at a time */
344 + if (command_data->command)
347 + cmd = ibmasm_new_command(count);
351 + if ( copy_from_user((void *)cmd->buffer, (void *)ubuff, count) ) {
356 + spin_lock_irqsave(&command_data->sp->lock, flags);
357 + if (command_data->command) {
358 + spin_unlock_irqrestore(&command_data->sp->lock, flags);
362 + command_data->command = cmd;
363 + spin_unlock_irqrestore(&command_data->sp->lock, flags);
365 + ibmasm_exec_command(command_data->sp, cmd);
366 + ibmasm_wait_for_response(cmd, get_dot_command_timeout(cmd->buffer));
371 +static int command_file_ioctl(struct inode *inode, struct file *file, unsigned int ioctl_cmd, unsigned long arg)
373 + struct ibmasmfs_command_data *command_data = file->private_data;
374 + struct command *cmd;
375 + unsigned long flags;
377 + if (ioctl_cmd != IBMASM_IO_CANCEL)
380 + spin_lock_irqsave(&command_data->sp->lock, flags);
381 + cmd = command_data->command;
383 + spin_unlock_irqrestore(&command_data->sp->lock, flags);
386 + wake_up_interruptible(&cmd->wait);
387 + spin_lock_irqsave(&command_data->sp->lock, flags);
392 +static int event_file_open(struct inode *inode, struct file *file)
394 + struct ibmasmfs_event_data *event_data;
395 + struct service_processor *sp;
397 + if ( !inode->u.generic_ip )
400 + sp = inode->u.generic_ip;
402 + event_data = kmalloc(sizeof(struct ibmasmfs_event_data), GFP_KERNEL);
406 + ibmasm_event_reader_register(sp, &event_data->reader);
408 + event_data->sp = sp;
409 + file->private_data = event_data;
413 +static int event_file_close(struct inode *inode, struct file *file)
415 + struct ibmasmfs_event_data *event_data = file->private_data;
417 + ibmasm_event_reader_unregister(event_data->sp, &event_data->reader);
422 +static ssize_t event_file_read(struct file *file, char *buf, size_t count, loff_t *offset)
424 + struct ibmasmfs_event_data *event_data = file->private_data;
425 + struct event_reader *reader = &event_data->reader;
430 + if (count == 0 || count > IBMASM_EVENT_MAX_SIZE)
435 + ret = ibmasm_get_next_event(event_data->sp, reader);
439 + if (count < reader->data_size)
442 + if (copy_to_user(buf, reader->data, reader->data_size))
445 + return reader->data_size;
448 +static int event_file_ioctl(struct inode *inode, struct file *file, unsigned int ioctl_cmd, unsigned long arg)
450 + struct ibmasmfs_event_data *event_data = file->private_data;
452 + if (ioctl_cmd != IBMASM_IO_CANCEL)
455 + wake_up_interruptible(&event_data->reader.wait);
459 +static int r_heartbeat_file_open(struct inode *inode, struct file *file)
461 + struct ibmasmfs_heartbeat_data *rhbeat;
463 + if ( !inode->u.generic_ip )
466 + rhbeat = kmalloc(sizeof(struct ibmasmfs_heartbeat_data), GFP_KERNEL);
470 + rhbeat->sp = (struct service_processor *)inode->u.generic_ip;
471 + rhbeat->lock = SPIN_LOCK_UNLOCKED;
472 + rhbeat->active = 0;
473 + ibmasm_init_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat);
474 + file->private_data = rhbeat;
478 +static int r_heartbeat_file_close(struct inode *inode, struct file *file)
480 + struct ibmasmfs_heartbeat_data *rhbeat = file->private_data;
486 +static ssize_t r_heartbeat_file_read(struct file *file, char *buf, size_t count, loff_t *offset)
488 + struct ibmasmfs_heartbeat_data *rhbeat = file->private_data;
489 + unsigned long flags;
494 + if (count == 0 || count > 1024)
499 + /* allow only one reverse heartbeat per process */
500 + spin_lock_irqsave(&rhbeat->lock, flags);
501 + if (rhbeat->active) {
502 + spin_unlock_irqrestore(&rhbeat->lock, flags);
505 + rhbeat->active = 1;
506 + spin_unlock_irqrestore(&rhbeat->lock, flags);
508 + result = ibmasm_start_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat);
510 + spin_lock_irqsave(&rhbeat->lock, flags);
511 + rhbeat->active = 0;
512 + ibmasm_init_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat);
513 + spin_unlock_irqrestore(&rhbeat->lock, flags);
518 +static int r_heartbeat_file_ioctl(struct inode *inode, struct file *file, unsigned int ioctl_cmd, unsigned long arg)
520 + struct ibmasmfs_heartbeat_data *rhbeat = file->private_data;
521 + unsigned long flags;
523 + if (ioctl_cmd != IBMASM_IO_CANCEL)
526 + spin_lock_irqsave(&rhbeat->lock, flags);
527 + if (!rhbeat->active) {
528 + spin_unlock_irqrestore(&rhbeat->lock, flags);
531 + spin_unlock_irqrestore(&rhbeat->lock, flags);
533 + ibmasm_stop_reverse_heartbeat(&rhbeat->heartbeat);
537 +static int remote_settings_file_open(struct inode *inode, struct file *file)
539 + file->private_data = inode->u.generic_ip;
543 +static int remote_settings_file_close(struct inode *inode, struct file *file)
548 +static ssize_t remote_settings_file_read(struct file *file, char *buf, size_t count, loff_t *offset)
550 + unsigned long address = (unsigned long)file->private_data;
551 + unsigned char *page;
554 + unsigned int value;
558 + if (count == 0 || count > 1024)
563 + page = (unsigned char *)__get_free_page(GFP_KERNEL);
567 + value = readl(address);
568 + len = sprintf(page, "%d\n", value);
570 + if (copy_to_user(buf, page, len)) {
578 + free_page((unsigned long)page);
582 +static ssize_t remote_settings_file_write(struct file *file, const char *ubuff, size_t count, loff_t *offset)
584 + unsigned long address = (unsigned long)file->private_data;
586 + unsigned int value;
590 + if (count == 0 || count > 1024)
595 + buff = kmalloc (count + 1, GFP_KERNEL);
599 + memset(buff, 0x0, count + 1);
601 + if ( copy_from_user((void *)buff, (void *)ubuff, count) ) {
606 + value = simple_strtoul(buff, NULL, 10);
607 + writel(value, address);
613 +static int remote_event_file_open(struct inode *inode, struct file *file)
615 + struct service_processor *sp;
616 + unsigned long flags;
617 + struct remote_queue *q;
619 + file->private_data = inode->u.generic_ip;
620 + sp = file->private_data;
621 + q = &sp->remote_queue;
623 + /* allow only one event reader */
624 + spin_lock_irqsave(&sp->lock, flags);
626 + spin_unlock_irqrestore(&sp->lock, flags);
630 + spin_unlock_irqrestore(&sp->lock, flags);
632 + enable_mouse_interrupts(sp);
637 +static int remote_event_file_close(struct inode *inode, struct file *file)
639 + struct service_processor *sp = file->private_data;
641 + disable_mouse_interrupts(sp);
642 + wake_up_interruptible(&sp->remote_queue.wait);
643 + sp->remote_queue.open = 0;
648 +static ssize_t remote_event_file_read(struct file *file, char *buf, size_t count, loff_t *offset)
650 + struct service_processor *sp = file->private_data;
651 + struct remote_queue *q = &sp->remote_queue;
653 + struct remote_event *reader = q->reader;
656 + if (wait_event_interruptible(q->wait, q->reader != q->writer))
657 + return -ERESTARTSYS;
659 + /* only get multiples of struct remote_event */
660 + num_events = min((count/sizeof(struct remote_event)), ibmasm_events_available(q));
664 + data_size = num_events * sizeof(struct remote_event);
666 + if (copy_to_user(buf, reader, data_size))
669 + ibmasm_advance_reader(q, num_events);
675 +static struct file_operations command_fops = {
676 + .open = command_file_open,
677 + .release = command_file_close,
678 + .read = command_file_read,
679 + .write = command_file_write,
680 + .ioctl = command_file_ioctl,
683 +static struct file_operations event_fops = {
684 + .open = event_file_open,
685 + .release = event_file_close,
686 + .read = event_file_read,
687 + .ioctl event_file_ioctl,
690 +static struct file_operations r_heartbeat_fops = {
691 + .open = r_heartbeat_file_open,
692 + .release = r_heartbeat_file_close,
693 + .read = r_heartbeat_file_read,
694 + .ioctl = r_heartbeat_file_ioctl,
697 +static struct file_operations remote_settings_fops = {
698 + .open = remote_settings_file_open,
699 + .release = remote_settings_file_close,
700 + .read = remote_settings_file_read,
701 + .write = remote_settings_file_write,
704 +static struct file_operations remote_event_fops = {
705 + .open = remote_event_file_open,
706 + .release = remote_event_file_close,
707 + .read = remote_event_file_read,
711 +static void ibmasmfs_create_files (struct super_block *sb, struct dentry *root)
713 + struct list_head *entry;
714 + struct service_processor *sp;
716 + list_for_each(entry, &service_processors) {
717 + struct dentry *dir;
718 + struct dentry *remote_dir;
719 + sp = list_entry(entry, struct service_processor, node);
720 + dir = ibmasmfs_create_dir(sb, root, sp->dirname);
724 + ibmasmfs_create_file(sb, dir, "command", &command_fops, sp, 0600);
725 + ibmasmfs_create_file(sb, dir, "event", &event_fops, sp, 0400);
726 + ibmasmfs_create_file(sb, dir, "reverse_heartbeat", &r_heartbeat_fops, sp, 0400);
728 + remote_dir = ibmasmfs_create_dir(sb, dir, "remote_video");
732 + ibmasmfs_create_file(sb, remote_dir, "width", &remote_settings_fops, (void *)display_width(sp), 0600);
733 + ibmasmfs_create_file(sb, remote_dir, "height", &remote_settings_fops, (void *)display_height(sp), 0600);
734 + ibmasmfs_create_file(sb, remote_dir, "depth", &remote_settings_fops, (void *)display_depth(sp), 0600);
735 + ibmasmfs_create_file(sb, remote_dir, "connected", &remote_settings_fops, (void *)vnc_status(sp), 0400);
736 + ibmasmfs_create_file(sb, remote_dir, "events", &remote_event_fops, (void *)sp, 0400);