Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v2.6.14 823 lines 22 kB view raw
1/* -*- linux-c -*- 2 * drivers/cdrom/viocd.c 3 * 4 * iSeries Virtual CD Rom 5 * 6 * Authors: Dave Boutcher <boutcher@us.ibm.com> 7 * Ryan Arnold <ryanarn@us.ibm.com> 8 * Colin Devilbiss <devilbis@us.ibm.com> 9 * Stephen Rothwell <sfr@au1.ibm.com> 10 * 11 * (C) Copyright 2000-2004 IBM Corporation 12 * 13 * This program is free software; you can redistribute it and/or 14 * modify it under the terms of the GNU General Public License as 15 * published by the Free Software Foundation; either version 2 of the 16 * License, or (at your option) anyu later version. 17 * 18 * This program is distributed in the hope that it will be useful, but 19 * WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 21 * General Public License for more details. 22 * 23 * You should have received a copy of the GNU General Public License 24 * along with this program; if not, write to the Free Software Foundation, 25 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 26 * 27 * This routine provides access to CD ROM drives owned and managed by an 28 * OS/400 partition running on the same box as this Linux partition. 29 * 30 * All operations are performed by sending messages back and forth to 31 * the OS/400 partition. 32 */ 33 34#include <linux/major.h> 35#include <linux/blkdev.h> 36#include <linux/cdrom.h> 37#include <linux/errno.h> 38#include <linux/init.h> 39#include <linux/dma-mapping.h> 40#include <linux/module.h> 41#include <linux/completion.h> 42#include <linux/proc_fs.h> 43#include <linux/seq_file.h> 44 45#include <asm/bug.h> 46 47#include <asm/vio.h> 48#include <asm/scatterlist.h> 49#include <asm/iSeries/HvTypes.h> 50#include <asm/iSeries/HvLpEvent.h> 51#include <asm/iSeries/vio.h> 52 53#define VIOCD_DEVICE "iseries/vcd" 54#define VIOCD_DEVICE_DEVFS "iseries/vcd" 55 56#define VIOCD_VERS "1.06" 57 58#define VIOCD_KERN_WARNING KERN_WARNING "viocd: " 59#define VIOCD_KERN_INFO KERN_INFO "viocd: " 60 61struct viocdlpevent { 62 struct HvLpEvent event; 63 u32 reserved; 64 u16 version; 65 u16 sub_result; 66 u16 disk; 67 u16 flags; 68 u32 token; 69 u64 offset; /* On open, max number of disks */ 70 u64 len; /* On open, size of the disk */ 71 u32 block_size; /* Only set on open */ 72 u32 media_size; /* Only set on open */ 73}; 74 75enum viocdsubtype { 76 viocdopen = 0x0001, 77 viocdclose = 0x0002, 78 viocdread = 0x0003, 79 viocdwrite = 0x0004, 80 viocdlockdoor = 0x0005, 81 viocdgetinfo = 0x0006, 82 viocdcheck = 0x0007 83}; 84 85/* 86 * Should probably make this a module parameter....sigh 87 */ 88#define VIOCD_MAX_CD HVMAXARCHITECTEDVIRTUALCDROMS 89 90static const struct vio_error_entry viocd_err_table[] = { 91 {0x0201, EINVAL, "Invalid Range"}, 92 {0x0202, EINVAL, "Invalid Token"}, 93 {0x0203, EIO, "DMA Error"}, 94 {0x0204, EIO, "Use Error"}, 95 {0x0205, EIO, "Release Error"}, 96 {0x0206, EINVAL, "Invalid CD"}, 97 {0x020C, EROFS, "Read Only Device"}, 98 {0x020D, ENOMEDIUM, "Changed or Missing Volume (or Varied Off?)"}, 99 {0x020E, EIO, "Optical System Error (Varied Off?)"}, 100 {0x02FF, EIO, "Internal Error"}, 101 {0x3010, EIO, "Changed Volume"}, 102 {0xC100, EIO, "Optical System Error"}, 103 {0x0000, 0, NULL}, 104}; 105 106/* 107 * This is the structure we use to exchange info between driver and interrupt 108 * handler 109 */ 110struct viocd_waitevent { 111 struct completion com; 112 int rc; 113 u16 sub_result; 114 int changed; 115}; 116 117/* this is a lookup table for the true capabilities of a device */ 118struct capability_entry { 119 char *type; 120 int capability; 121}; 122 123static struct capability_entry capability_table[] __initdata = { 124 { "6330", CDC_LOCK | CDC_DVD_RAM | CDC_RAM }, 125 { "6331", CDC_LOCK | CDC_DVD_RAM | CDC_RAM }, 126 { "6333", CDC_LOCK | CDC_DVD_RAM | CDC_RAM }, 127 { "632A", CDC_LOCK | CDC_DVD_RAM | CDC_RAM }, 128 { "6321", CDC_LOCK }, 129 { "632B", 0 }, 130 { NULL , CDC_LOCK }, 131}; 132 133/* These are our internal structures for keeping track of devices */ 134static int viocd_numdev; 135 136struct cdrom_info { 137 char rsrcname[10]; 138 char type[4]; 139 char model[3]; 140}; 141/* 142 * This needs to be allocated since it is passed to the 143 * Hypervisor and we may be a module. 144 */ 145static struct cdrom_info *viocd_unitinfo; 146static dma_addr_t unitinfo_dmaaddr; 147 148struct disk_info { 149 struct gendisk *viocd_disk; 150 struct cdrom_device_info viocd_info; 151 struct device *dev; 152}; 153static struct disk_info viocd_diskinfo[VIOCD_MAX_CD]; 154 155#define DEVICE_NR(di) ((di) - &viocd_diskinfo[0]) 156 157static spinlock_t viocd_reqlock; 158 159#define MAX_CD_REQ 1 160 161/* procfs support */ 162static int proc_viocd_show(struct seq_file *m, void *v) 163{ 164 int i; 165 166 for (i = 0; i < viocd_numdev; i++) { 167 seq_printf(m, "viocd device %d is iSeries resource %10.10s" 168 "type %4.4s, model %3.3s\n", 169 i, viocd_unitinfo[i].rsrcname, 170 viocd_unitinfo[i].type, 171 viocd_unitinfo[i].model); 172 } 173 return 0; 174} 175 176static int proc_viocd_open(struct inode *inode, struct file *file) 177{ 178 return single_open(file, proc_viocd_show, NULL); 179} 180 181static struct file_operations proc_viocd_operations = { 182 .open = proc_viocd_open, 183 .read = seq_read, 184 .llseek = seq_lseek, 185 .release = single_release, 186}; 187 188static int viocd_blk_open(struct inode *inode, struct file *file) 189{ 190 struct disk_info *di = inode->i_bdev->bd_disk->private_data; 191 return cdrom_open(&di->viocd_info, inode, file); 192} 193 194static int viocd_blk_release(struct inode *inode, struct file *file) 195{ 196 struct disk_info *di = inode->i_bdev->bd_disk->private_data; 197 return cdrom_release(&di->viocd_info, file); 198} 199 200static int viocd_blk_ioctl(struct inode *inode, struct file *file, 201 unsigned cmd, unsigned long arg) 202{ 203 struct disk_info *di = inode->i_bdev->bd_disk->private_data; 204 return cdrom_ioctl(file, &di->viocd_info, inode, cmd, arg); 205} 206 207static int viocd_blk_media_changed(struct gendisk *disk) 208{ 209 struct disk_info *di = disk->private_data; 210 return cdrom_media_changed(&di->viocd_info); 211} 212 213struct block_device_operations viocd_fops = { 214 .owner = THIS_MODULE, 215 .open = viocd_blk_open, 216 .release = viocd_blk_release, 217 .ioctl = viocd_blk_ioctl, 218 .media_changed = viocd_blk_media_changed, 219}; 220 221/* Get info on CD devices from OS/400 */ 222static void __init get_viocd_info(void) 223{ 224 HvLpEvent_Rc hvrc; 225 int i; 226 struct viocd_waitevent we; 227 228 viocd_unitinfo = dma_alloc_coherent(iSeries_vio_dev, 229 sizeof(*viocd_unitinfo) * VIOCD_MAX_CD, 230 &unitinfo_dmaaddr, GFP_ATOMIC); 231 if (viocd_unitinfo == NULL) { 232 printk(VIOCD_KERN_WARNING "error allocating unitinfo\n"); 233 return; 234 } 235 236 memset(viocd_unitinfo, 0, sizeof(*viocd_unitinfo) * VIOCD_MAX_CD); 237 238 init_completion(&we.com); 239 240 hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, 241 HvLpEvent_Type_VirtualIo, 242 viomajorsubtype_cdio | viocdgetinfo, 243 HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, 244 viopath_sourceinst(viopath_hostLp), 245 viopath_targetinst(viopath_hostLp), 246 (u64)&we, VIOVERSION << 16, unitinfo_dmaaddr, 0, 247 sizeof(*viocd_unitinfo) * VIOCD_MAX_CD, 0); 248 if (hvrc != HvLpEvent_Rc_Good) { 249 printk(VIOCD_KERN_WARNING "cdrom error sending event. rc %d\n", 250 (int)hvrc); 251 goto error_ret; 252 } 253 254 wait_for_completion(&we.com); 255 256 if (we.rc) { 257 const struct vio_error_entry *err = 258 vio_lookup_rc(viocd_err_table, we.sub_result); 259 printk(VIOCD_KERN_WARNING "bad rc %d:0x%04X on getinfo: %s\n", 260 we.rc, we.sub_result, err->msg); 261 goto error_ret; 262 } 263 264 for (i = 0; (i < VIOCD_MAX_CD) && viocd_unitinfo[i].rsrcname[0]; i++) 265 viocd_numdev++; 266 267error_ret: 268 if (viocd_numdev == 0) { 269 dma_free_coherent(iSeries_vio_dev, 270 sizeof(*viocd_unitinfo) * VIOCD_MAX_CD, 271 viocd_unitinfo, unitinfo_dmaaddr); 272 viocd_unitinfo = NULL; 273 } 274} 275 276static int viocd_open(struct cdrom_device_info *cdi, int purpose) 277{ 278 struct disk_info *diskinfo = cdi->handle; 279 int device_no = DEVICE_NR(diskinfo); 280 HvLpEvent_Rc hvrc; 281 struct viocd_waitevent we; 282 283 init_completion(&we.com); 284 hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, 285 HvLpEvent_Type_VirtualIo, 286 viomajorsubtype_cdio | viocdopen, 287 HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, 288 viopath_sourceinst(viopath_hostLp), 289 viopath_targetinst(viopath_hostLp), 290 (u64)&we, VIOVERSION << 16, ((u64)device_no << 48), 291 0, 0, 0); 292 if (hvrc != 0) { 293 printk(VIOCD_KERN_WARNING 294 "bad rc on HvCallEvent_signalLpEventFast %d\n", 295 (int)hvrc); 296 return -EIO; 297 } 298 299 wait_for_completion(&we.com); 300 301 if (we.rc) { 302 const struct vio_error_entry *err = 303 vio_lookup_rc(viocd_err_table, we.sub_result); 304 printk(VIOCD_KERN_WARNING "bad rc %d:0x%04X on open: %s\n", 305 we.rc, we.sub_result, err->msg); 306 return -err->errno; 307 } 308 309 return 0; 310} 311 312static void viocd_release(struct cdrom_device_info *cdi) 313{ 314 int device_no = DEVICE_NR((struct disk_info *)cdi->handle); 315 HvLpEvent_Rc hvrc; 316 317 hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, 318 HvLpEvent_Type_VirtualIo, 319 viomajorsubtype_cdio | viocdclose, 320 HvLpEvent_AckInd_NoAck, HvLpEvent_AckType_ImmediateAck, 321 viopath_sourceinst(viopath_hostLp), 322 viopath_targetinst(viopath_hostLp), 0, 323 VIOVERSION << 16, ((u64)device_no << 48), 0, 0, 0); 324 if (hvrc != 0) 325 printk(VIOCD_KERN_WARNING 326 "bad rc on HvCallEvent_signalLpEventFast %d\n", 327 (int)hvrc); 328} 329 330/* Send a read or write request to OS/400 */ 331static int send_request(struct request *req) 332{ 333 HvLpEvent_Rc hvrc; 334 struct disk_info *diskinfo = req->rq_disk->private_data; 335 u64 len; 336 dma_addr_t dmaaddr; 337 int direction; 338 u16 cmd; 339 struct scatterlist sg; 340 341 BUG_ON(req->nr_phys_segments > 1); 342 343 if (rq_data_dir(req) == READ) { 344 direction = DMA_FROM_DEVICE; 345 cmd = viomajorsubtype_cdio | viocdread; 346 } else { 347 direction = DMA_TO_DEVICE; 348 cmd = viomajorsubtype_cdio | viocdwrite; 349 } 350 351 if (blk_rq_map_sg(req->q, req, &sg) == 0) { 352 printk(VIOCD_KERN_WARNING 353 "error setting up scatter/gather list\n"); 354 return -1; 355 } 356 357 if (dma_map_sg(diskinfo->dev, &sg, 1, direction) == 0) { 358 printk(VIOCD_KERN_WARNING "error allocating sg tce\n"); 359 return -1; 360 } 361 dmaaddr = sg_dma_address(&sg); 362 len = sg_dma_len(&sg); 363 364 hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, 365 HvLpEvent_Type_VirtualIo, cmd, 366 HvLpEvent_AckInd_DoAck, 367 HvLpEvent_AckType_ImmediateAck, 368 viopath_sourceinst(viopath_hostLp), 369 viopath_targetinst(viopath_hostLp), 370 (u64)req, VIOVERSION << 16, 371 ((u64)DEVICE_NR(diskinfo) << 48) | dmaaddr, 372 (u64)req->sector * 512, len, 0); 373 if (hvrc != HvLpEvent_Rc_Good) { 374 printk(VIOCD_KERN_WARNING "hv error on op %d\n", (int)hvrc); 375 return -1; 376 } 377 378 return 0; 379} 380 381 382static int rwreq; 383 384static void do_viocd_request(request_queue_t *q) 385{ 386 struct request *req; 387 388 while ((rwreq == 0) && ((req = elv_next_request(q)) != NULL)) { 389 if (!blk_fs_request(req)) 390 end_request(req, 0); 391 else if (send_request(req) < 0) { 392 printk(VIOCD_KERN_WARNING 393 "unable to send message to OS/400!"); 394 end_request(req, 0); 395 } else 396 rwreq++; 397 } 398} 399 400static int viocd_media_changed(struct cdrom_device_info *cdi, int disc_nr) 401{ 402 struct viocd_waitevent we; 403 HvLpEvent_Rc hvrc; 404 int device_no = DEVICE_NR((struct disk_info *)cdi->handle); 405 406 init_completion(&we.com); 407 408 /* Send the open event to OS/400 */ 409 hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, 410 HvLpEvent_Type_VirtualIo, 411 viomajorsubtype_cdio | viocdcheck, 412 HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, 413 viopath_sourceinst(viopath_hostLp), 414 viopath_targetinst(viopath_hostLp), 415 (u64)&we, VIOVERSION << 16, ((u64)device_no << 48), 416 0, 0, 0); 417 if (hvrc != 0) { 418 printk(VIOCD_KERN_WARNING "bad rc on HvCallEvent_signalLpEventFast %d\n", 419 (int)hvrc); 420 return -EIO; 421 } 422 423 wait_for_completion(&we.com); 424 425 /* Check the return code. If bad, assume no change */ 426 if (we.rc) { 427 const struct vio_error_entry *err = 428 vio_lookup_rc(viocd_err_table, we.sub_result); 429 printk(VIOCD_KERN_WARNING 430 "bad rc %d:0x%04X on check_change: %s; Assuming no change\n", 431 we.rc, we.sub_result, err->msg); 432 return 0; 433 } 434 435 return we.changed; 436} 437 438static int viocd_lock_door(struct cdrom_device_info *cdi, int locking) 439{ 440 HvLpEvent_Rc hvrc; 441 u64 device_no = DEVICE_NR((struct disk_info *)cdi->handle); 442 /* NOTE: flags is 1 or 0 so it won't overwrite the device_no */ 443 u64 flags = !!locking; 444 struct viocd_waitevent we; 445 446 init_completion(&we.com); 447 448 /* Send the lockdoor event to OS/400 */ 449 hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, 450 HvLpEvent_Type_VirtualIo, 451 viomajorsubtype_cdio | viocdlockdoor, 452 HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, 453 viopath_sourceinst(viopath_hostLp), 454 viopath_targetinst(viopath_hostLp), 455 (u64)&we, VIOVERSION << 16, 456 (device_no << 48) | (flags << 32), 0, 0, 0); 457 if (hvrc != 0) { 458 printk(VIOCD_KERN_WARNING "bad rc on HvCallEvent_signalLpEventFast %d\n", 459 (int)hvrc); 460 return -EIO; 461 } 462 463 wait_for_completion(&we.com); 464 465 if (we.rc != 0) 466 return -EIO; 467 return 0; 468} 469 470static int viocd_packet(struct cdrom_device_info *cdi, 471 struct packet_command *cgc) 472{ 473 unsigned int buflen = cgc->buflen; 474 int ret = -EIO; 475 476 switch (cgc->cmd[0]) { 477 case GPCMD_READ_DISC_INFO: 478 { 479 disc_information *di = (disc_information *)cgc->buffer; 480 481 if (buflen >= 2) { 482 di->disc_information_length = cpu_to_be16(1); 483 ret = 0; 484 } 485 if (buflen >= 3) 486 di->erasable = 487 (cdi->ops->capability & ~cdi->mask 488 & (CDC_DVD_RAM | CDC_RAM)) != 0; 489 } 490 break; 491 case GPCMD_GET_CONFIGURATION: 492 if (cgc->cmd[3] == CDF_RWRT) { 493 struct rwrt_feature_desc *rfd = (struct rwrt_feature_desc *)(cgc->buffer + sizeof(struct feature_header)); 494 495 if ((buflen >= 496 (sizeof(struct feature_header) + sizeof(*rfd))) && 497 (cdi->ops->capability & ~cdi->mask 498 & (CDC_DVD_RAM | CDC_RAM))) { 499 rfd->feature_code = cpu_to_be16(CDF_RWRT); 500 rfd->curr = 1; 501 ret = 0; 502 } 503 } 504 break; 505 default: 506 if (cgc->sense) { 507 /* indicate Unknown code */ 508 cgc->sense->sense_key = 0x05; 509 cgc->sense->asc = 0x20; 510 cgc->sense->ascq = 0x00; 511 } 512 break; 513 } 514 515 cgc->stat = ret; 516 return ret; 517} 518 519static void restart_all_queues(int first_index) 520{ 521 int i; 522 523 for (i = first_index + 1; i < viocd_numdev; i++) 524 if (viocd_diskinfo[i].viocd_disk) 525 blk_run_queue(viocd_diskinfo[i].viocd_disk->queue); 526 for (i = 0; i <= first_index; i++) 527 if (viocd_diskinfo[i].viocd_disk) 528 blk_run_queue(viocd_diskinfo[i].viocd_disk->queue); 529} 530 531/* This routine handles incoming CD LP events */ 532static void vio_handle_cd_event(struct HvLpEvent *event) 533{ 534 struct viocdlpevent *bevent; 535 struct viocd_waitevent *pwe; 536 struct disk_info *di; 537 unsigned long flags; 538 struct request *req; 539 540 541 if (event == NULL) 542 /* Notification that a partition went away! */ 543 return; 544 /* First, we should NEVER get an int here...only acks */ 545 if (event->xFlags.xFunction == HvLpEvent_Function_Int) { 546 printk(VIOCD_KERN_WARNING 547 "Yikes! got an int in viocd event handler!\n"); 548 if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) { 549 event->xRc = HvLpEvent_Rc_InvalidSubtype; 550 HvCallEvent_ackLpEvent(event); 551 } 552 } 553 554 bevent = (struct viocdlpevent *)event; 555 556 switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) { 557 case viocdopen: 558 if (event->xRc == 0) { 559 di = &viocd_diskinfo[bevent->disk]; 560 blk_queue_hardsect_size(di->viocd_disk->queue, 561 bevent->block_size); 562 set_capacity(di->viocd_disk, 563 bevent->media_size * 564 bevent->block_size / 512); 565 } 566 /* FALLTHROUGH !! */ 567 case viocdgetinfo: 568 case viocdlockdoor: 569 pwe = (struct viocd_waitevent *)event->xCorrelationToken; 570return_complete: 571 pwe->rc = event->xRc; 572 pwe->sub_result = bevent->sub_result; 573 complete(&pwe->com); 574 break; 575 576 case viocdcheck: 577 pwe = (struct viocd_waitevent *)event->xCorrelationToken; 578 pwe->changed = bevent->flags; 579 goto return_complete; 580 581 case viocdclose: 582 break; 583 584 case viocdwrite: 585 case viocdread: 586 /* 587 * Since this is running in interrupt mode, we need to 588 * make sure we're not stepping on any global I/O operations 589 */ 590 di = &viocd_diskinfo[bevent->disk]; 591 spin_lock_irqsave(&viocd_reqlock, flags); 592 dma_unmap_single(di->dev, bevent->token, bevent->len, 593 ((event->xSubtype & VIOMINOR_SUBTYPE_MASK) == viocdread) 594 ? DMA_FROM_DEVICE : DMA_TO_DEVICE); 595 req = (struct request *)bevent->event.xCorrelationToken; 596 rwreq--; 597 598 if (event->xRc != HvLpEvent_Rc_Good) { 599 const struct vio_error_entry *err = 600 vio_lookup_rc(viocd_err_table, 601 bevent->sub_result); 602 printk(VIOCD_KERN_WARNING "request %p failed " 603 "with rc %d:0x%04X: %s\n", 604 req, event->xRc, 605 bevent->sub_result, err->msg); 606 end_request(req, 0); 607 } else 608 end_request(req, 1); 609 610 /* restart handling of incoming requests */ 611 spin_unlock_irqrestore(&viocd_reqlock, flags); 612 restart_all_queues(bevent->disk); 613 break; 614 615 default: 616 printk(VIOCD_KERN_WARNING 617 "message with invalid subtype %0x04X!\n", 618 event->xSubtype & VIOMINOR_SUBTYPE_MASK); 619 if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) { 620 event->xRc = HvLpEvent_Rc_InvalidSubtype; 621 HvCallEvent_ackLpEvent(event); 622 } 623 } 624} 625 626static struct cdrom_device_ops viocd_dops = { 627 .open = viocd_open, 628 .release = viocd_release, 629 .media_changed = viocd_media_changed, 630 .lock_door = viocd_lock_door, 631 .generic_packet = viocd_packet, 632 .capability = CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_SELECT_SPEED | CDC_SELECT_DISC | CDC_MULTI_SESSION | CDC_MCN | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | CDC_RESET | CDC_IOCTLS | CDC_DRIVE_STATUS | CDC_GENERIC_PACKET | CDC_CD_R | CDC_CD_RW | CDC_DVD | CDC_DVD_R | CDC_DVD_RAM | CDC_RAM 633}; 634 635static int __init find_capability(const char *type) 636{ 637 struct capability_entry *entry; 638 639 for(entry = capability_table; entry->type; ++entry) 640 if(!strncmp(entry->type, type, 4)) 641 break; 642 return entry->capability; 643} 644 645static int viocd_probe(struct vio_dev *vdev, const struct vio_device_id *id) 646{ 647 struct gendisk *gendisk; 648 int deviceno; 649 struct disk_info *d; 650 struct cdrom_device_info *c; 651 struct cdrom_info *ci; 652 struct request_queue *q; 653 654 deviceno = vdev->unit_address; 655 if (deviceno >= viocd_numdev) 656 return -ENODEV; 657 658 d = &viocd_diskinfo[deviceno]; 659 c = &d->viocd_info; 660 ci = &viocd_unitinfo[deviceno]; 661 662 c->ops = &viocd_dops; 663 c->speed = 4; 664 c->capacity = 1; 665 c->handle = d; 666 c->mask = ~find_capability(ci->type); 667 sprintf(c->name, VIOCD_DEVICE "%c", 'a' + deviceno); 668 669 if (register_cdrom(c) != 0) { 670 printk(VIOCD_KERN_WARNING "Cannot register viocd CD-ROM %s!\n", 671 c->name); 672 goto out; 673 } 674 printk(VIOCD_KERN_INFO "cd %s is iSeries resource %10.10s " 675 "type %4.4s, model %3.3s\n", 676 c->name, ci->rsrcname, ci->type, ci->model); 677 q = blk_init_queue(do_viocd_request, &viocd_reqlock); 678 if (q == NULL) { 679 printk(VIOCD_KERN_WARNING "Cannot allocate queue for %s!\n", 680 c->name); 681 goto out_unregister_cdrom; 682 } 683 gendisk = alloc_disk(1); 684 if (gendisk == NULL) { 685 printk(VIOCD_KERN_WARNING "Cannot create gendisk for %s!\n", 686 c->name); 687 goto out_cleanup_queue; 688 } 689 gendisk->major = VIOCD_MAJOR; 690 gendisk->first_minor = deviceno; 691 strncpy(gendisk->disk_name, c->name, 692 sizeof(gendisk->disk_name)); 693 snprintf(gendisk->devfs_name, sizeof(gendisk->devfs_name), 694 VIOCD_DEVICE_DEVFS "%d", deviceno); 695 blk_queue_max_hw_segments(q, 1); 696 blk_queue_max_phys_segments(q, 1); 697 blk_queue_max_sectors(q, 4096 / 512); 698 gendisk->queue = q; 699 gendisk->fops = &viocd_fops; 700 gendisk->flags = GENHD_FL_CD|GENHD_FL_REMOVABLE; 701 set_capacity(gendisk, 0); 702 gendisk->private_data = d; 703 d->viocd_disk = gendisk; 704 d->dev = &vdev->dev; 705 gendisk->driverfs_dev = d->dev; 706 add_disk(gendisk); 707 return 0; 708 709out_cleanup_queue: 710 blk_cleanup_queue(q); 711out_unregister_cdrom: 712 unregister_cdrom(c); 713out: 714 return -ENODEV; 715} 716 717static int viocd_remove(struct vio_dev *vdev) 718{ 719 struct disk_info *d = &viocd_diskinfo[vdev->unit_address]; 720 721 if (unregister_cdrom(&d->viocd_info) != 0) 722 printk(VIOCD_KERN_WARNING 723 "Cannot unregister viocd CD-ROM %s!\n", 724 d->viocd_info.name); 725 del_gendisk(d->viocd_disk); 726 blk_cleanup_queue(d->viocd_disk->queue); 727 put_disk(d->viocd_disk); 728 return 0; 729} 730 731/** 732 * viocd_device_table: Used by vio.c to match devices that we 733 * support. 734 */ 735static struct vio_device_id viocd_device_table[] __devinitdata = { 736 { "viocd", "" }, 737 { "", "" } 738}; 739 740MODULE_DEVICE_TABLE(vio, viocd_device_table); 741static struct vio_driver viocd_driver = { 742 .name = "viocd", 743 .id_table = viocd_device_table, 744 .probe = viocd_probe, 745 .remove = viocd_remove 746}; 747 748static int __init viocd_init(void) 749{ 750 struct proc_dir_entry *e; 751 int ret = 0; 752 753 if (viopath_hostLp == HvLpIndexInvalid) { 754 vio_set_hostlp(); 755 /* If we don't have a host, bail out */ 756 if (viopath_hostLp == HvLpIndexInvalid) 757 return -ENODEV; 758 } 759 760 printk(VIOCD_KERN_INFO "vers " VIOCD_VERS ", hosting partition %d\n", 761 viopath_hostLp); 762 763 if (register_blkdev(VIOCD_MAJOR, VIOCD_DEVICE) != 0) { 764 printk(VIOCD_KERN_WARNING "Unable to get major %d for %s\n", 765 VIOCD_MAJOR, VIOCD_DEVICE); 766 return -EIO; 767 } 768 769 ret = viopath_open(viopath_hostLp, viomajorsubtype_cdio, 770 MAX_CD_REQ + 2); 771 if (ret) { 772 printk(VIOCD_KERN_WARNING 773 "error opening path to host partition %d\n", 774 viopath_hostLp); 775 goto out_unregister; 776 } 777 778 /* Initialize our request handler */ 779 vio_setHandler(viomajorsubtype_cdio, vio_handle_cd_event); 780 781 get_viocd_info(); 782 783 spin_lock_init(&viocd_reqlock); 784 785 ret = vio_register_driver(&viocd_driver); 786 if (ret) 787 goto out_free_info; 788 789 e = create_proc_entry("iSeries/viocd", S_IFREG|S_IRUGO, NULL); 790 if (e) { 791 e->owner = THIS_MODULE; 792 e->proc_fops = &proc_viocd_operations; 793 } 794 795 return 0; 796 797out_free_info: 798 dma_free_coherent(iSeries_vio_dev, 799 sizeof(*viocd_unitinfo) * VIOCD_MAX_CD, 800 viocd_unitinfo, unitinfo_dmaaddr); 801 vio_clearHandler(viomajorsubtype_cdio); 802 viopath_close(viopath_hostLp, viomajorsubtype_cdio, MAX_CD_REQ + 2); 803out_unregister: 804 unregister_blkdev(VIOCD_MAJOR, VIOCD_DEVICE); 805 return ret; 806} 807 808static void __exit viocd_exit(void) 809{ 810 remove_proc_entry("iSeries/viocd", NULL); 811 vio_unregister_driver(&viocd_driver); 812 if (viocd_unitinfo != NULL) 813 dma_free_coherent(iSeries_vio_dev, 814 sizeof(*viocd_unitinfo) * VIOCD_MAX_CD, 815 viocd_unitinfo, unitinfo_dmaaddr); 816 viopath_close(viopath_hostLp, viomajorsubtype_cdio, MAX_CD_REQ + 2); 817 vio_clearHandler(viomajorsubtype_cdio); 818 unregister_blkdev(VIOCD_MAJOR, VIOCD_DEVICE); 819} 820 821module_init(viocd_init); 822module_exit(viocd_exit); 823MODULE_LICENSE("GPL");