162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * IBM ASM Service Processor Device Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) IBM Corporation, 2004 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Max Asböck <amax@us.ibm.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci/* 1162306a36Sopenharmony_ci * Parts of this code are based on an article by Jonathan Corbet 1262306a36Sopenharmony_ci * that appeared in Linux Weekly News. 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* 1762306a36Sopenharmony_ci * The IBMASM file virtual filesystem. It creates the following hierarchy 1862306a36Sopenharmony_ci * dynamically when mounted from user space: 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * /ibmasm 2162306a36Sopenharmony_ci * |-- 0 2262306a36Sopenharmony_ci * | |-- command 2362306a36Sopenharmony_ci * | |-- event 2462306a36Sopenharmony_ci * | |-- reverse_heartbeat 2562306a36Sopenharmony_ci * | `-- remote_video 2662306a36Sopenharmony_ci * | |-- depth 2762306a36Sopenharmony_ci * | |-- height 2862306a36Sopenharmony_ci * | `-- width 2962306a36Sopenharmony_ci * . 3062306a36Sopenharmony_ci * . 3162306a36Sopenharmony_ci * . 3262306a36Sopenharmony_ci * `-- n 3362306a36Sopenharmony_ci * |-- command 3462306a36Sopenharmony_ci * |-- event 3562306a36Sopenharmony_ci * |-- reverse_heartbeat 3662306a36Sopenharmony_ci * `-- remote_video 3762306a36Sopenharmony_ci * |-- depth 3862306a36Sopenharmony_ci * |-- height 3962306a36Sopenharmony_ci * `-- width 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * For each service processor the following files are created: 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci * command: execute dot commands 4462306a36Sopenharmony_ci * write: execute a dot command on the service processor 4562306a36Sopenharmony_ci * read: return the result of a previously executed dot command 4662306a36Sopenharmony_ci * 4762306a36Sopenharmony_ci * events: listen for service processor events 4862306a36Sopenharmony_ci * read: sleep (interruptible) until an event occurs 4962306a36Sopenharmony_ci * write: wakeup sleeping event listener 5062306a36Sopenharmony_ci * 5162306a36Sopenharmony_ci * reverse_heartbeat: send a heartbeat to the service processor 5262306a36Sopenharmony_ci * read: sleep (interruptible) until the reverse heartbeat fails 5362306a36Sopenharmony_ci * write: wakeup sleeping heartbeat listener 5462306a36Sopenharmony_ci * 5562306a36Sopenharmony_ci * remote_video/width 5662306a36Sopenharmony_ci * remote_video/height 5762306a36Sopenharmony_ci * remote_video/width: control remote display settings 5862306a36Sopenharmony_ci * write: set value 5962306a36Sopenharmony_ci * read: read value 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#include <linux/fs.h> 6362306a36Sopenharmony_ci#include <linux/fs_context.h> 6462306a36Sopenharmony_ci#include <linux/pagemap.h> 6562306a36Sopenharmony_ci#include <linux/slab.h> 6662306a36Sopenharmony_ci#include <linux/uaccess.h> 6762306a36Sopenharmony_ci#include <asm/io.h> 6862306a36Sopenharmony_ci#include "ibmasm.h" 6962306a36Sopenharmony_ci#include "remote.h" 7062306a36Sopenharmony_ci#include "dot_command.h" 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#define IBMASMFS_MAGIC 0x66726f67 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic LIST_HEAD(service_processors); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode); 7762306a36Sopenharmony_cistatic void ibmasmfs_create_files (struct super_block *sb); 7862306a36Sopenharmony_cistatic int ibmasmfs_fill_super(struct super_block *sb, struct fs_context *fc); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic int ibmasmfs_get_tree(struct fs_context *fc) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci return get_tree_single(fc, ibmasmfs_fill_super); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic const struct fs_context_operations ibmasmfs_context_ops = { 8662306a36Sopenharmony_ci .get_tree = ibmasmfs_get_tree, 8762306a36Sopenharmony_ci}; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic int ibmasmfs_init_fs_context(struct fs_context *fc) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci fc->ops = &ibmasmfs_context_ops; 9262306a36Sopenharmony_ci return 0; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic const struct super_operations ibmasmfs_s_ops = { 9662306a36Sopenharmony_ci .statfs = simple_statfs, 9762306a36Sopenharmony_ci .drop_inode = generic_delete_inode, 9862306a36Sopenharmony_ci}; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic const struct file_operations *ibmasmfs_dir_ops = &simple_dir_operations; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic struct file_system_type ibmasmfs_type = { 10362306a36Sopenharmony_ci .owner = THIS_MODULE, 10462306a36Sopenharmony_ci .name = "ibmasmfs", 10562306a36Sopenharmony_ci .init_fs_context = ibmasmfs_init_fs_context, 10662306a36Sopenharmony_ci .kill_sb = kill_litter_super, 10762306a36Sopenharmony_ci}; 10862306a36Sopenharmony_ciMODULE_ALIAS_FS("ibmasmfs"); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic int ibmasmfs_fill_super(struct super_block *sb, struct fs_context *fc) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci struct inode *root; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci sb->s_blocksize = PAGE_SIZE; 11562306a36Sopenharmony_ci sb->s_blocksize_bits = PAGE_SHIFT; 11662306a36Sopenharmony_ci sb->s_magic = IBMASMFS_MAGIC; 11762306a36Sopenharmony_ci sb->s_op = &ibmasmfs_s_ops; 11862306a36Sopenharmony_ci sb->s_time_gran = 1; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci root = ibmasmfs_make_inode (sb, S_IFDIR | 0500); 12162306a36Sopenharmony_ci if (!root) 12262306a36Sopenharmony_ci return -ENOMEM; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci root->i_op = &simple_dir_inode_operations; 12562306a36Sopenharmony_ci root->i_fop = ibmasmfs_dir_ops; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci sb->s_root = d_make_root(root); 12862306a36Sopenharmony_ci if (!sb->s_root) 12962306a36Sopenharmony_ci return -ENOMEM; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci ibmasmfs_create_files(sb); 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct inode *ret = new_inode(sb); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (ret) { 14062306a36Sopenharmony_ci ret->i_ino = get_next_ino(); 14162306a36Sopenharmony_ci ret->i_mode = mode; 14262306a36Sopenharmony_ci ret->i_atime = ret->i_mtime = inode_set_ctime_current(ret); 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci return ret; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic struct dentry *ibmasmfs_create_file(struct dentry *parent, 14862306a36Sopenharmony_ci const char *name, 14962306a36Sopenharmony_ci const struct file_operations *fops, 15062306a36Sopenharmony_ci void *data, 15162306a36Sopenharmony_ci int mode) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct dentry *dentry; 15462306a36Sopenharmony_ci struct inode *inode; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci dentry = d_alloc_name(parent, name); 15762306a36Sopenharmony_ci if (!dentry) 15862306a36Sopenharmony_ci return NULL; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci inode = ibmasmfs_make_inode(parent->d_sb, S_IFREG | mode); 16162306a36Sopenharmony_ci if (!inode) { 16262306a36Sopenharmony_ci dput(dentry); 16362306a36Sopenharmony_ci return NULL; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci inode->i_fop = fops; 16762306a36Sopenharmony_ci inode->i_private = data; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci d_add(dentry, inode); 17062306a36Sopenharmony_ci return dentry; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic struct dentry *ibmasmfs_create_dir(struct dentry *parent, 17462306a36Sopenharmony_ci const char *name) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct dentry *dentry; 17762306a36Sopenharmony_ci struct inode *inode; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci dentry = d_alloc_name(parent, name); 18062306a36Sopenharmony_ci if (!dentry) 18162306a36Sopenharmony_ci return NULL; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci inode = ibmasmfs_make_inode(parent->d_sb, S_IFDIR | 0500); 18462306a36Sopenharmony_ci if (!inode) { 18562306a36Sopenharmony_ci dput(dentry); 18662306a36Sopenharmony_ci return NULL; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci inode->i_op = &simple_dir_inode_operations; 19062306a36Sopenharmony_ci inode->i_fop = ibmasmfs_dir_ops; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci d_add(dentry, inode); 19362306a36Sopenharmony_ci return dentry; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ciint ibmasmfs_register(void) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci return register_filesystem(&ibmasmfs_type); 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_civoid ibmasmfs_unregister(void) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci unregister_filesystem(&ibmasmfs_type); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_civoid ibmasmfs_add_sp(struct service_processor *sp) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci list_add(&sp->node, &service_processors); 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci/* struct to save state between command file operations */ 21262306a36Sopenharmony_cistruct ibmasmfs_command_data { 21362306a36Sopenharmony_ci struct service_processor *sp; 21462306a36Sopenharmony_ci struct command *command; 21562306a36Sopenharmony_ci}; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci/* struct to save state between event file operations */ 21862306a36Sopenharmony_cistruct ibmasmfs_event_data { 21962306a36Sopenharmony_ci struct service_processor *sp; 22062306a36Sopenharmony_ci struct event_reader reader; 22162306a36Sopenharmony_ci int active; 22262306a36Sopenharmony_ci}; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci/* struct to save state between reverse heartbeat file operations */ 22562306a36Sopenharmony_cistruct ibmasmfs_heartbeat_data { 22662306a36Sopenharmony_ci struct service_processor *sp; 22762306a36Sopenharmony_ci struct reverse_heartbeat heartbeat; 22862306a36Sopenharmony_ci int active; 22962306a36Sopenharmony_ci}; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic int command_file_open(struct inode *inode, struct file *file) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci struct ibmasmfs_command_data *command_data; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (!inode->i_private) 23662306a36Sopenharmony_ci return -ENODEV; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci command_data = kmalloc(sizeof(struct ibmasmfs_command_data), GFP_KERNEL); 23962306a36Sopenharmony_ci if (!command_data) 24062306a36Sopenharmony_ci return -ENOMEM; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci command_data->command = NULL; 24362306a36Sopenharmony_ci command_data->sp = inode->i_private; 24462306a36Sopenharmony_ci file->private_data = command_data; 24562306a36Sopenharmony_ci return 0; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic int command_file_close(struct inode *inode, struct file *file) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci struct ibmasmfs_command_data *command_data = file->private_data; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (command_data->command) 25362306a36Sopenharmony_ci command_put(command_data->command); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci kfree(command_data); 25662306a36Sopenharmony_ci return 0; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic ssize_t command_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci struct ibmasmfs_command_data *command_data = file->private_data; 26262306a36Sopenharmony_ci struct command *cmd; 26362306a36Sopenharmony_ci int len; 26462306a36Sopenharmony_ci unsigned long flags; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (*offset < 0) 26762306a36Sopenharmony_ci return -EINVAL; 26862306a36Sopenharmony_ci if (count == 0 || count > IBMASM_CMD_MAX_BUFFER_SIZE) 26962306a36Sopenharmony_ci return 0; 27062306a36Sopenharmony_ci if (*offset != 0) 27162306a36Sopenharmony_ci return 0; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci spin_lock_irqsave(&command_data->sp->lock, flags); 27462306a36Sopenharmony_ci cmd = command_data->command; 27562306a36Sopenharmony_ci if (cmd == NULL) { 27662306a36Sopenharmony_ci spin_unlock_irqrestore(&command_data->sp->lock, flags); 27762306a36Sopenharmony_ci return 0; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci command_data->command = NULL; 28062306a36Sopenharmony_ci spin_unlock_irqrestore(&command_data->sp->lock, flags); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (cmd->status != IBMASM_CMD_COMPLETE) { 28362306a36Sopenharmony_ci command_put(cmd); 28462306a36Sopenharmony_ci return -EIO; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci len = min(count, cmd->buffer_size); 28762306a36Sopenharmony_ci if (copy_to_user(buf, cmd->buffer, len)) { 28862306a36Sopenharmony_ci command_put(cmd); 28962306a36Sopenharmony_ci return -EFAULT; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci command_put(cmd); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci return len; 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic ssize_t command_file_write(struct file *file, const char __user *ubuff, size_t count, loff_t *offset) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct ibmasmfs_command_data *command_data = file->private_data; 29962306a36Sopenharmony_ci struct command *cmd; 30062306a36Sopenharmony_ci unsigned long flags; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (*offset < 0) 30362306a36Sopenharmony_ci return -EINVAL; 30462306a36Sopenharmony_ci if (count == 0 || count > IBMASM_CMD_MAX_BUFFER_SIZE) 30562306a36Sopenharmony_ci return 0; 30662306a36Sopenharmony_ci if (*offset != 0) 30762306a36Sopenharmony_ci return 0; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* commands are executed sequentially, only one command at a time */ 31062306a36Sopenharmony_ci if (command_data->command) 31162306a36Sopenharmony_ci return -EAGAIN; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci cmd = ibmasm_new_command(command_data->sp, count); 31462306a36Sopenharmony_ci if (!cmd) 31562306a36Sopenharmony_ci return -ENOMEM; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (copy_from_user(cmd->buffer, ubuff, count)) { 31862306a36Sopenharmony_ci command_put(cmd); 31962306a36Sopenharmony_ci return -EFAULT; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci spin_lock_irqsave(&command_data->sp->lock, flags); 32362306a36Sopenharmony_ci if (command_data->command) { 32462306a36Sopenharmony_ci spin_unlock_irqrestore(&command_data->sp->lock, flags); 32562306a36Sopenharmony_ci command_put(cmd); 32662306a36Sopenharmony_ci return -EAGAIN; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci command_data->command = cmd; 32962306a36Sopenharmony_ci spin_unlock_irqrestore(&command_data->sp->lock, flags); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci ibmasm_exec_command(command_data->sp, cmd); 33262306a36Sopenharmony_ci ibmasm_wait_for_response(cmd, get_dot_command_timeout(cmd->buffer)); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci return count; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic int event_file_open(struct inode *inode, struct file *file) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci struct ibmasmfs_event_data *event_data; 34062306a36Sopenharmony_ci struct service_processor *sp; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (!inode->i_private) 34362306a36Sopenharmony_ci return -ENODEV; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci sp = inode->i_private; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci event_data = kmalloc(sizeof(struct ibmasmfs_event_data), GFP_KERNEL); 34862306a36Sopenharmony_ci if (!event_data) 34962306a36Sopenharmony_ci return -ENOMEM; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci ibmasm_event_reader_register(sp, &event_data->reader); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci event_data->sp = sp; 35462306a36Sopenharmony_ci event_data->active = 0; 35562306a36Sopenharmony_ci file->private_data = event_data; 35662306a36Sopenharmony_ci return 0; 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic int event_file_close(struct inode *inode, struct file *file) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct ibmasmfs_event_data *event_data = file->private_data; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci ibmasm_event_reader_unregister(event_data->sp, &event_data->reader); 36462306a36Sopenharmony_ci kfree(event_data); 36562306a36Sopenharmony_ci return 0; 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic ssize_t event_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci struct ibmasmfs_event_data *event_data = file->private_data; 37162306a36Sopenharmony_ci struct event_reader *reader = &event_data->reader; 37262306a36Sopenharmony_ci struct service_processor *sp = event_data->sp; 37362306a36Sopenharmony_ci int ret; 37462306a36Sopenharmony_ci unsigned long flags; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (*offset < 0) 37762306a36Sopenharmony_ci return -EINVAL; 37862306a36Sopenharmony_ci if (count == 0 || count > IBMASM_EVENT_MAX_SIZE) 37962306a36Sopenharmony_ci return 0; 38062306a36Sopenharmony_ci if (*offset != 0) 38162306a36Sopenharmony_ci return 0; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci spin_lock_irqsave(&sp->lock, flags); 38462306a36Sopenharmony_ci if (event_data->active) { 38562306a36Sopenharmony_ci spin_unlock_irqrestore(&sp->lock, flags); 38662306a36Sopenharmony_ci return -EBUSY; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci event_data->active = 1; 38962306a36Sopenharmony_ci spin_unlock_irqrestore(&sp->lock, flags); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci ret = ibmasm_get_next_event(sp, reader); 39262306a36Sopenharmony_ci if (ret <= 0) 39362306a36Sopenharmony_ci goto out; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (count < reader->data_size) { 39662306a36Sopenharmony_ci ret = -EINVAL; 39762306a36Sopenharmony_ci goto out; 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (copy_to_user(buf, reader->data, reader->data_size)) { 40162306a36Sopenharmony_ci ret = -EFAULT; 40262306a36Sopenharmony_ci goto out; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci ret = reader->data_size; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ciout: 40762306a36Sopenharmony_ci event_data->active = 0; 40862306a36Sopenharmony_ci return ret; 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic ssize_t event_file_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci struct ibmasmfs_event_data *event_data = file->private_data; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (*offset < 0) 41662306a36Sopenharmony_ci return -EINVAL; 41762306a36Sopenharmony_ci if (count != 1) 41862306a36Sopenharmony_ci return 0; 41962306a36Sopenharmony_ci if (*offset != 0) 42062306a36Sopenharmony_ci return 0; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci ibmasm_cancel_next_event(&event_data->reader); 42362306a36Sopenharmony_ci return 0; 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic int r_heartbeat_file_open(struct inode *inode, struct file *file) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct ibmasmfs_heartbeat_data *rhbeat; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (!inode->i_private) 43162306a36Sopenharmony_ci return -ENODEV; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci rhbeat = kmalloc(sizeof(struct ibmasmfs_heartbeat_data), GFP_KERNEL); 43462306a36Sopenharmony_ci if (!rhbeat) 43562306a36Sopenharmony_ci return -ENOMEM; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci rhbeat->sp = inode->i_private; 43862306a36Sopenharmony_ci rhbeat->active = 0; 43962306a36Sopenharmony_ci ibmasm_init_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat); 44062306a36Sopenharmony_ci file->private_data = rhbeat; 44162306a36Sopenharmony_ci return 0; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic int r_heartbeat_file_close(struct inode *inode, struct file *file) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci struct ibmasmfs_heartbeat_data *rhbeat = file->private_data; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci kfree(rhbeat); 44962306a36Sopenharmony_ci return 0; 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic ssize_t r_heartbeat_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci struct ibmasmfs_heartbeat_data *rhbeat = file->private_data; 45562306a36Sopenharmony_ci unsigned long flags; 45662306a36Sopenharmony_ci int result; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (*offset < 0) 45962306a36Sopenharmony_ci return -EINVAL; 46062306a36Sopenharmony_ci if (count == 0 || count > 1024) 46162306a36Sopenharmony_ci return 0; 46262306a36Sopenharmony_ci if (*offset != 0) 46362306a36Sopenharmony_ci return 0; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci /* allow only one reverse heartbeat per process */ 46662306a36Sopenharmony_ci spin_lock_irqsave(&rhbeat->sp->lock, flags); 46762306a36Sopenharmony_ci if (rhbeat->active) { 46862306a36Sopenharmony_ci spin_unlock_irqrestore(&rhbeat->sp->lock, flags); 46962306a36Sopenharmony_ci return -EBUSY; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci rhbeat->active = 1; 47262306a36Sopenharmony_ci spin_unlock_irqrestore(&rhbeat->sp->lock, flags); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci result = ibmasm_start_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat); 47562306a36Sopenharmony_ci rhbeat->active = 0; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci return result; 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic ssize_t r_heartbeat_file_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci struct ibmasmfs_heartbeat_data *rhbeat = file->private_data; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if (*offset < 0) 48562306a36Sopenharmony_ci return -EINVAL; 48662306a36Sopenharmony_ci if (count != 1) 48762306a36Sopenharmony_ci return 0; 48862306a36Sopenharmony_ci if (*offset != 0) 48962306a36Sopenharmony_ci return 0; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (rhbeat->active) 49262306a36Sopenharmony_ci ibmasm_stop_reverse_heartbeat(&rhbeat->heartbeat); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci return 1; 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic int remote_settings_file_close(struct inode *inode, struct file *file) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci return 0; 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic ssize_t remote_settings_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci void __iomem *address = (void __iomem *)file->private_data; 50562306a36Sopenharmony_ci int len = 0; 50662306a36Sopenharmony_ci unsigned int value; 50762306a36Sopenharmony_ci char lbuf[20]; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci value = readl(address); 51062306a36Sopenharmony_ci len = snprintf(lbuf, sizeof(lbuf), "%d\n", value); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci return simple_read_from_buffer(buf, count, offset, lbuf, len); 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic ssize_t remote_settings_file_write(struct file *file, const char __user *ubuff, size_t count, loff_t *offset) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci void __iomem *address = (void __iomem *)file->private_data; 51862306a36Sopenharmony_ci char *buff; 51962306a36Sopenharmony_ci unsigned int value; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (*offset < 0) 52262306a36Sopenharmony_ci return -EINVAL; 52362306a36Sopenharmony_ci if (count == 0 || count > 1024) 52462306a36Sopenharmony_ci return 0; 52562306a36Sopenharmony_ci if (*offset != 0) 52662306a36Sopenharmony_ci return 0; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci buff = kzalloc (count + 1, GFP_KERNEL); 52962306a36Sopenharmony_ci if (!buff) 53062306a36Sopenharmony_ci return -ENOMEM; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (copy_from_user(buff, ubuff, count)) { 53462306a36Sopenharmony_ci kfree(buff); 53562306a36Sopenharmony_ci return -EFAULT; 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci value = simple_strtoul(buff, NULL, 10); 53962306a36Sopenharmony_ci writel(value, address); 54062306a36Sopenharmony_ci kfree(buff); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci return count; 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic const struct file_operations command_fops = { 54662306a36Sopenharmony_ci .open = command_file_open, 54762306a36Sopenharmony_ci .release = command_file_close, 54862306a36Sopenharmony_ci .read = command_file_read, 54962306a36Sopenharmony_ci .write = command_file_write, 55062306a36Sopenharmony_ci .llseek = generic_file_llseek, 55162306a36Sopenharmony_ci}; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic const struct file_operations event_fops = { 55462306a36Sopenharmony_ci .open = event_file_open, 55562306a36Sopenharmony_ci .release = event_file_close, 55662306a36Sopenharmony_ci .read = event_file_read, 55762306a36Sopenharmony_ci .write = event_file_write, 55862306a36Sopenharmony_ci .llseek = generic_file_llseek, 55962306a36Sopenharmony_ci}; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cistatic const struct file_operations r_heartbeat_fops = { 56262306a36Sopenharmony_ci .open = r_heartbeat_file_open, 56362306a36Sopenharmony_ci .release = r_heartbeat_file_close, 56462306a36Sopenharmony_ci .read = r_heartbeat_file_read, 56562306a36Sopenharmony_ci .write = r_heartbeat_file_write, 56662306a36Sopenharmony_ci .llseek = generic_file_llseek, 56762306a36Sopenharmony_ci}; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cistatic const struct file_operations remote_settings_fops = { 57062306a36Sopenharmony_ci .open = simple_open, 57162306a36Sopenharmony_ci .release = remote_settings_file_close, 57262306a36Sopenharmony_ci .read = remote_settings_file_read, 57362306a36Sopenharmony_ci .write = remote_settings_file_write, 57462306a36Sopenharmony_ci .llseek = generic_file_llseek, 57562306a36Sopenharmony_ci}; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cistatic void ibmasmfs_create_files (struct super_block *sb) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci struct list_head *entry; 58162306a36Sopenharmony_ci struct service_processor *sp; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci list_for_each(entry, &service_processors) { 58462306a36Sopenharmony_ci struct dentry *dir; 58562306a36Sopenharmony_ci struct dentry *remote_dir; 58662306a36Sopenharmony_ci sp = list_entry(entry, struct service_processor, node); 58762306a36Sopenharmony_ci dir = ibmasmfs_create_dir(sb->s_root, sp->dirname); 58862306a36Sopenharmony_ci if (!dir) 58962306a36Sopenharmony_ci continue; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci ibmasmfs_create_file(dir, "command", &command_fops, sp, S_IRUSR|S_IWUSR); 59262306a36Sopenharmony_ci ibmasmfs_create_file(dir, "event", &event_fops, sp, S_IRUSR|S_IWUSR); 59362306a36Sopenharmony_ci ibmasmfs_create_file(dir, "reverse_heartbeat", &r_heartbeat_fops, sp, S_IRUSR|S_IWUSR); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci remote_dir = ibmasmfs_create_dir(dir, "remote_video"); 59662306a36Sopenharmony_ci if (!remote_dir) 59762306a36Sopenharmony_ci continue; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci ibmasmfs_create_file(remote_dir, "width", &remote_settings_fops, (void *)display_width(sp), S_IRUSR|S_IWUSR); 60062306a36Sopenharmony_ci ibmasmfs_create_file(remote_dir, "height", &remote_settings_fops, (void *)display_height(sp), S_IRUSR|S_IWUSR); 60162306a36Sopenharmony_ci ibmasmfs_create_file(remote_dir, "depth", &remote_settings_fops, (void *)display_depth(sp), S_IRUSR|S_IWUSR); 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci} 604