#include <linux/proc_fs.h>
#include <linux/seq_file.h>
-#include <asm/bug.h>
-
+#include <asm/vio.h>
#include <asm/scatterlist.h>
-#include <asm/iSeries/HvTypes.h>
-#include <asm/iSeries/HvLpEvent.h>
-#include <asm/iSeries/vio.h>
+#include <asm/iseries/hv_types.h>
+#include <asm/iseries/hv_lp_event.h>
+#include <asm/iseries/vio.h>
#define VIOCD_DEVICE "iseries/vcd"
#define VIOCD_DEVICE_DEVFS "iseries/vcd"
/*
* Should probably make this a module parameter....sigh
*/
-#define VIOCD_MAX_CD 8
+#define VIOCD_MAX_CD HVMAXARCHITECTEDVIRTUALCDROMS
static const struct vio_error_entry viocd_err_table[] = {
{0x0201, EINVAL, "Invalid Range"},
};
static struct capability_entry capability_table[] __initdata = {
- { "6330", CDC_LOCK | CDC_DVD_RAM },
+ { "6330", CDC_LOCK | CDC_DVD_RAM | CDC_RAM },
+ { "6331", CDC_LOCK | CDC_DVD_RAM | CDC_RAM },
+ { "6333", CDC_LOCK | CDC_DVD_RAM | CDC_RAM },
+ { "632A", CDC_LOCK | CDC_DVD_RAM | CDC_RAM },
{ "6321", CDC_LOCK },
{ "632B", 0 },
{ NULL , CDC_LOCK },
struct disk_info {
struct gendisk *viocd_disk;
struct cdrom_device_info viocd_info;
+ struct device *dev;
};
static struct disk_info viocd_diskinfo[VIOCD_MAX_CD];
#define DEVICE_NR(di) ((di) - &viocd_diskinfo[0])
-static request_queue_t *viocd_queue;
static spinlock_t viocd_reqlock;
#define MAX_CD_REQ 1
unsigned cmd, unsigned long arg)
{
struct disk_info *di = inode->i_bdev->bd_disk->private_data;
- return cdrom_ioctl(&di->viocd_info, inode, cmd, arg);
+ return cdrom_ioctl(file, &di->viocd_info, inode, cmd, arg);
}
static int viocd_blk_media_changed(struct gendisk *disk)
for (i = 0; (i < VIOCD_MAX_CD) && viocd_unitinfo[i].rsrcname[0]; i++)
viocd_numdev++;
- return;
-
error_ret:
- dma_free_coherent(iSeries_vio_dev,
- sizeof(*viocd_unitinfo) * VIOCD_MAX_CD,
- viocd_unitinfo, unitinfo_dmaaddr);
- viocd_unitinfo = NULL;
+ if (viocd_numdev == 0) {
+ dma_free_coherent(iSeries_vio_dev,
+ sizeof(*viocd_unitinfo) * VIOCD_MAX_CD,
+ viocd_unitinfo, unitinfo_dmaaddr);
+ viocd_unitinfo = NULL;
+ }
}
static int viocd_open(struct cdrom_device_info *cdi, int purpose)
struct disk_info *diskinfo = req->rq_disk->private_data;
u64 len;
dma_addr_t dmaaddr;
+ int direction;
+ u16 cmd;
struct scatterlist sg;
BUG_ON(req->nr_phys_segments > 1);
- BUG_ON(rq_data_dir(req) != READ);
+
+ if (rq_data_dir(req) == READ) {
+ direction = DMA_FROM_DEVICE;
+ cmd = viomajorsubtype_cdio | viocdread;
+ } else {
+ direction = DMA_TO_DEVICE;
+ cmd = viomajorsubtype_cdio | viocdwrite;
+ }
if (blk_rq_map_sg(req->q, req, &sg) == 0) {
printk(VIOCD_KERN_WARNING
return -1;
}
- if (dma_map_sg(iSeries_vio_dev, &sg, 1, DMA_FROM_DEVICE) == 0) {
+ if (dma_map_sg(diskinfo->dev, &sg, 1, direction) == 0) {
printk(VIOCD_KERN_WARNING "error allocating sg tce\n");
return -1;
}
len = sg_dma_len(&sg);
hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
- HvLpEvent_Type_VirtualIo,
- viomajorsubtype_cdio | viocdread,
+ HvLpEvent_Type_VirtualIo, cmd,
HvLpEvent_AckInd_DoAck,
HvLpEvent_AckType_ImmediateAck,
viopath_sourceinst(viopath_hostLp),
return 0;
}
+static int viocd_packet(struct cdrom_device_info *cdi,
+ struct packet_command *cgc)
+{
+ unsigned int buflen = cgc->buflen;
+ int ret = -EIO;
+
+ switch (cgc->cmd[0]) {
+ case GPCMD_READ_DISC_INFO:
+ {
+ disc_information *di = (disc_information *)cgc->buffer;
+
+ if (buflen >= 2) {
+ di->disc_information_length = cpu_to_be16(1);
+ ret = 0;
+ }
+ if (buflen >= 3)
+ di->erasable =
+ (cdi->ops->capability & ~cdi->mask
+ & (CDC_DVD_RAM | CDC_RAM)) != 0;
+ }
+ break;
+ case GPCMD_GET_CONFIGURATION:
+ if (cgc->cmd[3] == CDF_RWRT) {
+ struct rwrt_feature_desc *rfd = (struct rwrt_feature_desc *)(cgc->buffer + sizeof(struct feature_header));
+
+ if ((buflen >=
+ (sizeof(struct feature_header) + sizeof(*rfd))) &&
+ (cdi->ops->capability & ~cdi->mask
+ & (CDC_DVD_RAM | CDC_RAM))) {
+ rfd->feature_code = cpu_to_be16(CDF_RWRT);
+ rfd->curr = 1;
+ ret = 0;
+ }
+ }
+ break;
+ default:
+ if (cgc->sense) {
+ /* indicate Unknown code */
+ cgc->sense->sense_key = 0x05;
+ cgc->sense->asc = 0x20;
+ cgc->sense->ascq = 0x00;
+ }
+ break;
+ }
+
+ cgc->stat = ret;
+ return ret;
+}
+
+static void restart_all_queues(int first_index)
+{
+ int i;
+
+ for (i = first_index + 1; i < viocd_numdev; i++)
+ if (viocd_diskinfo[i].viocd_disk)
+ blk_run_queue(viocd_diskinfo[i].viocd_disk->queue);
+ for (i = 0; i <= first_index; i++)
+ if (viocd_diskinfo[i].viocd_disk)
+ blk_run_queue(viocd_diskinfo[i].viocd_disk->queue);
+}
+
/* This routine handles incoming CD LP events */
static void vio_handle_cd_event(struct HvLpEvent *event)
{
/* Notification that a partition went away! */
return;
/* First, we should NEVER get an int here...only acks */
- if (event->xFlags.xFunction == HvLpEvent_Function_Int) {
+ if (hvlpevent_is_int(event)) {
printk(VIOCD_KERN_WARNING
"Yikes! got an int in viocd event handler!\n");
- if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) {
+ if (hvlpevent_need_ack(event)) {
event->xRc = HvLpEvent_Rc_InvalidSubtype;
HvCallEvent_ackLpEvent(event);
}
case viocdopen:
if (event->xRc == 0) {
di = &viocd_diskinfo[bevent->disk];
- blk_queue_hardsect_size(viocd_queue,
+ blk_queue_hardsect_size(di->viocd_disk->queue,
bevent->block_size);
set_capacity(di->viocd_disk,
bevent->media_size *
case viocdclose:
break;
+ case viocdwrite:
case viocdread:
/*
* Since this is running in interrupt mode, we need to
* make sure we're not stepping on any global I/O operations
*/
+ di = &viocd_diskinfo[bevent->disk];
spin_lock_irqsave(&viocd_reqlock, flags);
- dma_unmap_single(iSeries_vio_dev, bevent->token, bevent->len,
- DMA_FROM_DEVICE);
+ dma_unmap_single(di->dev, bevent->token, bevent->len,
+ ((event->xSubtype & VIOMINOR_SUBTYPE_MASK) == viocdread)
+ ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
req = (struct request *)bevent->event.xCorrelationToken;
rwreq--;
/* restart handling of incoming requests */
spin_unlock_irqrestore(&viocd_reqlock, flags);
- blk_run_queue(viocd_queue);
+ restart_all_queues(bevent->disk);
break;
default:
printk(VIOCD_KERN_WARNING
"message with invalid subtype %0x04X!\n",
event->xSubtype & VIOMINOR_SUBTYPE_MASK);
- if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) {
+ if (hvlpevent_need_ack(event)) {
event->xRc = HvLpEvent_Rc_InvalidSubtype;
HvCallEvent_ackLpEvent(event);
}
.release = viocd_release,
.media_changed = viocd_media_changed,
.lock_door = viocd_lock_door,
- .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
+ .generic_packet = viocd_packet,
+ .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
};
static int __init find_capability(const char *type)
return entry->capability;
}
-static int __init viocd_init(void)
+static int viocd_probe(struct vio_dev *vdev, const struct vio_device_id *id)
{
struct gendisk *gendisk;
int deviceno;
- int ret = 0;
+ struct disk_info *d;
+ struct cdrom_device_info *c;
+ struct cdrom_info *ci;
+ struct request_queue *q;
+
+ deviceno = vdev->unit_address;
+ if (deviceno >= viocd_numdev)
+ return -ENODEV;
+
+ d = &viocd_diskinfo[deviceno];
+ c = &d->viocd_info;
+ ci = &viocd_unitinfo[deviceno];
+
+ c->ops = &viocd_dops;
+ c->speed = 4;
+ c->capacity = 1;
+ c->handle = d;
+ c->mask = ~find_capability(ci->type);
+ sprintf(c->name, VIOCD_DEVICE "%c", 'a' + deviceno);
+
+ if (register_cdrom(c) != 0) {
+ printk(VIOCD_KERN_WARNING "Cannot register viocd CD-ROM %s!\n",
+ c->name);
+ goto out;
+ }
+ printk(VIOCD_KERN_INFO "cd %s is iSeries resource %10.10s "
+ "type %4.4s, model %3.3s\n",
+ c->name, ci->rsrcname, ci->type, ci->model);
+ q = blk_init_queue(do_viocd_request, &viocd_reqlock);
+ if (q == NULL) {
+ printk(VIOCD_KERN_WARNING "Cannot allocate queue for %s!\n",
+ c->name);
+ goto out_unregister_cdrom;
+ }
+ gendisk = alloc_disk(1);
+ if (gendisk == NULL) {
+ printk(VIOCD_KERN_WARNING "Cannot create gendisk for %s!\n",
+ c->name);
+ goto out_cleanup_queue;
+ }
+ gendisk->major = VIOCD_MAJOR;
+ gendisk->first_minor = deviceno;
+ strncpy(gendisk->disk_name, c->name,
+ sizeof(gendisk->disk_name));
+ snprintf(gendisk->devfs_name, sizeof(gendisk->devfs_name),
+ VIOCD_DEVICE_DEVFS "%d", deviceno);
+ blk_queue_max_hw_segments(q, 1);
+ blk_queue_max_phys_segments(q, 1);
+ blk_queue_max_sectors(q, 4096 / 512);
+ gendisk->queue = q;
+ gendisk->fops = &viocd_fops;
+ gendisk->flags = GENHD_FL_CD|GENHD_FL_REMOVABLE;
+ set_capacity(gendisk, 0);
+ gendisk->private_data = d;
+ d->viocd_disk = gendisk;
+ d->dev = &vdev->dev;
+ gendisk->driverfs_dev = d->dev;
+ add_disk(gendisk);
+ return 0;
+
+out_cleanup_queue:
+ blk_cleanup_queue(q);
+out_unregister_cdrom:
+ unregister_cdrom(c);
+out:
+ return -ENODEV;
+}
+
+static int viocd_remove(struct vio_dev *vdev)
+{
+ struct disk_info *d = &viocd_diskinfo[vdev->unit_address];
+
+ if (unregister_cdrom(&d->viocd_info) != 0)
+ printk(VIOCD_KERN_WARNING
+ "Cannot unregister viocd CD-ROM %s!\n",
+ d->viocd_info.name);
+ del_gendisk(d->viocd_disk);
+ blk_cleanup_queue(d->viocd_disk->queue);
+ put_disk(d->viocd_disk);
+ return 0;
+}
+
+/**
+ * viocd_device_table: Used by vio.c to match devices that we
+ * support.
+ */
+static struct vio_device_id viocd_device_table[] __devinitdata = {
+ { "viocd", "" },
+ { "", "" }
+};
+MODULE_DEVICE_TABLE(vio, viocd_device_table);
+
+static struct vio_driver viocd_driver = {
+ .id_table = viocd_device_table,
+ .probe = viocd_probe,
+ .remove = viocd_remove,
+ .driver = {
+ .name = "viocd",
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init viocd_init(void)
+{
struct proc_dir_entry *e;
+ int ret = 0;
if (viopath_hostLp == HvLpIndexInvalid) {
vio_set_hostlp();
viopath_hostLp);
if (register_blkdev(VIOCD_MAJOR, VIOCD_DEVICE) != 0) {
- printk(VIOCD_KERN_WARNING
- "Unable to get major %d for %s\n",
+ printk(VIOCD_KERN_WARNING "Unable to get major %d for %s\n",
VIOCD_MAJOR, VIOCD_DEVICE);
return -EIO;
}
vio_setHandler(viomajorsubtype_cdio, vio_handle_cd_event);
get_viocd_info();
- if (viocd_numdev == 0)
- goto out_undo_vio;
- ret = -ENOMEM;
spin_lock_init(&viocd_reqlock);
- viocd_queue = blk_init_queue(do_viocd_request, &viocd_reqlock);
- if (viocd_queue == NULL)
- goto out_unregister;
- blk_queue_max_hw_segments(viocd_queue, 1);
- blk_queue_max_phys_segments(viocd_queue, 1);
- blk_queue_max_sectors(viocd_queue, 4096 / 512);
-
- /* initialize units */
- for (deviceno = 0; deviceno < viocd_numdev; deviceno++) {
- struct disk_info *d = &viocd_diskinfo[deviceno];
- struct cdrom_device_info *c = &d->viocd_info;
- struct cdrom_info *ci = &viocd_unitinfo[deviceno];
-
- c->ops = &viocd_dops;
- c->speed = 4;
- c->capacity = 1;
- c->handle = d;
- c->mask = ~find_capability(ci->type);
- sprintf(c->name, VIOCD_DEVICE "%c", 'a' + deviceno);
-
- if (register_cdrom(c) != 0) {
- printk(VIOCD_KERN_WARNING
- "Cannot register viocd CD-ROM %s!\n",
- c->name);
- continue;
- }
- printk(VIOCD_KERN_INFO "cd %s is iSeries resource %10.10s "
- "type %4.4s, model %3.3s\n",
- c->name, ci->rsrcname, ci->type, ci->model);
- gendisk = alloc_disk(1);
- if (gendisk == NULL) {
- printk(VIOCD_KERN_WARNING
- "Cannot create gendisk for %s!\n",
- c->name);
- unregister_cdrom(c);
- continue;
- }
- gendisk->major = VIOCD_MAJOR;
- gendisk->first_minor = deviceno;
- strncpy(gendisk->disk_name, c->name,
- sizeof(gendisk->disk_name));
- snprintf(gendisk->devfs_name, sizeof(gendisk->devfs_name),
- VIOCD_DEVICE_DEVFS "%d", deviceno);
- gendisk->queue = viocd_queue;
- gendisk->fops = &viocd_fops;
- gendisk->flags = GENHD_FL_CD|GENHD_FL_REMOVABLE;
- set_capacity(gendisk, 0);
- gendisk->private_data = d;
- d->viocd_disk = gendisk;
- add_disk(gendisk);
- }
+
+ ret = vio_register_driver(&viocd_driver);
+ if (ret)
+ goto out_free_info;
e = create_proc_entry("iSeries/viocd", S_IFREG|S_IRUGO, NULL);
if (e) {
return 0;
-out_undo_vio:
+out_free_info:
+ dma_free_coherent(iSeries_vio_dev,
+ sizeof(*viocd_unitinfo) * VIOCD_MAX_CD,
+ viocd_unitinfo, unitinfo_dmaaddr);
vio_clearHandler(viomajorsubtype_cdio);
viopath_close(viopath_hostLp, viomajorsubtype_cdio, MAX_CD_REQ + 2);
out_unregister:
static void __exit viocd_exit(void)
{
- int deviceno;
-
remove_proc_entry("iSeries/viocd", NULL);
- for (deviceno = 0; deviceno < viocd_numdev; deviceno++) {
- struct disk_info *d = &viocd_diskinfo[deviceno];
- if (unregister_cdrom(&d->viocd_info) != 0)
- printk(VIOCD_KERN_WARNING
- "Cannot unregister viocd CD-ROM %s!\n",
- d->viocd_info.name);
- del_gendisk(d->viocd_disk);
- put_disk(d->viocd_disk);
- }
- blk_cleanup_queue(viocd_queue);
+ vio_unregister_driver(&viocd_driver);
if (viocd_unitinfo != NULL)
dma_free_coherent(iSeries_vio_dev,
sizeof(*viocd_unitinfo) * VIOCD_MAX_CD,