Blame view

drivers/cdrom/viocd.c 19.2 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
  /* -*- linux-c -*-
   *  drivers/cdrom/viocd.c
   *
   *  iSeries Virtual CD Rom
   *
   *  Authors: Dave Boutcher <boutcher@us.ibm.com>
   *           Ryan Arnold <ryanarn@us.ibm.com>
   *           Colin Devilbiss <devilbis@us.ibm.com>
8962cadbe   Stephen Rothwell   [POWERPC] iSeries...
9
   *           Stephen Rothwell
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
   *
   * (C) Copyright 2000-2004 IBM Corporation
   *
   * This program is free software;  you can redistribute it and/or
   * modify it under the terms of the GNU General Public License as
   * published by the Free Software Foundation; either version 2 of the
   * License, or (at your option) anyu later version.
   *
   * This program is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software Foundation,
   * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
   *
   * This routine provides access to CD ROM drives owned and managed by an
   * OS/400 partition running on the same box as this Linux partition.
   *
   * All operations are performed by sending messages back and forth to
   * the OS/400 partition.
   */
e597cd09f   Joe Perches   drivers/cdrom: us...
33
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34
35
36
37
38
39
40
41
42
  #include <linux/major.h>
  #include <linux/blkdev.h>
  #include <linux/cdrom.h>
  #include <linux/errno.h>
  #include <linux/init.h>
  #include <linux/dma-mapping.h>
  #include <linux/module.h>
  #include <linux/completion.h>
  #include <linux/proc_fs.h>
2a48fc0ab   Arnd Bergmann   block: autoconver...
43
  #include <linux/mutex.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44
  #include <linux/seq_file.h>
3d1266c70   Jens Axboe   SG: audit of driv...
45
  #include <linux/scatterlist.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47
  #include <asm/vio.h>
1ec65d76f   Kelly Daly   merge filename an...
48
  #include <asm/iseries/hv_types.h>
e45423eac   Kelly Daly   merge filename an...
49
  #include <asm/iseries/hv_lp_event.h>
b42067787   Kelly Daly   merge filename an...
50
  #include <asm/iseries/vio.h>
31c72ad0d   Stephen Rothwell   [POWERPC] iSeries...
51
  #include <asm/firmware.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52
53
  
  #define VIOCD_DEVICE			"iseries/vcd"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
54
55
  
  #define VIOCD_VERS "1.06"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
57
58
59
  /*
   * Should probably make this a module parameter....sigh
   */
  #define VIOCD_MAX_CD	HVMAXARCHITECTEDVIRTUALCDROMS
