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