blob: 117cfc8cd05a4c509876d137bf7fb3e2bd272382 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 pcd.c (c) 1997-8 Grant R. Guenther <grant@torque.net>
3 Under the terms of the GNU General Public License.
4
5 This is a high-level driver for parallel port ATAPI CD-ROM
6 drives based on chips supported by the paride module.
7
8 By default, the driver will autoprobe for a single parallel
9 port ATAPI CD-ROM drive, but if their individual parameters are
10 specified, the driver can handle up to 4 drives.
11
12 The behaviour of the pcd driver can be altered by setting
13 some parameters from the insmod command line. The following
14 parameters are adjustable:
15
16 drive0 These four arguments can be arrays of
17 drive1 1-6 integers as follows:
18 drive2
19 drive3 <prt>,<pro>,<uni>,<mod>,<slv>,<dly>
20
21 Where,
22
23 <prt> is the base of the parallel port address for
24 the corresponding drive. (required)
25
26 <pro> is the protocol number for the adapter that
27 supports this drive. These numbers are
28 logged by 'paride' when the protocol modules
29 are initialised. (0 if not given)
30
31 <uni> for those adapters that support chained
32 devices, this is the unit selector for the
33 chain of devices on the given port. It should
34 be zero for devices that don't support chaining.
35 (0 if not given)
36
37 <mod> this can be -1 to choose the best mode, or one
38 of the mode numbers supported by the adapter.
39 (-1 if not given)
40
41 <slv> ATAPI CD-ROMs can be jumpered to master or slave.
42 Set this to 0 to choose the master drive, 1 to
43 choose the slave, -1 (the default) to choose the
44 first drive found.
45
46 <dly> some parallel ports require the driver to
47 go more slowly. -1 sets a default value that
48 should work with the chosen protocol. Otherwise,
49 set this to a small integer, the larger it is
50 the slower the port i/o. In some cases, setting
51 this to zero will speed up the device. (default -1)
52
Masahiro Yamada505d3082017-03-09 16:16:33 -080053 major You may use this parameter to override the
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 default major number (46) that this driver
55 will use. Be sure to change the device
56 name as well.
57
58 name This parameter is a character string that
59 contains the name the kernel will use for this
60 device (in /proc output, for instance).
61 (default "pcd")
62
63 verbose This parameter controls the amount of logging
64 that the driver will do. Set it to 0 for
65 normal operation, 1 to see autoprobe progress
66 messages, or 2 to see additional debugging
67 output. (default 0)
68
69 nice This parameter controls the driver's use of
70 idle CPU time, at the expense of some speed.
71
Geert Uytterhoeven336ec132014-06-29 12:13:49 +020072 If this driver is built into the kernel, you can use the
73 following kernel command line parameters, with the same values
Linus Torvalds1da177e2005-04-16 15:20:36 -070074 as the corresponding module parameters listed above:
75
76 pcd.drive0
77 pcd.drive1
78 pcd.drive2
79 pcd.drive3
80 pcd.nice
81
82 In addition, you can use the parameter pcd.disable to disable
83 the driver entirely.
84
85*/
86
87/* Changes:
88
89 1.01 GRG 1998.01.24 Added test unit ready support
90 1.02 GRG 1998.05.06 Changes to pcd_completion, ready_wait,
91 and loosen interpretation of ATAPI
92 standard for clearing error status.
93 Use spinlocks. Eliminate sti().
94 1.03 GRG 1998.06.16 Eliminated an Ugh
95 1.04 GRG 1998.08.15 Added extra debugging, improvements to
96 pcd_completion, use HZ in loop timing
97 1.05 GRG 1998.08.16 Conformed to "Uniform CD-ROM" standard
98 1.06 GRG 1998.08.19 Added audio ioctl support
99 1.07 GRG 1998.09.24 Increased reset timeout, added jumbo support
100
101*/
102
103#define PCD_VERSION "1.07"
104#define PCD_MAJOR 46
105#define PCD_NAME "pcd"
106#define PCD_UNITS 4
107
108/* Here are things one can override from the insmod command.
109 Most are autoprobed by paride unless set here. Verbose is off
110 by default.
111
112*/
113
114static int verbose = 0;
115static int major = PCD_MAJOR;
116static char *name = PCD_NAME;
117static int nice = 0;
118static int disable = 0;
119
120static int drive0[6] = { 0, 0, 0, -1, -1, -1 };
121static int drive1[6] = { 0, 0, 0, -1, -1, -1 };
122static int drive2[6] = { 0, 0, 0, -1, -1, -1 };
123static int drive3[6] = { 0, 0, 0, -1, -1, -1 };
124
125static int (*drives[4])[6] = {&drive0, &drive1, &drive2, &drive3};
126static int pcd_drive_count;
127
128enum {D_PRT, D_PRO, D_UNI, D_MOD, D_SLV, D_DLY};
129
130/* end of parameters */
131
132#include <linux/module.h>
133#include <linux/init.h>
134#include <linux/errno.h>
135#include <linux/fs.h>
136#include <linux/kernel.h>
137#include <linux/delay.h>
138#include <linux/cdrom.h>
139#include <linux/spinlock.h>
Jens Axboe89c6b162018-10-15 08:38:52 -0600140#include <linux/blk-mq.h>
Arnd Bergmann2a48fc02010-06-02 14:28:52 +0200141#include <linux/mutex.h>
Linus Torvalds7c0f6ba2016-12-24 11:46:01 -0800142#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143
Arnd Bergmann2a48fc02010-06-02 14:28:52 +0200144static DEFINE_MUTEX(pcd_mutex);
Alexey Dobriyan671d40f2007-04-23 14:41:07 -0700145static DEFINE_SPINLOCK(pcd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146
Rusty Russell1b9fbaf2012-01-13 09:32:26 +1030147module_param(verbose, int, 0644);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148module_param(major, int, 0);
149module_param(name, charp, 0);
150module_param(nice, int, 0);
151module_param_array(drive0, int, NULL, 0);
152module_param_array(drive1, int, NULL, 0);
153module_param_array(drive2, int, NULL, 0);
154module_param_array(drive3, int, NULL, 0);
155
156#include "paride.h"
157#include "pseudo.h"
158
159#define PCD_RETRIES 5
160#define PCD_TMO 800 /* timeout in jiffies */
161#define PCD_DELAY 50 /* spin delay in uS */
162#define PCD_READY_TMO 20 /* in seconds */
163#define PCD_RESET_TMO 100 /* in tenths of a second */
164
165#define PCD_SPIN (1000000*PCD_TMO)/(HZ*PCD_DELAY)
166
167#define IDE_ERR 0x01
168#define IDE_DRQ 0x08
169#define IDE_READY 0x40
170#define IDE_BUSY 0x80
171
172static int pcd_open(struct cdrom_device_info *cdi, int purpose);
173static void pcd_release(struct cdrom_device_info *cdi);
174static int pcd_drive_status(struct cdrom_device_info *cdi, int slot_nr);
Tejun Heob1b56b92011-03-09 19:54:28 +0100175static unsigned int pcd_check_events(struct cdrom_device_info *cdi,
176 unsigned int clearing, int slot_nr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177static int pcd_tray_move(struct cdrom_device_info *cdi, int position);
178static int pcd_lock_door(struct cdrom_device_info *cdi, int lock);
179static int pcd_drive_reset(struct cdrom_device_info *cdi);
180static int pcd_get_mcn(struct cdrom_device_info *cdi, struct cdrom_mcn *mcn);
181static int pcd_audio_ioctl(struct cdrom_device_info *cdi,
182 unsigned int cmd, void *arg);
183static int pcd_packet(struct cdrom_device_info *cdi,
184 struct packet_command *cgc);
185
186static int pcd_detect(void);
187static void pcd_probe_capabilities(void);
188static void do_pcd_read_drq(void);
Jens Axboe89c6b162018-10-15 08:38:52 -0600189static blk_status_t pcd_queue_rq(struct blk_mq_hw_ctx *hctx,
190 const struct blk_mq_queue_data *bd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191static void do_pcd_read(void);
192
193struct pcd_unit {
194 struct pi_adapter pia; /* interface to paride layer */
195 struct pi_adapter *pi;
196 int drive; /* master/slave */
197 int last_sense; /* result of last request sense */
198 int changed; /* media change seen */
199 int present; /* does this unit exist ? */
200 char *name; /* pcd0, pcd1, etc */
201 struct cdrom_device_info info; /* uniform cdrom interface */
202 struct gendisk *disk;
Jens Axboe89c6b162018-10-15 08:38:52 -0600203 struct blk_mq_tag_set tag_set;
204 struct list_head rq_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205};
206
207static struct pcd_unit pcd[PCD_UNITS];
208
209static char pcd_scratch[64];
210static char pcd_buffer[2048]; /* raw block buffer */
211static int pcd_bufblk = -1; /* block in buffer, in CD units,
212 -1 for nothing there. See also
213 pd_unit.
214 */
215
216/* the variables below are used mainly in the I/O request engine, which
217 processes only one request at a time.
218*/
219
220static struct pcd_unit *pcd_current; /* current request's drive */
221static struct request *pcd_req;
222static int pcd_retries; /* retries on current request */
223static int pcd_busy; /* request being processed ? */
224static int pcd_sector; /* address of next requested sector */
225static int pcd_count; /* number of blocks still to do */
226static char *pcd_buf; /* buffer for request in progress */
Sudip Mukherjee9f4ba6b2015-05-20 20:57:01 +0530227static void *par_drv; /* reference of parport driver */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229/* kernel glue structures */
230
Al Viroc9acf902008-03-02 09:35:06 -0500231static int pcd_block_open(struct block_device *bdev, fmode_t mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232{
Al Viroc9acf902008-03-02 09:35:06 -0500233 struct pcd_unit *cd = bdev->bd_disk->private_data;
Arnd Bergmann6e9624b2010-08-07 18:25:34 +0200234 int ret;
235
Maurizio Lombardi2bbea6e2018-03-09 13:59:06 +0100236 check_disk_change(bdev);
237
Arnd Bergmann2a48fc02010-06-02 14:28:52 +0200238 mutex_lock(&pcd_mutex);
Arnd Bergmann6e9624b2010-08-07 18:25:34 +0200239 ret = cdrom_open(&cd->info, bdev, mode);
Arnd Bergmann2a48fc02010-06-02 14:28:52 +0200240 mutex_unlock(&pcd_mutex);
Arnd Bergmann6e9624b2010-08-07 18:25:34 +0200241
242 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243}
244
Al Virodb2a1442013-05-05 21:52:57 -0400245static void pcd_block_release(struct gendisk *disk, fmode_t mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246{
Al Viroc9acf902008-03-02 09:35:06 -0500247 struct pcd_unit *cd = disk->private_data;
Arnd Bergmann2a48fc02010-06-02 14:28:52 +0200248 mutex_lock(&pcd_mutex);
Al Viroc9acf902008-03-02 09:35:06 -0500249 cdrom_release(&cd->info, mode);
Arnd Bergmann2a48fc02010-06-02 14:28:52 +0200250 mutex_unlock(&pcd_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251}
252
Al Viroc9acf902008-03-02 09:35:06 -0500253static int pcd_block_ioctl(struct block_device *bdev, fmode_t mode,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 unsigned cmd, unsigned long arg)
255{
Al Viroc9acf902008-03-02 09:35:06 -0500256 struct pcd_unit *cd = bdev->bd_disk->private_data;
Arnd Bergmann8a6cfeb2010-07-08 10:18:46 +0200257 int ret;
258
Arnd Bergmann2a48fc02010-06-02 14:28:52 +0200259 mutex_lock(&pcd_mutex);
Arnd Bergmann8a6cfeb2010-07-08 10:18:46 +0200260 ret = cdrom_ioctl(&cd->info, bdev, mode, cmd, arg);
Arnd Bergmann2a48fc02010-06-02 14:28:52 +0200261 mutex_unlock(&pcd_mutex);
Arnd Bergmann8a6cfeb2010-07-08 10:18:46 +0200262
263 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264}
265
Tejun Heob1b56b92011-03-09 19:54:28 +0100266static unsigned int pcd_block_check_events(struct gendisk *disk,
267 unsigned int clearing)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268{
269 struct pcd_unit *cd = disk->private_data;
Tejun Heob1b56b92011-03-09 19:54:28 +0100270 return cdrom_check_events(&cd->info, clearing);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271}
272
Alexey Dobriyan83d5cde2009-09-21 17:01:13 -0700273static const struct block_device_operations pcd_bdops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 .owner = THIS_MODULE,
Al Viroc9acf902008-03-02 09:35:06 -0500275 .open = pcd_block_open,
276 .release = pcd_block_release,
Arnd Bergmann8a6cfeb2010-07-08 10:18:46 +0200277 .ioctl = pcd_block_ioctl,
Arnd Bergmann64cbfa92019-11-28 15:55:17 +0100278#ifdef CONFIG_COMPAT
279 .ioctl = blkdev_compat_ptr_ioctl,
280#endif
Tejun Heob1b56b92011-03-09 19:54:28 +0100281 .check_events = pcd_block_check_events,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282};
283
Kees Cook853fe1b2017-02-13 16:25:26 -0800284static const struct cdrom_device_ops pcd_dops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 .open = pcd_open,
286 .release = pcd_release,
287 .drive_status = pcd_drive_status,
Tejun Heob1b56b92011-03-09 19:54:28 +0100288 .check_events = pcd_check_events,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289 .tray_move = pcd_tray_move,
290 .lock_door = pcd_lock_door,
291 .get_mcn = pcd_get_mcn,
292 .reset = pcd_drive_reset,
293 .audio_ioctl = pcd_audio_ioctl,
294 .generic_packet = pcd_packet,
295 .capability = CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK |
296 CDC_MCN | CDC_MEDIA_CHANGED | CDC_RESET |
297 CDC_PLAY_AUDIO | CDC_GENERIC_PACKET | CDC_CD_R |
298 CDC_CD_RW,
299};
300
Jens Axboe89c6b162018-10-15 08:38:52 -0600301static const struct blk_mq_ops pcd_mq_ops = {
302 .queue_rq = pcd_queue_rq,
303};
304
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305static void pcd_init_units(void)
306{
307 struct pcd_unit *cd;
308 int unit;
309
310 pcd_drive_count = 0;
311 for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++) {
312 struct gendisk *disk = alloc_disk(1);
Jens Axboe89c6b162018-10-15 08:38:52 -0600313
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 if (!disk)
315 continue;
Jens Axboe89c6b162018-10-15 08:38:52 -0600316
317 disk->queue = blk_mq_init_sq_queue(&cd->tag_set, &pcd_mq_ops,
318 1, BLK_MQ_F_SHOULD_MERGE);
319 if (IS_ERR(disk->queue)) {
320 disk->queue = NULL;
zhengbind821cce2019-08-13 19:27:40 +0800321 put_disk(disk);
Omar Sandoval547b50a2017-03-27 23:28:44 -0700322 continue;
323 }
Jens Axboe89c6b162018-10-15 08:38:52 -0600324
325 INIT_LIST_HEAD(&cd->rq_list);
326 disk->queue->queuedata = cd;
Christoph Hellwig8fc45042017-06-19 09:26:26 +0200327 blk_queue_bounce_limit(disk->queue, BLK_BOUNCE_HIGH);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 cd->disk = disk;
329 cd->pi = &cd->pia;
330 cd->present = 0;
331 cd->last_sense = 0;
332 cd->changed = 1;
333 cd->drive = (*drives[unit])[D_SLV];
334 if ((*drives[unit])[D_PRT])
335 pcd_drive_count++;
336
337 cd->name = &cd->info.name[0];
338 snprintf(cd->name, sizeof(cd->info.name), "%s%d", name, unit);
339 cd->info.ops = &pcd_dops;
340 cd->info.handle = cd;
341 cd->info.speed = 0;
342 cd->info.capacity = 1;
343 cd->info.mask = 0;
344 disk->major = major;
345 disk->first_minor = unit;
346 strcpy(disk->disk_name, cd->name); /* umm... */
347 disk->fops = &pcd_bdops;
Tejun Heod4dc2102011-04-21 20:54:46 +0200348 disk->flags = GENHD_FL_BLOCK_EVENTS_ON_EXCL_WRITE;
Martin Wilck773008f2019-03-27 14:51:04 +0100349 disk->events = DISK_EVENT_MEDIA_CHANGE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 }
351}
352
353static int pcd_open(struct cdrom_device_info *cdi, int purpose)
354{
355 struct pcd_unit *cd = cdi->handle;
356 if (!cd->present)
357 return -ENODEV;
358 return 0;
359}
360
361static void pcd_release(struct cdrom_device_info *cdi)
362{
363}
364
365static inline int status_reg(struct pcd_unit *cd)
366{
367 return pi_read_regr(cd->pi, 1, 6);
368}
369
370static inline int read_reg(struct pcd_unit *cd, int reg)
371{
372 return pi_read_regr(cd->pi, 0, reg);
373}
374
375static inline void write_reg(struct pcd_unit *cd, int reg, int val)
376{
377 pi_write_regr(cd->pi, 0, reg, val);
378}
379
380static int pcd_wait(struct pcd_unit *cd, int go, int stop, char *fun, char *msg)
381{
382 int j, r, e, s, p;
383
384 j = 0;
385 while ((((r = status_reg(cd)) & go) || (stop && (!(r & stop))))
386 && (j++ < PCD_SPIN))
387 udelay(PCD_DELAY);
388
Roel Kluinc12ec0a2010-03-11 14:09:47 -0800389 if ((r & (IDE_ERR & stop)) || (j > PCD_SPIN)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 s = read_reg(cd, 7);
391 e = read_reg(cd, 1);
392 p = read_reg(cd, 2);
Roel Kluinc12ec0a2010-03-11 14:09:47 -0800393 if (j > PCD_SPIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 e |= 0x100;
395 if (fun)
396 printk("%s: %s %s: alt=0x%x stat=0x%x err=0x%x"
397 " loop=%d phase=%d\n",
398 cd->name, fun, msg, r, s, e, j, p);
399 return (s << 8) + r;
400 }
401 return 0;
402}
403
404static int pcd_command(struct pcd_unit *cd, char *cmd, int dlen, char *fun)
405{
406 pi_connect(cd->pi);
407
408 write_reg(cd, 6, 0xa0 + 0x10 * cd->drive);
409
410 if (pcd_wait(cd, IDE_BUSY | IDE_DRQ, 0, fun, "before command")) {
411 pi_disconnect(cd->pi);
412 return -1;
413 }
414
415 write_reg(cd, 4, dlen % 256);
416 write_reg(cd, 5, dlen / 256);
417 write_reg(cd, 7, 0xa0); /* ATAPI packet command */
418
419 if (pcd_wait(cd, IDE_BUSY, IDE_DRQ, fun, "command DRQ")) {
420 pi_disconnect(cd->pi);
421 return -1;
422 }
423
424 if (read_reg(cd, 2) != 1) {
425 printk("%s: %s: command phase error\n", cd->name, fun);
426 pi_disconnect(cd->pi);
427 return -1;
428 }
429
430 pi_write_block(cd->pi, cmd, 12);
431
432 return 0;
433}
434
435static int pcd_completion(struct pcd_unit *cd, char *buf, char *fun)
436{
437 int r, d, p, n, k, j;
438
439 r = -1;
440 k = 0;
441 j = 0;
442
443 if (!pcd_wait(cd, IDE_BUSY, IDE_DRQ | IDE_READY | IDE_ERR,
444 fun, "completion")) {
445 r = 0;
446 while (read_reg(cd, 7) & IDE_DRQ) {
447 d = read_reg(cd, 4) + 256 * read_reg(cd, 5);
448 n = (d + 3) & 0xfffc;
449 p = read_reg(cd, 2) & 3;
450
451 if ((p == 2) && (n > 0) && (j == 0)) {
452 pi_read_block(cd->pi, buf, n);
453 if (verbose > 1)
454 printk("%s: %s: Read %d bytes\n",
455 cd->name, fun, n);
456 r = 0;
457 j++;
458 } else {
459 if (verbose > 1)
460 printk
461 ("%s: %s: Unexpected phase %d, d=%d, k=%d\n",
462 cd->name, fun, p, d, k);
Marcin Slusarz49b3a3c2009-08-24 10:56:38 +0200463 if (verbose < 2)
464 printk_once(
465 "%s: WARNING: ATAPI phase errors\n",
466 cd->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 mdelay(1);
468 }
469 if (k++ > PCD_TMO) {
470 printk("%s: Stuck DRQ\n", cd->name);
471 break;
472 }
473 if (pcd_wait
474 (cd, IDE_BUSY, IDE_DRQ | IDE_READY | IDE_ERR, fun,
475 "completion")) {
476 r = -1;
477 break;
478 }
479 }
480 }
481
482 pi_disconnect(cd->pi);
483
484 return r;
485}
486
487static void pcd_req_sense(struct pcd_unit *cd, char *fun)
488{
489 char rs_cmd[12] = { 0x03, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0 };
490 char buf[16];
491 int r, c;
492
493 r = pcd_command(cd, rs_cmd, 16, "Request sense");
494 mdelay(1);
495 if (!r)
496 pcd_completion(cd, buf, "Request sense");
497
498 cd->last_sense = -1;
499 c = 2;
500 if (!r) {
501 if (fun)
502 printk("%s: %s: Sense key: %x, ASC: %x, ASQ: %x\n",
503 cd->name, fun, buf[2] & 0xf, buf[12], buf[13]);
504 c = buf[2] & 0xf;
505 cd->last_sense =
506 c | ((buf[12] & 0xff) << 8) | ((buf[13] & 0xff) << 16);
507 }
508 if ((c == 2) || (c == 6))
509 cd->changed = 1;
510}
511
512static int pcd_atapi(struct pcd_unit *cd, char *cmd, int dlen, char *buf, char *fun)
513{
514 int r;
515
516 r = pcd_command(cd, cmd, dlen, fun);
517 mdelay(1);
518 if (!r)
519 r = pcd_completion(cd, buf, fun);
520 if (r)
521 pcd_req_sense(cd, fun);
522
523 return r;
524}
525
526static int pcd_packet(struct cdrom_device_info *cdi, struct packet_command *cgc)
527{
528 return pcd_atapi(cdi->handle, cgc->cmd, cgc->buflen, cgc->buffer,
529 "generic packet");
530}
531
532#define DBMSG(msg) ((verbose>1)?(msg):NULL)
533
Tejun Heob1b56b92011-03-09 19:54:28 +0100534static unsigned int pcd_check_events(struct cdrom_device_info *cdi,
535 unsigned int clearing, int slot_nr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536{
537 struct pcd_unit *cd = cdi->handle;
538 int res = cd->changed;
539 if (res)
540 cd->changed = 0;
Tejun Heob1b56b92011-03-09 19:54:28 +0100541 return res ? DISK_EVENT_MEDIA_CHANGE : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542}
543
544static int pcd_lock_door(struct cdrom_device_info *cdi, int lock)
545{
546 char un_cmd[12] = { 0x1e, 0, 0, 0, lock, 0, 0, 0, 0, 0, 0, 0 };
547
548 return pcd_atapi(cdi->handle, un_cmd, 0, pcd_scratch,
549 lock ? "lock door" : "unlock door");
550}
551
552static int pcd_tray_move(struct cdrom_device_info *cdi, int position)
553{
554 char ej_cmd[12] = { 0x1b, 0, 0, 0, 3 - position, 0, 0, 0, 0, 0, 0, 0 };
555
556 return pcd_atapi(cdi->handle, ej_cmd, 0, pcd_scratch,
557 position ? "eject" : "close tray");
558}
559
560static void pcd_sleep(int cs)
561{
Nishanth Aravamudan86e84862005-09-10 00:27:28 -0700562 schedule_timeout_interruptible(cs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563}
564
565static int pcd_reset(struct pcd_unit *cd)
566{
567 int i, k, flg;
568 int expect[5] = { 1, 1, 1, 0x14, 0xeb };
569
570 pi_connect(cd->pi);
571 write_reg(cd, 6, 0xa0 + 0x10 * cd->drive);
572 write_reg(cd, 7, 8);
573
574 pcd_sleep(20 * HZ / 1000); /* delay a bit */
575
576 k = 0;
577 while ((k++ < PCD_RESET_TMO) && (status_reg(cd) & IDE_BUSY))
578 pcd_sleep(HZ / 10);
579
580 flg = 1;
581 for (i = 0; i < 5; i++)
582 flg &= (read_reg(cd, i + 1) == expect[i]);
583
584 if (verbose) {
585 printk("%s: Reset (%d) signature = ", cd->name, k);
586 for (i = 0; i < 5; i++)
587 printk("%3x", read_reg(cd, i + 1));
588 if (!flg)
589 printk(" (incorrect)");
590 printk("\n");
591 }
592
593 pi_disconnect(cd->pi);
594 return flg - 1;
595}
596
597static int pcd_drive_reset(struct cdrom_device_info *cdi)
598{
599 return pcd_reset(cdi->handle);
600}
601
602static int pcd_ready_wait(struct pcd_unit *cd, int tmo)
603{
604 char tr_cmd[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
605 int k, p;
606
607 k = 0;
608 while (k < tmo) {
609 cd->last_sense = 0;
610 pcd_atapi(cd, tr_cmd, 0, NULL, DBMSG("test unit ready"));
611 p = cd->last_sense;
612 if (!p)
613 return 0;
614 if (!(((p & 0xffff) == 0x0402) || ((p & 0xff) == 6)))
615 return p;
616 k++;
617 pcd_sleep(HZ);
618 }
619 return 0x000020; /* timeout */
620}
621
622static int pcd_drive_status(struct cdrom_device_info *cdi, int slot_nr)
623{
624 char rc_cmd[12] = { 0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
625 struct pcd_unit *cd = cdi->handle;
626
627 if (pcd_ready_wait(cd, PCD_READY_TMO))
628 return CDS_DRIVE_NOT_READY;
629 if (pcd_atapi(cd, rc_cmd, 8, pcd_scratch, DBMSG("check media")))
630 return CDS_NO_DISC;
631 return CDS_DISC_OK;
632}
633
634static int pcd_identify(struct pcd_unit *cd, char *id)
635{
636 int k, s;
637 char id_cmd[12] = { 0x12, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0 };
638
639 pcd_bufblk = -1;
640
641 s = pcd_atapi(cd, id_cmd, 36, pcd_buffer, "identify");
642
643 if (s)
644 return -1;
645 if ((pcd_buffer[0] & 0x1f) != 5) {
646 if (verbose)
647 printk("%s: %s is not a CD-ROM\n",
648 cd->name, cd->drive ? "Slave" : "Master");
649 return -1;
650 }
651 memcpy(id, pcd_buffer + 16, 16);
652 id[16] = 0;
653 k = 16;
654 while ((k >= 0) && (id[k] <= 0x20)) {
655 id[k] = 0;
656 k--;
657 }
658
659 printk("%s: %s: %s\n", cd->name, cd->drive ? "Slave" : "Master", id);
660
661 return 0;
662}
663
664/*
665 * returns 0, with id set if drive is detected
666 * -1, if drive detection failed
667 */
668static int pcd_probe(struct pcd_unit *cd, int ms, char *id)
669{
670 if (ms == -1) {
671 for (cd->drive = 0; cd->drive <= 1; cd->drive++)
672 if (!pcd_reset(cd) && !pcd_identify(cd, id))
673 return 0;
674 } else {
675 cd->drive = ms;
676 if (!pcd_reset(cd) && !pcd_identify(cd, id))
677 return 0;
678 }
679 return -1;
680}
681
682static void pcd_probe_capabilities(void)
683{
684 int unit, r;
685 char buffer[32];
686 char cmd[12] = { 0x5a, 1 << 3, 0x2a, 0, 0, 0, 0, 18, 0, 0, 0, 0 };
687 struct pcd_unit *cd;
688
689 for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++) {
690 if (!cd->present)
691 continue;
692 r = pcd_atapi(cd, cmd, 18, buffer, "mode sense capabilities");
693 if (r)
694 continue;
695 /* we should now have the cap page */
696 if ((buffer[11] & 1) == 0)
697 cd->info.mask |= CDC_CD_R;
698 if ((buffer[11] & 2) == 0)
699 cd->info.mask |= CDC_CD_RW;
700 if ((buffer[12] & 1) == 0)
701 cd->info.mask |= CDC_PLAY_AUDIO;
702 if ((buffer[14] & 1) == 0)
703 cd->info.mask |= CDC_LOCK;
704 if ((buffer[14] & 8) == 0)
705 cd->info.mask |= CDC_OPEN_TRAY;
706 if ((buffer[14] >> 6) == 0)
707 cd->info.mask |= CDC_CLOSE_TRAY;
708 }
709}
710
711static int pcd_detect(void)
712{
713 char id[18];
714 int k, unit;
715 struct pcd_unit *cd;
716
717 printk("%s: %s version %s, major %d, nice %d\n",
718 name, name, PCD_VERSION, major, nice);
719
Sudip Mukherjee9f4ba6b2015-05-20 20:57:01 +0530720 par_drv = pi_register_driver(name);
721 if (!par_drv) {
722 pr_err("failed to register %s driver\n", name);
723 return -1;
724 }
725
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 k = 0;
727 if (pcd_drive_count == 0) { /* nothing spec'd - so autoprobe for 1 */
728 cd = pcd;
zhengbin03754ea2019-08-13 19:23:12 +0800729 if (cd->disk && pi_init(cd->pi, 1, -1, -1, -1, -1, -1,
730 pcd_buffer, PI_PCD, verbose, cd->name)) {
731 if (!pcd_probe(cd, -1, id)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 cd->present = 1;
733 k++;
734 } else
735 pi_release(cd->pi);
736 }
737 } else {
738 for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++) {
739 int *conf = *drives[unit];
740 if (!conf[D_PRT])
741 continue;
zhengbin03754ea2019-08-13 19:23:12 +0800742 if (!cd->disk)
743 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744 if (!pi_init(cd->pi, 0, conf[D_PRT], conf[D_MOD],
745 conf[D_UNI], conf[D_PRO], conf[D_DLY],
746 pcd_buffer, PI_PCD, verbose, cd->name))
747 continue;
zhengbin03754ea2019-08-13 19:23:12 +0800748 if (!pcd_probe(cd, conf[D_SLV], id)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 cd->present = 1;
750 k++;
751 } else
752 pi_release(cd->pi);
753 }
754 }
755 if (k)
756 return 0;
757
758 printk("%s: No CD-ROM drive found\n", name);
Jens Axboe81b74ac2019-03-18 08:10:32 -0600759 for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++) {
YueHaibingf0d17622019-04-05 10:14:58 +0800760 if (!cd->disk)
761 continue;
Jens Axboe81b74ac2019-03-18 08:10:32 -0600762 blk_cleanup_queue(cd->disk->queue);
763 cd->disk->queue = NULL;
764 blk_mq_free_tag_set(&cd->tag_set);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 put_disk(cd->disk);
Jens Axboe81b74ac2019-03-18 08:10:32 -0600766 }
Sudip Mukherjee9f4ba6b2015-05-20 20:57:01 +0530767 pi_unregister_driver(par_drv);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 return -1;
769}
770
771/* I/O request processing */
Omar Sandoval547b50a2017-03-27 23:28:44 -0700772static int pcd_queue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773
Omar Sandoval547b50a2017-03-27 23:28:44 -0700774static int set_next_request(void)
775{
776 struct pcd_unit *cd;
Omar Sandoval547b50a2017-03-27 23:28:44 -0700777 int old_pos = pcd_queue;
778
779 do {
780 cd = &pcd[pcd_queue];
Omar Sandoval547b50a2017-03-27 23:28:44 -0700781 if (++pcd_queue == PCD_UNITS)
782 pcd_queue = 0;
Jens Axboe89c6b162018-10-15 08:38:52 -0600783 if (cd->present && !list_empty(&cd->rq_list)) {
784 pcd_req = list_first_entry(&cd->rq_list, struct request,
785 queuelist);
786 list_del_init(&pcd_req->queuelist);
787 blk_mq_start_request(pcd_req);
788 break;
Omar Sandoval547b50a2017-03-27 23:28:44 -0700789 }
790 } while (pcd_queue != old_pos);
791
792 return pcd_req != NULL;
793}
794
795static void pcd_request(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796{
Jens Axboe89c6b162018-10-15 08:38:52 -0600797 struct pcd_unit *cd;
798
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 if (pcd_busy)
800 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801
Jens Axboe89c6b162018-10-15 08:38:52 -0600802 if (!pcd_req && !set_next_request())
803 return;
804
805 cd = pcd_req->rq_disk->private_data;
806 if (cd != pcd_current)
807 pcd_bufblk = -1;
808 pcd_current = cd;
809 pcd_sector = blk_rq_pos(pcd_req);
810 pcd_count = blk_rq_cur_sectors(pcd_req);
811 pcd_buf = bio_data(pcd_req->bio);
812 pcd_busy = 1;
813 ps_set_intr(do_pcd_read, NULL, 0, nice);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814}
815
Jens Axboe89c6b162018-10-15 08:38:52 -0600816static blk_status_t pcd_queue_rq(struct blk_mq_hw_ctx *hctx,
817 const struct blk_mq_queue_data *bd)
Omar Sandoval547b50a2017-03-27 23:28:44 -0700818{
Jens Axboe89c6b162018-10-15 08:38:52 -0600819 struct pcd_unit *cd = hctx->queue->queuedata;
820
821 if (rq_data_dir(bd->rq) != READ) {
822 blk_mq_start_request(bd->rq);
823 return BLK_STS_IOERR;
824 }
825
826 spin_lock_irq(&pcd_lock);
827 list_add_tail(&bd->rq->queuelist, &cd->rq_list);
Omar Sandoval547b50a2017-03-27 23:28:44 -0700828 pcd_request();
Jens Axboe89c6b162018-10-15 08:38:52 -0600829 spin_unlock_irq(&pcd_lock);
830
831 return BLK_STS_OK;
Omar Sandoval547b50a2017-03-27 23:28:44 -0700832}
833
Christoph Hellwig2a842ac2017-06-03 09:38:04 +0200834static inline void next_request(blk_status_t err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835{
836 unsigned long saved_flags;
837
838 spin_lock_irqsave(&pcd_lock, saved_flags);
Jens Axboe89c6b162018-10-15 08:38:52 -0600839 if (!blk_update_request(pcd_req, err, blk_rq_cur_bytes(pcd_req))) {
840 __blk_mq_end_request(pcd_req, err);
Tejun Heob12d4f82009-05-08 11:54:06 +0900841 pcd_req = NULL;
Jens Axboe89c6b162018-10-15 08:38:52 -0600842 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843 pcd_busy = 0;
Omar Sandoval547b50a2017-03-27 23:28:44 -0700844 pcd_request();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 spin_unlock_irqrestore(&pcd_lock, saved_flags);
846}
847
848static int pcd_ready(void)
849{
850 return (((status_reg(pcd_current) & (IDE_BUSY | IDE_DRQ)) == IDE_DRQ));
851}
852
853static void pcd_transfer(void)
854{
855
856 while (pcd_count && (pcd_sector / 4 == pcd_bufblk)) {
857 int o = (pcd_sector % 4) * 512;
858 memcpy(pcd_buf, pcd_buffer + o, 512);
859 pcd_count--;
860 pcd_buf += 512;
861 pcd_sector++;
862 }
863}
864
865static void pcd_start(void)
866{
867 int b, i;
868 char rd_cmd[12] = { 0xa8, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 };
869
870 pcd_bufblk = pcd_sector / 4;
871 b = pcd_bufblk;
872 for (i = 0; i < 4; i++) {
873 rd_cmd[5 - i] = b & 0xff;
874 b = b >> 8;
875 }
876
877 if (pcd_command(pcd_current, rd_cmd, 2048, "read block")) {
878 pcd_bufblk = -1;
Christoph Hellwig2a842ac2017-06-03 09:38:04 +0200879 next_request(BLK_STS_IOERR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880 return;
881 }
882
883 mdelay(1);
884
885 ps_set_intr(do_pcd_read_drq, pcd_ready, PCD_TMO, nice);
886}
887
888static void do_pcd_read(void)
889{
890 pcd_busy = 1;
891 pcd_retries = 0;
892 pcd_transfer();
893 if (!pcd_count) {
Tejun Heof06d9a22009-04-23 11:05:19 +0900894 next_request(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895 return;
896 }
897
898 pi_do_claimed(pcd_current->pi, pcd_start);
899}
900
901static void do_pcd_read_drq(void)
902{
903 unsigned long saved_flags;
904
905 if (pcd_completion(pcd_current, pcd_buffer, "read block")) {
906 if (pcd_retries < PCD_RETRIES) {
907 mdelay(1);
908 pcd_retries++;
909 pi_do_claimed(pcd_current->pi, pcd_start);
910 return;
911 }
912 pcd_bufblk = -1;
Christoph Hellwig2a842ac2017-06-03 09:38:04 +0200913 next_request(BLK_STS_IOERR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914 return;
915 }
916
917 do_pcd_read();
918 spin_lock_irqsave(&pcd_lock, saved_flags);
Omar Sandoval547b50a2017-03-27 23:28:44 -0700919 pcd_request();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920 spin_unlock_irqrestore(&pcd_lock, saved_flags);
921}
922
923/* the audio_ioctl stuff is adapted from sr_ioctl.c */
924
925static int pcd_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void *arg)
926{
927 struct pcd_unit *cd = cdi->handle;
928
929 switch (cmd) {
930
931 case CDROMREADTOCHDR:
932
933 {
934 char cmd[12] =
935 { GPCMD_READ_TOC_PMA_ATIP, 0, 0, 0, 0, 0, 0, 0, 12,
936 0, 0, 0 };
937 struct cdrom_tochdr *tochdr =
938 (struct cdrom_tochdr *) arg;
939 char buffer[32];
940 int r;
941
942 r = pcd_atapi(cd, cmd, 12, buffer, "read toc header");
943
944 tochdr->cdth_trk0 = buffer[2];
945 tochdr->cdth_trk1 = buffer[3];
946
947 return r ? -EIO : 0;
948 }
949
950 case CDROMREADTOCENTRY:
951
952 {
953 char cmd[12] =
954 { GPCMD_READ_TOC_PMA_ATIP, 0, 0, 0, 0, 0, 0, 0, 12,
955 0, 0, 0 };
956
957 struct cdrom_tocentry *tocentry =
958 (struct cdrom_tocentry *) arg;
959 unsigned char buffer[32];
960 int r;
961
962 cmd[1] =
963 (tocentry->cdte_format == CDROM_MSF ? 0x02 : 0);
964 cmd[6] = tocentry->cdte_track;
965
966 r = pcd_atapi(cd, cmd, 12, buffer, "read toc entry");
967
968 tocentry->cdte_ctrl = buffer[5] & 0xf;
969 tocentry->cdte_adr = buffer[5] >> 4;
970 tocentry->cdte_datamode =
971 (tocentry->cdte_ctrl & 0x04) ? 1 : 0;
972 if (tocentry->cdte_format == CDROM_MSF) {
973 tocentry->cdte_addr.msf.minute = buffer[9];
974 tocentry->cdte_addr.msf.second = buffer[10];
975 tocentry->cdte_addr.msf.frame = buffer[11];
976 } else
977 tocentry->cdte_addr.lba =
978 (((((buffer[8] << 8) + buffer[9]) << 8)
979 + buffer[10]) << 8) + buffer[11];
980
981 return r ? -EIO : 0;
982 }
983
984 default:
985
986 return -ENOSYS;
987 }
988}
989
990static int pcd_get_mcn(struct cdrom_device_info *cdi, struct cdrom_mcn *mcn)
991{
992 char cmd[12] =
993 { GPCMD_READ_SUBCHANNEL, 0, 0x40, 2, 0, 0, 0, 0, 24, 0, 0, 0 };
994 char buffer[32];
995
996 if (pcd_atapi(cdi->handle, cmd, 24, buffer, "get mcn"))
997 return -EIO;
998
999 memcpy(mcn->medium_catalog_number, buffer + 9, 13);
1000 mcn->medium_catalog_number[13] = 0;
1001
1002 return 0;
1003}
1004
1005static int __init pcd_init(void)
1006{
1007 struct pcd_unit *cd;
1008 int unit;
1009
1010 if (disable)
Akinobu Mita8bca98c2006-12-06 20:36:43 -08001011 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012
1013 pcd_init_units();
1014
1015 if (pcd_detect())
Akinobu Mita8bca98c2006-12-06 20:36:43 -08001016 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017
1018 /* get the atapi capabilities page */
1019 pcd_probe_capabilities();
1020
1021 if (register_blkdev(major, name)) {
YueHaibingf0d17622019-04-05 10:14:58 +08001022 for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++) {
1023 if (!cd->disk)
1024 continue;
1025
1026 blk_cleanup_queue(cd->disk->queue);
1027 blk_mq_free_tag_set(&cd->tag_set);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 put_disk(cd->disk);
YueHaibingf0d17622019-04-05 10:14:58 +08001029 }
Akinobu Mita8bca98c2006-12-06 20:36:43 -08001030 return -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 }
1032
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++) {
1034 if (cd->present) {
1035 register_cdrom(&cd->info);
1036 cd->disk->private_data = cd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 add_disk(cd->disk);
1038 }
1039 }
1040
1041 return 0;
1042}
1043
1044static void __exit pcd_exit(void)
1045{
1046 struct pcd_unit *cd;
1047 int unit;
1048
1049 for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++) {
YueHaibingf0d17622019-04-05 10:14:58 +08001050 if (!cd->disk)
1051 continue;
1052
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053 if (cd->present) {
1054 del_gendisk(cd->disk);
1055 pi_release(cd->pi);
1056 unregister_cdrom(&cd->info);
1057 }
Omar Sandoval547b50a2017-03-27 23:28:44 -07001058 blk_cleanup_queue(cd->disk->queue);
Jens Axboe89c6b162018-10-15 08:38:52 -06001059 blk_mq_free_tag_set(&cd->tag_set);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060 put_disk(cd->disk);
1061 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 unregister_blkdev(major, name);
Sudip Mukherjee9f4ba6b2015-05-20 20:57:01 +05301063 pi_unregister_driver(par_drv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064}
1065
1066MODULE_LICENSE("GPL");
1067module_init(pcd_init)
1068module_exit(pcd_exit)