2a48fc0ab   Arnd Bergmann   block: autoconver...
60
  static DEFINE_MUTEX(viocd_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
  static const struct vio_error_entry viocd_err_table[] = {
  	{0x0201, EINVAL, "Invalid Range"},
  	{0x0202, EINVAL, "Invalid Token"},
  	{0x0203, EIO, "DMA Error"},
  	{0x0204, EIO, "Use Error"},
  	{0x0205, EIO, "Release Error"},
  	{0x0206, EINVAL, "Invalid CD"},
  	{0x020C, EROFS, "Read Only Device"},
  	{0x020D, ENOMEDIUM, "Changed or Missing Volume (or Varied Off?)"},
  	{0x020E, EIO, "Optical System Error (Varied Off?)"},
  	{0x02FF, EIO, "Internal Error"},
  	{0x3010, EIO, "Changed Volume"},
  	{0xC100, EIO, "Optical System Error"},
  	{0x0000, 0, NULL},
  };
  
  /*
   * This is the structure we use to exchange info between driver and interrupt
   * handler
   */
  struct viocd_waitevent {
  	struct completion	com;
  	int			rc;
  	u16			sub_result;
  	int			changed;
  };
  
  /* this is a lookup table for the true capabilities of a device */
  struct capability_entry {
  	char	*type;
  	int	capability;
  };
  
  static struct capability_entry capability_table[] __initdata = {
  	{ "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 },
  };
  
  /* These are our internal structures for keeping track of devices */
  static int viocd_numdev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
107
108
109
  struct disk_info {
  	struct gendisk			*viocd_disk;
  	struct cdrom_device_info	viocd_info;
  	struct device			*dev;
b833b481c   Stephen Rothwell   [POWERPC] iSeries...
110
111
112
  	const char			*rsrcname;
  	const char			*type;
  	const char			*model;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
  };
  static struct disk_info viocd_diskinfo[VIOCD_MAX_CD];
  
  #define DEVICE_NR(di)	((di) - &viocd_diskinfo[0])
  
  static spinlock_t viocd_reqlock;
  
  #define MAX_CD_REQ	1
  
  /* procfs support */
  static int proc_viocd_show(struct seq_file *m, void *v)
  {
  	int i;
  
  	for (i = 0; i < viocd_numdev; i++) {
  		seq_printf(m, "viocd device %d is iSeries resource %10.10s"
  				"type %4.4s, model %3.3s
  ",
b833b481c   Stephen Rothwell   [POWERPC] iSeries...
131
132
133
  				i, viocd_diskinfo[i].rsrcname,
  				viocd_diskinfo[i].type,
  				viocd_diskinfo[i].model);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134
135
136
137
138
139
140
141
  	}
  	return 0;
  }
  
  static int proc_viocd_open(struct inode *inode, struct file *file)
  {
  	return single_open(file, proc_viocd_show, NULL);
  }
2b8693c06   Arjan van de Ven   [PATCH] mark stru...
142
  static const struct file_operations proc_viocd_operations = {
c7705f344   Denis V. Lunev   drivers: use non-...
143
  	.owner		= THIS_MODULE,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144
145
146
147
148
  	.open		= proc_viocd_open,
  	.read		= seq_read,
  	.llseek		= seq_lseek,
  	.release	= single_release,
  };
4e379ae6a   Al Viro   [PATCH] switch viocd
149
  static int viocd_blk_open(struct block_device *bdev, fmode_t mode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
150
  {
4e379ae6a   Al Viro   [PATCH] switch viocd
151
  	struct disk_info *di = bdev->bd_disk->private_data;
6e9624b8c   Arnd Bergmann   block: push down ...
152
  	int ret;
2a48fc0ab   Arnd Bergmann   block: autoconver...
153
  	mutex_lock(&viocd_mutex);
6e9624b8c   Arnd Bergmann   block: push down ...
154
  	ret = cdrom_open(&di->viocd_info, bdev, mode);
2a48fc0ab   Arnd Bergmann   block: autoconver...
155
  	mutex_unlock(&viocd_mutex);
6e9624b8c   Arnd Bergmann   block: push down ...
156
157
  
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158
  }
4e379ae6a   Al Viro   [PATCH] switch viocd
159
  static int viocd_blk_release(struct gendisk *disk, fmode_t mode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
  {
4e379ae6a   Al Viro   [PATCH] switch viocd
161
  	struct disk_info *di = disk->private_data;
2a48fc0ab   Arnd Bergmann   block: autoconver...
162
  	mutex_lock(&viocd_mutex);
4e379ae6a   Al Viro   [PATCH] switch viocd
163
  	cdrom_release(&di->viocd_info, mode);
2a48fc0ab   Arnd Bergmann   block: autoconver...
164
  	mutex_unlock(&viocd_mutex);
bbc1cc978   Al Viro   [PATCH] switch cd...
165
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
  }
4e379ae6a   Al Viro   [PATCH] switch viocd
167
  static int viocd_blk_ioctl(struct block_device *bdev, fmode_t mode,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168
169
  		unsigned cmd, unsigned long arg)
  {
4e379ae6a   Al Viro   [PATCH] switch viocd
170
  	struct disk_info *di = bdev->bd_disk->private_data;
8a6cfeb6d   Arnd Bergmann   block: push down ...
171
  	int ret;
2a48fc0ab   Arnd Bergmann   block: autoconver...
172
  	mutex_lock(&viocd_mutex);
8a6cfeb6d   Arnd Bergmann   block: push down ...
173
  	ret = cdrom_ioctl(&di->viocd_info, bdev, mode, cmd, arg);
2a48fc0ab   Arnd Bergmann   block: autoconver...
174
  	mutex_unlock(&viocd_mutex);
8a6cfeb6d   Arnd Bergmann   block: push down ...
175
176
  
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177
  }
1c27030bd   Tejun Heo   gdrom,viocd: Conv...
178
179
  static unsigned int viocd_blk_check_events(struct gendisk *disk,
  					   unsigned int clearing)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
181
  {
  	struct disk_info *di = disk->private_data;
1c27030bd   Tejun Heo   gdrom,viocd: Conv...
182
  	return cdrom_check_events(&di->viocd_info, clearing);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
  }
83d5cde47   Alexey Dobriyan   const: make block...
184
  static const struct block_device_operations viocd_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
  	.owner =		THIS_MODULE,
4e379ae6a   Al Viro   [PATCH] switch viocd
186
187
  	.open =			viocd_blk_open,
  	.release =		viocd_blk_release,
8a6cfeb6d   Arnd Bergmann   block: push down ...
188
  	.ioctl =		viocd_blk_ioctl,
1c27030bd   Tejun Heo   gdrom,viocd: Conv...
189
  	.check_events =		viocd_blk_check_events,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
  static int viocd_open(struct cdrom_device_info *cdi, int purpose)
  {
          struct disk_info *diskinfo = cdi->handle;
  	int device_no = DEVICE_NR(diskinfo);
  	HvLpEvent_Rc hvrc;
  	struct viocd_waitevent we;
  
  	init_completion(&we.com);
  	hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
  			HvLpEvent_Type_VirtualIo,
  			viomajorsubtype_cdio | viocdopen,
  			HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
  			viopath_sourceinst(viopath_hostLp),
  			viopath_targetinst(viopath_hostLp),
  			(u64)&we, VIOVERSION << 16, ((u64)device_no << 48),
  			0, 0, 0);
  	if (hvrc != 0) {
e597cd09f   Joe Perches   drivers/cdrom: us...
208
209
210
  		pr_warning("bad rc on HvCallEvent_signalLpEventFast %d
  ",
  			   (int)hvrc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
212
213
214
215
216
217
218
  		return -EIO;
  	}
  
  	wait_for_completion(&we.com);
  
  	if (we.rc) {
  		const struct vio_error_entry *err =
  			vio_lookup_rc(viocd_err_table, we.sub_result);
e597cd09f   Joe Perches   drivers/cdrom: us...
219
220
221
  		pr_warning("bad rc %d:0x%04X on open: %s
  ",
  			   we.rc, we.sub_result, err->msg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
  		return -err->errno;
  	}
  
  	return 0;
  }
  
  static void viocd_release(struct cdrom_device_info *cdi)
  {
  	int device_no = DEVICE_NR((struct disk_info *)cdi->handle);
  	HvLpEvent_Rc hvrc;
  
  	hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
  			HvLpEvent_Type_VirtualIo,
  			viomajorsubtype_cdio | viocdclose,
  			HvLpEvent_AckInd_NoAck, HvLpEvent_AckType_ImmediateAck,
  			viopath_sourceinst(viopath_hostLp),
  			viopath_targetinst(viopath_hostLp), 0,
  			VIOVERSION << 16, ((u64)device_no << 48), 0, 0, 0);
  	if (hvrc != 0)
e597cd09f   Joe Perches   drivers/cdrom: us...
241
242
243
  		pr_warning("bad rc on HvCallEvent_signalLpEventFast %d
  ",
  			   (int)hvrc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
  }
  
  /* Send a read or write request to OS/400 */
  static int send_request(struct request *req)
  {
  	HvLpEvent_Rc hvrc;
  	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);
  
  	if (rq_data_dir(req) == READ) {
  		direction = DMA_FROM_DEVICE;
  		cmd = viomajorsubtype_cdio | viocdread;
  	} else {
  		direction = DMA_TO_DEVICE;
  		cmd = viomajorsubtype_cdio | viocdwrite;
  	}
3d1266c70   Jens Axboe   SG: audit of driv...
266
  	sg_init_table(&sg, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
267
          if (blk_rq_map_sg(req->q, req, &sg) == 0) {
e597cd09f   Joe Perches   drivers/cdrom: us...
268
269
  		pr_warning("error setting up scatter/gather list
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
270
271
272
273
  		return -1;
  	}
  
  	if (dma_map_sg(diskinfo->dev, &sg, 1, direction) == 0) {
e597cd09f   Joe Perches   drivers/cdrom: us...
274
275
  		pr_warning("error allocating sg tce
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
276
277
278
279
280
281
282
283
284
285
286
287
288
  		return -1;
  	}
  	dmaaddr = sg_dma_address(&sg);
  	len = sg_dma_len(&sg);
  
  	hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
  			HvLpEvent_Type_VirtualIo, cmd,
  			HvLpEvent_AckInd_DoAck,
  			HvLpEvent_AckType_ImmediateAck,
  			viopath_sourceinst(viopath_hostLp),
  			viopath_targetinst(viopath_hostLp),
  			(u64)req, VIOVERSION << 16,
  			((u64)DEVICE_NR(diskinfo) << 48) | dmaaddr,
83096ebf1   Tejun Heo   block: convert to...
289
  			(u64)blk_rq_pos(req) * 512, len, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
290
  	if (hvrc != HvLpEvent_Rc_Good) {
e597cd09f   Joe Perches   drivers/cdrom: us...
291
292
  		pr_warning("hv error on op %d
  ", (int)hvrc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293
294
295
296
297
  		return -1;
  	}
  
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298
  static int rwreq;
165125e1e   Jens Axboe   [BLOCK] Get rid o...
299
  static void do_viocd_request(struct request_queue *q)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
301
  {
  	struct request *req;
9934c8c04   Tejun Heo   block: implement ...
302
  	while ((rwreq == 0) && ((req = blk_fetch_request(q)) != NULL)) {
33659ebba   Christoph Hellwig   block: remove wra...
303
  		if (req->cmd_type != REQ_TYPE_FS)
40cbbb781   Tejun Heo   block: implement ...
304
  			__blk_end_request_all(req, -EIO);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
305
  		else if (send_request(req) < 0) {
e597cd09f   Joe Perches   drivers/cdrom: us...
306
307
  			pr_warning("unable to send message to OS/400!
  ");
40cbbb781   Tejun Heo   block: implement ...
308
  			__blk_end_request_all(req, -EIO);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
309
310
311
312
  		} else
  			rwreq++;
  	}
  }
1c27030bd   Tejun Heo   gdrom,viocd: Conv...
313
314
  static unsigned int viocd_check_events(struct cdrom_device_info *cdi,
  				       unsigned int clearing, int disc_nr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
  {
  	struct viocd_waitevent we;
  	HvLpEvent_Rc hvrc;
  	int device_no = DEVICE_NR((struct disk_info *)cdi->handle);
  
  	init_completion(&we.com);
  
  	/* Send the open event to OS/400 */
  	hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
  			HvLpEvent_Type_VirtualIo,
  			viomajorsubtype_cdio | viocdcheck,
  			HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
  			viopath_sourceinst(viopath_hostLp),
  			viopath_targetinst(viopath_hostLp),
  			(u64)&we, VIOVERSION << 16, ((u64)device_no << 48),
  			0, 0, 0);
  	if (hvrc != 0) {
e597cd09f   Joe Perches   drivers/cdrom: us...
332
333
334
  		pr_warning("bad rc on HvCallEvent_signalLpEventFast %d
  ",
  			   (int)hvrc);
1c27030bd   Tejun Heo   gdrom,viocd: Conv...
335
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
336
337
338
339
340
341
342
343
  	}
  
  	wait_for_completion(&we.com);
  
  	/* Check the return code.  If bad, assume no change */
  	if (we.rc) {
  		const struct vio_error_entry *err =
  			vio_lookup_rc(viocd_err_table, we.sub_result);
e597cd09f   Joe Perches   drivers/cdrom: us...
344
345
346
  		pr_warning("bad rc %d:0x%04X on check_change: %s; Assuming no change
  ",
  			   we.rc, we.sub_result, err->msg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
347
348
  		return 0;
  	}
1c27030bd   Tejun Heo   gdrom,viocd: Conv...
349
  	return we.changed ? DISK_EVENT_MEDIA_CHANGE : 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
  }
  
  static int viocd_lock_door(struct cdrom_device_info *cdi, int locking)
  {
  	HvLpEvent_Rc hvrc;
  	u64 device_no = DEVICE_NR((struct disk_info *)cdi->handle);
  	/* NOTE: flags is 1 or 0 so it won't overwrite the device_no */
  	u64 flags = !!locking;
  	struct viocd_waitevent we;
  
  	init_completion(&we.com);
  
  	/* Send the lockdoor event to OS/400 */
  	hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
  			HvLpEvent_Type_VirtualIo,
  			viomajorsubtype_cdio | viocdlockdoor,
  			HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
  			viopath_sourceinst(viopath_hostLp),
  			viopath_targetinst(viopath_hostLp),
  			(u64)&we, VIOVERSION << 16,
  			(device_no << 48) | (flags << 32), 0, 0, 0);
  	if (hvrc != 0) {
e597cd09f   Joe Perches   drivers/cdrom: us...
372
373
374
  		pr_warning("bad rc on HvCallEvent_signalLpEventFast %d
  ",
  			   (int)hvrc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
  		return -EIO;
  	}
  
  	wait_for_completion(&we.com);
  
  	if (we.rc != 0)
  		return -EIO;
  	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;
9db292588   Stephen Rothwell   [PATCH] ppc64 iSe...
406
407
408
409
410
411
412
413
414
415
416
417
418
419
  	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;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
  	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)
  {
  	struct viocdlpevent *bevent;
  	struct viocd_waitevent *pwe;
  	struct disk_info *di;
  	unsigned long flags;
  	struct request *req;
  
  
  	if (event == NULL)
  		/* Notification that a partition went away! */
  		return;
  	/* First, we should NEVER get an int here...only acks */
677f8c0d0   Stephen Rothwell   [PATCH] powerpc: ...
460
  	if (hvlpevent_is_int(event)) {
e597cd09f   Joe Perches   drivers/cdrom: us...
461
462
  		pr_warning("Yikes! got an int in viocd event handler!
  ");
677f8c0d0   Stephen Rothwell   [PATCH] powerpc: ...
463
  		if (hvlpevent_need_ack(event)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
464
465
466
467
468
469
470
471
472
473
474
  			event->xRc = HvLpEvent_Rc_InvalidSubtype;
  			HvCallEvent_ackLpEvent(event);
  		}
  	}
  
  	bevent = (struct viocdlpevent *)event;
  
  	switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
  	case viocdopen:
  		if (event->xRc == 0) {
  			di = &viocd_diskinfo[bevent->disk];
e1defc4ff   Martin K. Petersen   block: Do away wi...
475
476
  			blk_queue_logical_block_size(di->viocd_disk->queue,
  						     bevent->block_size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
477
478
479
480
481
  			set_capacity(di->viocd_disk,
  					bevent->media_size *
  					bevent->block_size / 512);
  		}
  		/* FALLTHROUGH !! */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
  	case viocdlockdoor:
  		pwe = (struct viocd_waitevent *)event->xCorrelationToken;
  return_complete:
  		pwe->rc = event->xRc;
  		pwe->sub_result = bevent->sub_result;
  		complete(&pwe->com);
  		break;
  
  	case viocdcheck:
  		pwe = (struct viocd_waitevent *)event->xCorrelationToken;
  		pwe->changed = bevent->flags;
  		goto return_complete;
  
  	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(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--;
  
  		if (event->xRc != HvLpEvent_Rc_Good) {
  			const struct vio_error_entry *err =
  				vio_lookup_rc(viocd_err_table,
  						bevent->sub_result);
e597cd09f   Joe Perches   drivers/cdrom: us...
516
517
518
519
  			pr_warning("request %p failed with rc %d:0x%04X: %s
  ",
  				   req, event->xRc,
  				   bevent->sub_result, err->msg);
40cbbb781   Tejun Heo   block: implement ...
520
  			__blk_end_request_all(req, -EIO);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
521
  		} else
40cbbb781   Tejun Heo   block: implement ...
522
  			__blk_end_request_all(req, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
523
524
525
526
527
528
529
  
  		/* restart handling of incoming requests */
  		spin_unlock_irqrestore(&viocd_reqlock, flags);
  		restart_all_queues(bevent->disk);
  		break;
  
  	default:
e597cd09f   Joe Perches   drivers/cdrom: us...
530
531
532
  		pr_warning("message with invalid subtype %0x04X!
  ",
  			   event->xSubtype & VIOMINOR_SUBTYPE_MASK);
677f8c0d0   Stephen Rothwell   [PATCH] powerpc: ...
533
  		if (hvlpevent_need_ack(event)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
534
535
536
537
538
  			event->xRc = HvLpEvent_Rc_InvalidSubtype;
  			HvCallEvent_ackLpEvent(event);
  		}
  	}
  }
3e636f78e   Borislav Petkov   viocd: add dummy ...
539
540
541
542
543
  static int viocd_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
  			     void *arg)
  {
  	return -EINVAL;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
544
545
546
  static struct cdrom_device_ops viocd_dops = {
  	.open = viocd_open,
  	.release = viocd_release,
1c27030bd   Tejun Heo   gdrom,viocd: Conv...
547
  	.check_events = viocd_check_events,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
548
549
  	.lock_door = viocd_lock_door,
  	.generic_packet = viocd_packet,
3e636f78e   Borislav Petkov   viocd: add dummy ...
550
  	.audio_ioctl = viocd_audio_ioctl,
6a2900b67   Christoph Hellwig   [PATCH] kill cdro...
551
  	.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
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
552
  };
c72119277   Stephen Rothwell   [POWERPC] iSeries...
553
  static int find_capability(const char *type)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
  {
  	struct capability_entry *entry;
  
  	for(entry = capability_table; entry->type; ++entry)
  		if(!strncmp(entry->type, type, 4))
  			break;
  	return entry->capability;
  }
  
  static int viocd_probe(struct vio_dev *vdev, const struct vio_device_id *id)
  {
  	struct gendisk *gendisk;
  	int deviceno;
  	struct disk_info *d;
  	struct cdrom_device_info *c;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
569
  	struct request_queue *q;
61c7a080a   Grant Likely   of: Always use 's...
570
  	struct device_node *node = vdev->dev.of_node;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
571
572
  
  	deviceno = vdev->unit_address;
d3375ea72   Roel Kluin   cdrom: beyond ARR...
573
  	if (deviceno >= VIOCD_MAX_CD)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
574
  		return -ENODEV;
b833b481c   Stephen Rothwell   [POWERPC] iSeries...
575
576
577
578
579
  	if (!node)
  		return -ENODEV;
  
  	if (deviceno >= viocd_numdev)
  		viocd_numdev = deviceno + 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
580
581
  
  	d = &viocd_diskinfo[deviceno];
b833b481c   Stephen Rothwell   [POWERPC] iSeries...
582
583
584
  	d->rsrcname = of_get_property(node, "linux,vio_rsrcname", NULL);
  	d->type = of_get_property(node, "linux,vio_type", NULL);
  	d->model = of_get_property(node, "linux,vio_model", NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
585
  	c = &d->viocd_info;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
586
587
588
589
590
  
  	c->ops = &viocd_dops;
  	c->speed = 4;
  	c->capacity = 1;
  	c->handle = d;
b833b481c   Stephen Rothwell   [POWERPC] iSeries...
591
  	c->mask = ~find_capability(d->type);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
592
593
594
  	sprintf(c->name, VIOCD_DEVICE "%c", 'a' + deviceno);
  
  	if (register_cdrom(c) != 0) {
e597cd09f   Joe Perches   drivers/cdrom: us...
595
596
  		pr_warning("Cannot register viocd CD-ROM %s!
  ", c->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
597
598
  		goto out;
  	}
e597cd09f   Joe Perches   drivers/cdrom: us...
599
600
601
  	pr_info("cd %s is iSeries resource %10.10s type %4.4s, model %3.3s
  ",
  		c->name, d->rsrcname, d->type, d->model);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
602
603
  	q = blk_init_queue(do_viocd_request, &viocd_reqlock);
  	if (q == NULL) {
e597cd09f   Joe Perches   drivers/cdrom: us...
604
605
  		pr_warning("Cannot allocate queue for %s!
  ", c->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
606
607
608
609
  		goto out_unregister_cdrom;
  	}
  	gendisk = alloc_disk(1);
  	if (gendisk == NULL) {
e597cd09f   Joe Perches   drivers/cdrom: us...
610
611
  		pr_warning("Cannot create gendisk for %s!
  ", c->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
612
613
614
615
616
617
  		goto out_cleanup_queue;
  	}
  	gendisk->major = VIOCD_MAJOR;
  	gendisk->first_minor = deviceno;
  	strncpy(gendisk->disk_name, c->name,
  			sizeof(gendisk->disk_name));
8a78362c4   Martin K. Petersen   block: Consolidat...
618
  	blk_queue_max_segments(q, 1);
086fa5ff0   Martin K. Petersen   block: Rename blk...
619
  	blk_queue_max_hw_sectors(q, 4096 / 512);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
620
621
  	gendisk->queue = q;
  	gendisk->fops = &viocd_fops;
d4dc210f6   Tejun Heo   block: don't bloc...
622
623
  	gendisk->flags = GENHD_FL_CD | GENHD_FL_REMOVABLE |
  			 GENHD_FL_BLOCK_EVENTS_ON_EXCL_WRITE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
  	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];
0a0c4114d   Akinobu Mita   cdrom: make unreg...
643
  	unregister_cdrom(&d->viocd_info);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
644
645
646
647
648
649
650
651
652
653
654
  	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 = {
de0fe3b83   Stephen Rothwell   [PATCH] powerpc: ...
655
  	{ "block", "IBM,iSeries-viocd" },
fb120da67   Stephen Rothwell   [PATCH] Make MODU...
656
  	{ "", "" }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
657
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
658
  MODULE_DEVICE_TABLE(vio, viocd_device_table);
915124d81   Stephen Rothwell   powerpc: set the ...
659

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
660
  static struct vio_driver viocd_driver = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
661
662
  	.id_table = viocd_device_table,
  	.probe = viocd_probe,
6fdf5392c   Stephen Rothwell   powerpc: don't du...
663
664
665
  	.remove = viocd_remove,
  	.driver = {
  		.name = "viocd",
915124d81   Stephen Rothwell   powerpc: set the ...
666
  		.owner = THIS_MODULE,
6fdf5392c   Stephen Rothwell   powerpc: don't du...
667
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
668
669
670
671
  };
  
  static int __init viocd_init(void)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
672
  	int ret = 0;
31c72ad0d   Stephen Rothwell   [POWERPC] iSeries...
673
674
  	if (!firmware_has_feature(FW_FEATURE_ISERIES))
  		return -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
675
676
677
678
679
680
  	if (viopath_hostLp == HvLpIndexInvalid) {
  		vio_set_hostlp();
  		/* If we don't have a host, bail out */
  		if (viopath_hostLp == HvLpIndexInvalid)
  			return -ENODEV;
  	}
e597cd09f   Joe Perches   drivers/cdrom: us...
681
682
  	pr_info("vers " VIOCD_VERS ", hosting partition %d
  ", viopath_hostLp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
683
684
  
  	if (register_blkdev(VIOCD_MAJOR, VIOCD_DEVICE) != 0) {
e597cd09f   Joe Perches   drivers/cdrom: us...
685
686
687
  		pr_warning("Unable to get major %d for %s
  ",
  			   VIOCD_MAJOR, VIOCD_DEVICE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
688
689
690
691
692
693
  		return -EIO;
  	}
  
  	ret = viopath_open(viopath_hostLp, viomajorsubtype_cdio,
  			MAX_CD_REQ + 2);
  	if (ret) {
e597cd09f   Joe Perches   drivers/cdrom: us...
694
695
696
  		pr_warning("error opening path to host partition %d
  ",
  			   viopath_hostLp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
697
698
699
700
701
  		goto out_unregister;
  	}
  
  	/* Initialize our request handler */
  	vio_setHandler(viomajorsubtype_cdio, vio_handle_cd_event);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
702
703
704
705
706
  	spin_lock_init(&viocd_reqlock);
  
  	ret = vio_register_driver(&viocd_driver);
  	if (ret)
  		goto out_free_info;
c7705f344   Denis V. Lunev   drivers: use non-...
707
708
  	proc_create("iSeries/viocd", S_IFREG|S_IRUGO, NULL,
  		    &proc_viocd_operations);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
709
710
711
  	return 0;
  
  out_free_info:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
712
713
714
715
716
717
718
719
720
721
722
  	vio_clearHandler(viomajorsubtype_cdio);
  	viopath_close(viopath_hostLp, viomajorsubtype_cdio, MAX_CD_REQ + 2);
  out_unregister:
  	unregister_blkdev(VIOCD_MAJOR, VIOCD_DEVICE);
  	return ret;
  }
  
  static void __exit viocd_exit(void)
  {
  	remove_proc_entry("iSeries/viocd", NULL);
  	vio_unregister_driver(&viocd_driver);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
723
724
725
726
727
728
729
730
  	viopath_close(viopath_hostLp, viomajorsubtype_cdio, MAX_CD_REQ + 2);
  	vio_clearHandler(viomajorsubtype_cdio);
  	unregister_blkdev(VIOCD_MAJOR, VIOCD_DEVICE);
  }
  
  module_init(viocd_init);
  module_exit(viocd_exit);
  MODULE_LICENSE("GPL");