Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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