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