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 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 100 static 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 .get_sb = ibmasmfs_get_super, 111 .kill_sb = kill_litter_super, 112 }; 113 114 static 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 143 static 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 154 static 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 181 static 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 205 int ibmasmfs_register(void) 206 { 207 return register_filesystem(&ibmasmfs_type); 208 } 209 210 void ibmasmfs_unregister(void) 211 { 212 unregister_filesystem(&ibmasmfs_type); 213 } 214 215 void 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 */ 221 struct ibmasmfs_command_data { 222 struct service_processor *sp; 223 struct command *command; 224 }; 225 226 /* struct to save state between event file operations */ 227 struct 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 */ 234 struct ibmasmfs_heartbeat_data { 235 struct service_processor *sp; 236 struct reverse_heartbeat heartbeat; 237 int active; 238 }; 239 240 static 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 257 static 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 268 static 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 305 static 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 346 static 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 368 static 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 377 static 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 415 out: 416 event_data->active = 0; 417 return ret; 418 } 419 420 static 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 435 static 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 453 static 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 461 static 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 489 static 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 506 static int remote_settings_file_open(struct inode *inode, struct file *file) 507 { 508 file->private_data = inode->i_private; 509 return 0; 510 } 511 512 static int remote_settings_file_close(struct inode *inode, struct file *file) 513 { 514 return 0; 515 } 516 517 static 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 546 exit: 547 free_page((unsigned long)page); 548 return retval; 549 } 550 551 static 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 581 static 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 588 static 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 595 static 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 602 static 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 610 static 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