18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci	pcd.c	(c) 1997-8  Grant R. Guenther <grant@torque.net>
38c2ecf20Sopenharmony_ci		            Under the terms of the GNU General Public License.
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci	This is a high-level driver for parallel port ATAPI CD-ROM
68c2ecf20Sopenharmony_ci        drives based on chips supported by the paride module.
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci        By default, the driver will autoprobe for a single parallel
98c2ecf20Sopenharmony_ci        port ATAPI CD-ROM drive, but if their individual parameters are
108c2ecf20Sopenharmony_ci        specified, the driver can handle up to 4 drives.
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci        The behaviour of the pcd driver can be altered by setting
138c2ecf20Sopenharmony_ci        some parameters from the insmod command line.  The following
148c2ecf20Sopenharmony_ci        parameters are adjustable:
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci            drive0      These four arguments can be arrays of
178c2ecf20Sopenharmony_ci            drive1      1-6 integers as follows:
188c2ecf20Sopenharmony_ci            drive2
198c2ecf20Sopenharmony_ci            drive3      <prt>,<pro>,<uni>,<mod>,<slv>,<dly>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci                        Where,
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci                <prt>   is the base of the parallel port address for
248c2ecf20Sopenharmony_ci                        the corresponding drive.  (required)
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci                <pro>   is the protocol number for the adapter that
278c2ecf20Sopenharmony_ci                        supports this drive.  These numbers are
288c2ecf20Sopenharmony_ci                        logged by 'paride' when the protocol modules
298c2ecf20Sopenharmony_ci                        are initialised.  (0 if not given)
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci                <uni>   for those adapters that support chained
328c2ecf20Sopenharmony_ci                        devices, this is the unit selector for the
338c2ecf20Sopenharmony_ci                        chain of devices on the given port.  It should
348c2ecf20Sopenharmony_ci                        be zero for devices that don't support chaining.
358c2ecf20Sopenharmony_ci                        (0 if not given)
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci                <mod>   this can be -1 to choose the best mode, or one
388c2ecf20Sopenharmony_ci                        of the mode numbers supported by the adapter.
398c2ecf20Sopenharmony_ci                        (-1 if not given)
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci		<slv>   ATAPI CD-ROMs can be jumpered to master or slave.
428c2ecf20Sopenharmony_ci			Set this to 0 to choose the master drive, 1 to
438c2ecf20Sopenharmony_ci                        choose the slave, -1 (the default) to choose the
448c2ecf20Sopenharmony_ci			first drive found.
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci                <dly>   some parallel ports require the driver to
478c2ecf20Sopenharmony_ci                        go more slowly.  -1 sets a default value that
488c2ecf20Sopenharmony_ci                        should work with the chosen protocol.  Otherwise,
498c2ecf20Sopenharmony_ci                        set this to a small integer, the larger it is
508c2ecf20Sopenharmony_ci                        the slower the port i/o.  In some cases, setting
518c2ecf20Sopenharmony_ci                        this to zero will speed up the device. (default -1)
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci            major       You may use this parameter to override the
548c2ecf20Sopenharmony_ci                        default major number (46) that this driver
558c2ecf20Sopenharmony_ci                        will use.  Be sure to change the device
568c2ecf20Sopenharmony_ci                        name as well.
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci            name        This parameter is a character string that
598c2ecf20Sopenharmony_ci                        contains the name the kernel will use for this
608c2ecf20Sopenharmony_ci                        device (in /proc output, for instance).
618c2ecf20Sopenharmony_ci                        (default "pcd")
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci            verbose     This parameter controls the amount of logging
648c2ecf20Sopenharmony_ci                        that the driver will do.  Set it to 0 for
658c2ecf20Sopenharmony_ci                        normal operation, 1 to see autoprobe progress
668c2ecf20Sopenharmony_ci                        messages, or 2 to see additional debugging
678c2ecf20Sopenharmony_ci                        output.  (default 0)
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci            nice        This parameter controls the driver's use of
708c2ecf20Sopenharmony_ci                        idle CPU time, at the expense of some speed.
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	If this driver is built into the kernel, you can use the
738c2ecf20Sopenharmony_ci        following kernel command line parameters, with the same values
748c2ecf20Sopenharmony_ci        as the corresponding module parameters listed above:
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	    pcd.drive0
778c2ecf20Sopenharmony_ci	    pcd.drive1
788c2ecf20Sopenharmony_ci	    pcd.drive2
798c2ecf20Sopenharmony_ci	    pcd.drive3
808c2ecf20Sopenharmony_ci	    pcd.nice
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci        In addition, you can use the parameter pcd.disable to disable
838c2ecf20Sopenharmony_ci        the driver entirely.
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci*/
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/* Changes:
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	1.01	GRG 1998.01.24	Added test unit ready support
908c2ecf20Sopenharmony_ci	1.02    GRG 1998.05.06  Changes to pcd_completion, ready_wait,
918c2ecf20Sopenharmony_ci				and loosen interpretation of ATAPI
928c2ecf20Sopenharmony_ci			        standard for clearing error status.
938c2ecf20Sopenharmony_ci				Use spinlocks. Eliminate sti().
948c2ecf20Sopenharmony_ci	1.03    GRG 1998.06.16  Eliminated an Ugh
958c2ecf20Sopenharmony_ci	1.04	GRG 1998.08.15  Added extra debugging, improvements to
968c2ecf20Sopenharmony_ci				pcd_completion, use HZ in loop timing
978c2ecf20Sopenharmony_ci	1.05	GRG 1998.08.16	Conformed to "Uniform CD-ROM" standard
988c2ecf20Sopenharmony_ci	1.06    GRG 1998.08.19  Added audio ioctl support
998c2ecf20Sopenharmony_ci	1.07    GRG 1998.09.24  Increased reset timeout, added jumbo support
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci*/
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci#define	PCD_VERSION	"1.07"
1048c2ecf20Sopenharmony_ci#define PCD_MAJOR	46
1058c2ecf20Sopenharmony_ci#define PCD_NAME	"pcd"
1068c2ecf20Sopenharmony_ci#define PCD_UNITS	4
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci/* Here are things one can override from the insmod command.
1098c2ecf20Sopenharmony_ci   Most are autoprobed by paride unless set here.  Verbose is off
1108c2ecf20Sopenharmony_ci   by default.
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci*/
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic int verbose = 0;
1158c2ecf20Sopenharmony_cistatic int major = PCD_MAJOR;
1168c2ecf20Sopenharmony_cistatic char *name = PCD_NAME;
1178c2ecf20Sopenharmony_cistatic int nice = 0;
1188c2ecf20Sopenharmony_cistatic int disable = 0;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic int drive0[6] = { 0, 0, 0, -1, -1, -1 };
1218c2ecf20Sopenharmony_cistatic int drive1[6] = { 0, 0, 0, -1, -1, -1 };
1228c2ecf20Sopenharmony_cistatic int drive2[6] = { 0, 0, 0, -1, -1, -1 };
1238c2ecf20Sopenharmony_cistatic int drive3[6] = { 0, 0, 0, -1, -1, -1 };
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic int (*drives[4])[6] = {&drive0, &drive1, &drive2, &drive3};
1268c2ecf20Sopenharmony_cistatic int pcd_drive_count;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cienum {D_PRT, D_PRO, D_UNI, D_MOD, D_SLV, D_DLY};
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci/* end of parameters */
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci#include <linux/module.h>
1338c2ecf20Sopenharmony_ci#include <linux/init.h>
1348c2ecf20Sopenharmony_ci#include <linux/errno.h>
1358c2ecf20Sopenharmony_ci#include <linux/fs.h>
1368c2ecf20Sopenharmony_ci#include <linux/kernel.h>
1378c2ecf20Sopenharmony_ci#include <linux/delay.h>
1388c2ecf20Sopenharmony_ci#include <linux/cdrom.h>
1398c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
1408c2ecf20Sopenharmony_ci#include <linux/blk-mq.h>
1418c2ecf20Sopenharmony_ci#include <linux/mutex.h>
1428c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(pcd_mutex);
1458c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(pcd_lock);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cimodule_param(verbose, int, 0644);
1488c2ecf20Sopenharmony_cimodule_param(major, int, 0);
1498c2ecf20Sopenharmony_cimodule_param(name, charp, 0);
1508c2ecf20Sopenharmony_cimodule_param(nice, int, 0);
1518c2ecf20Sopenharmony_cimodule_param_array(drive0, int, NULL, 0);
1528c2ecf20Sopenharmony_cimodule_param_array(drive1, int, NULL, 0);
1538c2ecf20Sopenharmony_cimodule_param_array(drive2, int, NULL, 0);
1548c2ecf20Sopenharmony_cimodule_param_array(drive3, int, NULL, 0);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci#include "paride.h"
1578c2ecf20Sopenharmony_ci#include "pseudo.h"
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci#define PCD_RETRIES	     5
1608c2ecf20Sopenharmony_ci#define PCD_TMO		   800	/* timeout in jiffies */
1618c2ecf20Sopenharmony_ci#define PCD_DELAY           50	/* spin delay in uS */
1628c2ecf20Sopenharmony_ci#define PCD_READY_TMO	    20	/* in seconds */
1638c2ecf20Sopenharmony_ci#define PCD_RESET_TMO	   100	/* in tenths of a second */
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci#define PCD_SPIN	(1000000*PCD_TMO)/(HZ*PCD_DELAY)
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci#define IDE_ERR		0x01
1688c2ecf20Sopenharmony_ci#define IDE_DRQ         0x08
1698c2ecf20Sopenharmony_ci#define IDE_READY       0x40
1708c2ecf20Sopenharmony_ci#define IDE_BUSY        0x80
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic int pcd_open(struct cdrom_device_info *cdi, int purpose);
1738c2ecf20Sopenharmony_cistatic void pcd_release(struct cdrom_device_info *cdi);
1748c2ecf20Sopenharmony_cistatic int pcd_drive_status(struct cdrom_device_info *cdi, int slot_nr);
1758c2ecf20Sopenharmony_cistatic unsigned int pcd_check_events(struct cdrom_device_info *cdi,
1768c2ecf20Sopenharmony_ci				     unsigned int clearing, int slot_nr);
1778c2ecf20Sopenharmony_cistatic int pcd_tray_move(struct cdrom_device_info *cdi, int position);
1788c2ecf20Sopenharmony_cistatic int pcd_lock_door(struct cdrom_device_info *cdi, int lock);
1798c2ecf20Sopenharmony_cistatic int pcd_drive_reset(struct cdrom_device_info *cdi);
1808c2ecf20Sopenharmony_cistatic int pcd_get_mcn(struct cdrom_device_info *cdi, struct cdrom_mcn *mcn);
1818c2ecf20Sopenharmony_cistatic int pcd_audio_ioctl(struct cdrom_device_info *cdi,
1828c2ecf20Sopenharmony_ci			   unsigned int cmd, void *arg);
1838c2ecf20Sopenharmony_cistatic int pcd_packet(struct cdrom_device_info *cdi,
1848c2ecf20Sopenharmony_ci		      struct packet_command *cgc);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic int pcd_detect(void);
1878c2ecf20Sopenharmony_cistatic void pcd_probe_capabilities(void);
1888c2ecf20Sopenharmony_cistatic void do_pcd_read_drq(void);
1898c2ecf20Sopenharmony_cistatic blk_status_t pcd_queue_rq(struct blk_mq_hw_ctx *hctx,
1908c2ecf20Sopenharmony_ci				 const struct blk_mq_queue_data *bd);
1918c2ecf20Sopenharmony_cistatic void do_pcd_read(void);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistruct pcd_unit {
1948c2ecf20Sopenharmony_ci	struct pi_adapter pia;	/* interface to paride layer */
1958c2ecf20Sopenharmony_ci	struct pi_adapter *pi;
1968c2ecf20Sopenharmony_ci	int drive;		/* master/slave */
1978c2ecf20Sopenharmony_ci	int last_sense;		/* result of last request sense */
1988c2ecf20Sopenharmony_ci	int changed;		/* media change seen */
1998c2ecf20Sopenharmony_ci	int present;		/* does this unit exist ? */
2008c2ecf20Sopenharmony_ci	char *name;		/* pcd0, pcd1, etc */
2018c2ecf20Sopenharmony_ci	struct cdrom_device_info info;	/* uniform cdrom interface */
2028c2ecf20Sopenharmony_ci	struct gendisk *disk;
2038c2ecf20Sopenharmony_ci	struct blk_mq_tag_set tag_set;
2048c2ecf20Sopenharmony_ci	struct list_head rq_list;
2058c2ecf20Sopenharmony_ci};
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic struct pcd_unit pcd[PCD_UNITS];
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic char pcd_scratch[64];
2108c2ecf20Sopenharmony_cistatic char pcd_buffer[2048];	/* raw block buffer */
2118c2ecf20Sopenharmony_cistatic int pcd_bufblk = -1;	/* block in buffer, in CD units,
2128c2ecf20Sopenharmony_ci				   -1 for nothing there. See also
2138c2ecf20Sopenharmony_ci				   pd_unit.
2148c2ecf20Sopenharmony_ci				 */
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci/* the variables below are used mainly in the I/O request engine, which
2178c2ecf20Sopenharmony_ci   processes only one request at a time.
2188c2ecf20Sopenharmony_ci*/
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic struct pcd_unit *pcd_current; /* current request's drive */
2218c2ecf20Sopenharmony_cistatic struct request *pcd_req;
2228c2ecf20Sopenharmony_cistatic int pcd_retries;		/* retries on current request */
2238c2ecf20Sopenharmony_cistatic int pcd_busy;		/* request being processed ? */
2248c2ecf20Sopenharmony_cistatic int pcd_sector;		/* address of next requested sector */
2258c2ecf20Sopenharmony_cistatic int pcd_count;		/* number of blocks still to do */
2268c2ecf20Sopenharmony_cistatic char *pcd_buf;		/* buffer for request in progress */
2278c2ecf20Sopenharmony_cistatic void *par_drv;		/* reference of parport driver */
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci/* kernel glue structures */
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic int pcd_block_open(struct block_device *bdev, fmode_t mode)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	struct pcd_unit *cd = bdev->bd_disk->private_data;
2348c2ecf20Sopenharmony_ci	int ret;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	bdev_check_media_change(bdev);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	mutex_lock(&pcd_mutex);
2398c2ecf20Sopenharmony_ci	ret = cdrom_open(&cd->info, bdev, mode);
2408c2ecf20Sopenharmony_ci	mutex_unlock(&pcd_mutex);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	return ret;
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cistatic void pcd_block_release(struct gendisk *disk, fmode_t mode)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	struct pcd_unit *cd = disk->private_data;
2488c2ecf20Sopenharmony_ci	mutex_lock(&pcd_mutex);
2498c2ecf20Sopenharmony_ci	cdrom_release(&cd->info, mode);
2508c2ecf20Sopenharmony_ci	mutex_unlock(&pcd_mutex);
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic int pcd_block_ioctl(struct block_device *bdev, fmode_t mode,
2548c2ecf20Sopenharmony_ci				unsigned cmd, unsigned long arg)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	struct pcd_unit *cd = bdev->bd_disk->private_data;
2578c2ecf20Sopenharmony_ci	int ret;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	mutex_lock(&pcd_mutex);
2608c2ecf20Sopenharmony_ci	ret = cdrom_ioctl(&cd->info, bdev, mode, cmd, arg);
2618c2ecf20Sopenharmony_ci	mutex_unlock(&pcd_mutex);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	return ret;
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic unsigned int pcd_block_check_events(struct gendisk *disk,
2678c2ecf20Sopenharmony_ci					   unsigned int clearing)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	struct pcd_unit *cd = disk->private_data;
2708c2ecf20Sopenharmony_ci	return cdrom_check_events(&cd->info, clearing);
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic const struct block_device_operations pcd_bdops = {
2748c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
2758c2ecf20Sopenharmony_ci	.open		= pcd_block_open,
2768c2ecf20Sopenharmony_ci	.release	= pcd_block_release,
2778c2ecf20Sopenharmony_ci	.ioctl		= pcd_block_ioctl,
2788c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
2798c2ecf20Sopenharmony_ci	.compat_ioctl	= blkdev_compat_ptr_ioctl,
2808c2ecf20Sopenharmony_ci#endif
2818c2ecf20Sopenharmony_ci	.check_events	= pcd_block_check_events,
2828c2ecf20Sopenharmony_ci};
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic const struct cdrom_device_ops pcd_dops = {
2858c2ecf20Sopenharmony_ci	.open		= pcd_open,
2868c2ecf20Sopenharmony_ci	.release	= pcd_release,
2878c2ecf20Sopenharmony_ci	.drive_status	= pcd_drive_status,
2888c2ecf20Sopenharmony_ci	.check_events	= pcd_check_events,
2898c2ecf20Sopenharmony_ci	.tray_move	= pcd_tray_move,
2908c2ecf20Sopenharmony_ci	.lock_door	= pcd_lock_door,
2918c2ecf20Sopenharmony_ci	.get_mcn	= pcd_get_mcn,
2928c2ecf20Sopenharmony_ci	.reset		= pcd_drive_reset,
2938c2ecf20Sopenharmony_ci	.audio_ioctl	= pcd_audio_ioctl,
2948c2ecf20Sopenharmony_ci	.generic_packet	= pcd_packet,
2958c2ecf20Sopenharmony_ci	.capability	= CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK |
2968c2ecf20Sopenharmony_ci			  CDC_MCN | CDC_MEDIA_CHANGED | CDC_RESET |
2978c2ecf20Sopenharmony_ci			  CDC_PLAY_AUDIO | CDC_GENERIC_PACKET | CDC_CD_R |
2988c2ecf20Sopenharmony_ci			  CDC_CD_RW,
2998c2ecf20Sopenharmony_ci};
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic const struct blk_mq_ops pcd_mq_ops = {
3028c2ecf20Sopenharmony_ci	.queue_rq	= pcd_queue_rq,
3038c2ecf20Sopenharmony_ci};
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_cistatic void pcd_init_units(void)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	struct pcd_unit *cd;
3088c2ecf20Sopenharmony_ci	int unit;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	pcd_drive_count = 0;
3118c2ecf20Sopenharmony_ci	for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++) {
3128c2ecf20Sopenharmony_ci		struct gendisk *disk = alloc_disk(1);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci		if (!disk)
3158c2ecf20Sopenharmony_ci			continue;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci		disk->queue = blk_mq_init_sq_queue(&cd->tag_set, &pcd_mq_ops,
3188c2ecf20Sopenharmony_ci						   1, BLK_MQ_F_SHOULD_MERGE);
3198c2ecf20Sopenharmony_ci		if (IS_ERR(disk->queue)) {
3208c2ecf20Sopenharmony_ci			disk->queue = NULL;
3218c2ecf20Sopenharmony_ci			put_disk(disk);
3228c2ecf20Sopenharmony_ci			continue;
3238c2ecf20Sopenharmony_ci		}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&cd->rq_list);
3268c2ecf20Sopenharmony_ci		disk->queue->queuedata = cd;
3278c2ecf20Sopenharmony_ci		blk_queue_bounce_limit(disk->queue, BLK_BOUNCE_HIGH);
3288c2ecf20Sopenharmony_ci		cd->disk = disk;
3298c2ecf20Sopenharmony_ci		cd->pi = &cd->pia;
3308c2ecf20Sopenharmony_ci		cd->present = 0;
3318c2ecf20Sopenharmony_ci		cd->last_sense = 0;
3328c2ecf20Sopenharmony_ci		cd->changed = 1;
3338c2ecf20Sopenharmony_ci		cd->drive = (*drives[unit])[D_SLV];
3348c2ecf20Sopenharmony_ci		if ((*drives[unit])[D_PRT])
3358c2ecf20Sopenharmony_ci			pcd_drive_count++;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci		cd->name = &cd->info.name[0];
3388c2ecf20Sopenharmony_ci		snprintf(cd->name, sizeof(cd->info.name), "%s%d", name, unit);
3398c2ecf20Sopenharmony_ci		cd->info.ops = &pcd_dops;
3408c2ecf20Sopenharmony_ci		cd->info.handle = cd;
3418c2ecf20Sopenharmony_ci		cd->info.speed = 0;
3428c2ecf20Sopenharmony_ci		cd->info.capacity = 1;
3438c2ecf20Sopenharmony_ci		cd->info.mask = 0;
3448c2ecf20Sopenharmony_ci		disk->major = major;
3458c2ecf20Sopenharmony_ci		disk->first_minor = unit;
3468c2ecf20Sopenharmony_ci		strcpy(disk->disk_name, cd->name);	/* umm... */
3478c2ecf20Sopenharmony_ci		disk->fops = &pcd_bdops;
3488c2ecf20Sopenharmony_ci		disk->flags = GENHD_FL_BLOCK_EVENTS_ON_EXCL_WRITE;
3498c2ecf20Sopenharmony_ci		disk->events = DISK_EVENT_MEDIA_CHANGE;
3508c2ecf20Sopenharmony_ci	}
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_cistatic int pcd_open(struct cdrom_device_info *cdi, int purpose)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	struct pcd_unit *cd = cdi->handle;
3568c2ecf20Sopenharmony_ci	if (!cd->present)
3578c2ecf20Sopenharmony_ci		return -ENODEV;
3588c2ecf20Sopenharmony_ci	return 0;
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic void pcd_release(struct cdrom_device_info *cdi)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_cistatic inline int status_reg(struct pcd_unit *cd)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	return pi_read_regr(cd->pi, 1, 6);
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_cistatic inline int read_reg(struct pcd_unit *cd, int reg)
3718c2ecf20Sopenharmony_ci{
3728c2ecf20Sopenharmony_ci	return pi_read_regr(cd->pi, 0, reg);
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cistatic inline void write_reg(struct pcd_unit *cd, int reg, int val)
3768c2ecf20Sopenharmony_ci{
3778c2ecf20Sopenharmony_ci	pi_write_regr(cd->pi, 0, reg, val);
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic int pcd_wait(struct pcd_unit *cd, int go, int stop, char *fun, char *msg)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	int j, r, e, s, p;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	j = 0;
3858c2ecf20Sopenharmony_ci	while ((((r = status_reg(cd)) & go) || (stop && (!(r & stop))))
3868c2ecf20Sopenharmony_ci	       && (j++ < PCD_SPIN))
3878c2ecf20Sopenharmony_ci		udelay(PCD_DELAY);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	if ((r & (IDE_ERR & stop)) || (j > PCD_SPIN)) {
3908c2ecf20Sopenharmony_ci		s = read_reg(cd, 7);
3918c2ecf20Sopenharmony_ci		e = read_reg(cd, 1);
3928c2ecf20Sopenharmony_ci		p = read_reg(cd, 2);
3938c2ecf20Sopenharmony_ci		if (j > PCD_SPIN)
3948c2ecf20Sopenharmony_ci			e |= 0x100;
3958c2ecf20Sopenharmony_ci		if (fun)
3968c2ecf20Sopenharmony_ci			printk("%s: %s %s: alt=0x%x stat=0x%x err=0x%x"
3978c2ecf20Sopenharmony_ci			       " loop=%d phase=%d\n",
3988c2ecf20Sopenharmony_ci			       cd->name, fun, msg, r, s, e, j, p);
3998c2ecf20Sopenharmony_ci		return (s << 8) + r;
4008c2ecf20Sopenharmony_ci	}
4018c2ecf20Sopenharmony_ci	return 0;
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistatic int pcd_command(struct pcd_unit *cd, char *cmd, int dlen, char *fun)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	pi_connect(cd->pi);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	write_reg(cd, 6, 0xa0 + 0x10 * cd->drive);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	if (pcd_wait(cd, IDE_BUSY | IDE_DRQ, 0, fun, "before command")) {
4118c2ecf20Sopenharmony_ci		pi_disconnect(cd->pi);
4128c2ecf20Sopenharmony_ci		return -1;
4138c2ecf20Sopenharmony_ci	}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	write_reg(cd, 4, dlen % 256);
4168c2ecf20Sopenharmony_ci	write_reg(cd, 5, dlen / 256);
4178c2ecf20Sopenharmony_ci	write_reg(cd, 7, 0xa0);	/* ATAPI packet command */
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	if (pcd_wait(cd, IDE_BUSY, IDE_DRQ, fun, "command DRQ")) {
4208c2ecf20Sopenharmony_ci		pi_disconnect(cd->pi);
4218c2ecf20Sopenharmony_ci		return -1;
4228c2ecf20Sopenharmony_ci	}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	if (read_reg(cd, 2) != 1) {
4258c2ecf20Sopenharmony_ci		printk("%s: %s: command phase error\n", cd->name, fun);
4268c2ecf20Sopenharmony_ci		pi_disconnect(cd->pi);
4278c2ecf20Sopenharmony_ci		return -1;
4288c2ecf20Sopenharmony_ci	}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	pi_write_block(cd->pi, cmd, 12);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	return 0;
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic int pcd_completion(struct pcd_unit *cd, char *buf, char *fun)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	int r, d, p, n, k, j;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	r = -1;
4408c2ecf20Sopenharmony_ci	k = 0;
4418c2ecf20Sopenharmony_ci	j = 0;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	if (!pcd_wait(cd, IDE_BUSY, IDE_DRQ | IDE_READY | IDE_ERR,
4448c2ecf20Sopenharmony_ci		      fun, "completion")) {
4458c2ecf20Sopenharmony_ci		r = 0;
4468c2ecf20Sopenharmony_ci		while (read_reg(cd, 7) & IDE_DRQ) {
4478c2ecf20Sopenharmony_ci			d = read_reg(cd, 4) + 256 * read_reg(cd, 5);
4488c2ecf20Sopenharmony_ci			n = (d + 3) & 0xfffc;
4498c2ecf20Sopenharmony_ci			p = read_reg(cd, 2) & 3;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci			if ((p == 2) && (n > 0) && (j == 0)) {
4528c2ecf20Sopenharmony_ci				pi_read_block(cd->pi, buf, n);
4538c2ecf20Sopenharmony_ci				if (verbose > 1)
4548c2ecf20Sopenharmony_ci					printk("%s: %s: Read %d bytes\n",
4558c2ecf20Sopenharmony_ci					       cd->name, fun, n);
4568c2ecf20Sopenharmony_ci				r = 0;
4578c2ecf20Sopenharmony_ci				j++;
4588c2ecf20Sopenharmony_ci			} else {
4598c2ecf20Sopenharmony_ci				if (verbose > 1)
4608c2ecf20Sopenharmony_ci					printk
4618c2ecf20Sopenharmony_ci					    ("%s: %s: Unexpected phase %d, d=%d, k=%d\n",
4628c2ecf20Sopenharmony_ci					     cd->name, fun, p, d, k);
4638c2ecf20Sopenharmony_ci				if (verbose < 2)
4648c2ecf20Sopenharmony_ci					printk_once(
4658c2ecf20Sopenharmony_ci					    "%s: WARNING: ATAPI phase errors\n",
4668c2ecf20Sopenharmony_ci					    cd->name);
4678c2ecf20Sopenharmony_ci				mdelay(1);
4688c2ecf20Sopenharmony_ci			}
4698c2ecf20Sopenharmony_ci			if (k++ > PCD_TMO) {
4708c2ecf20Sopenharmony_ci				printk("%s: Stuck DRQ\n", cd->name);
4718c2ecf20Sopenharmony_ci				break;
4728c2ecf20Sopenharmony_ci			}
4738c2ecf20Sopenharmony_ci			if (pcd_wait
4748c2ecf20Sopenharmony_ci			    (cd, IDE_BUSY, IDE_DRQ | IDE_READY | IDE_ERR, fun,
4758c2ecf20Sopenharmony_ci			     "completion")) {
4768c2ecf20Sopenharmony_ci				r = -1;
4778c2ecf20Sopenharmony_ci				break;
4788c2ecf20Sopenharmony_ci			}
4798c2ecf20Sopenharmony_ci		}
4808c2ecf20Sopenharmony_ci	}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	pi_disconnect(cd->pi);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	return r;
4858c2ecf20Sopenharmony_ci}
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_cistatic void pcd_req_sense(struct pcd_unit *cd, char *fun)
4888c2ecf20Sopenharmony_ci{
4898c2ecf20Sopenharmony_ci	char rs_cmd[12] = { 0x03, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0 };
4908c2ecf20Sopenharmony_ci	char buf[16];
4918c2ecf20Sopenharmony_ci	int r, c;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	r = pcd_command(cd, rs_cmd, 16, "Request sense");
4948c2ecf20Sopenharmony_ci	mdelay(1);
4958c2ecf20Sopenharmony_ci	if (!r)
4968c2ecf20Sopenharmony_ci		pcd_completion(cd, buf, "Request sense");
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	cd->last_sense = -1;
4998c2ecf20Sopenharmony_ci	c = 2;
5008c2ecf20Sopenharmony_ci	if (!r) {
5018c2ecf20Sopenharmony_ci		if (fun)
5028c2ecf20Sopenharmony_ci			printk("%s: %s: Sense key: %x, ASC: %x, ASQ: %x\n",
5038c2ecf20Sopenharmony_ci			       cd->name, fun, buf[2] & 0xf, buf[12], buf[13]);
5048c2ecf20Sopenharmony_ci		c = buf[2] & 0xf;
5058c2ecf20Sopenharmony_ci		cd->last_sense =
5068c2ecf20Sopenharmony_ci		    c | ((buf[12] & 0xff) << 8) | ((buf[13] & 0xff) << 16);
5078c2ecf20Sopenharmony_ci	}
5088c2ecf20Sopenharmony_ci	if ((c == 2) || (c == 6))
5098c2ecf20Sopenharmony_ci		cd->changed = 1;
5108c2ecf20Sopenharmony_ci}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_cistatic int pcd_atapi(struct pcd_unit *cd, char *cmd, int dlen, char *buf, char *fun)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	int r;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	r = pcd_command(cd, cmd, dlen, fun);
5178c2ecf20Sopenharmony_ci	mdelay(1);
5188c2ecf20Sopenharmony_ci	if (!r)
5198c2ecf20Sopenharmony_ci		r = pcd_completion(cd, buf, fun);
5208c2ecf20Sopenharmony_ci	if (r)
5218c2ecf20Sopenharmony_ci		pcd_req_sense(cd, fun);
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	return r;
5248c2ecf20Sopenharmony_ci}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_cistatic int pcd_packet(struct cdrom_device_info *cdi, struct packet_command *cgc)
5278c2ecf20Sopenharmony_ci{
5288c2ecf20Sopenharmony_ci	return pcd_atapi(cdi->handle, cgc->cmd, cgc->buflen, cgc->buffer,
5298c2ecf20Sopenharmony_ci			 "generic packet");
5308c2ecf20Sopenharmony_ci}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci#define DBMSG(msg)	((verbose>1)?(msg):NULL)
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_cistatic unsigned int pcd_check_events(struct cdrom_device_info *cdi,
5358c2ecf20Sopenharmony_ci				     unsigned int clearing, int slot_nr)
5368c2ecf20Sopenharmony_ci{
5378c2ecf20Sopenharmony_ci	struct pcd_unit *cd = cdi->handle;
5388c2ecf20Sopenharmony_ci	int res = cd->changed;
5398c2ecf20Sopenharmony_ci	if (res)
5408c2ecf20Sopenharmony_ci		cd->changed = 0;
5418c2ecf20Sopenharmony_ci	return res ? DISK_EVENT_MEDIA_CHANGE : 0;
5428c2ecf20Sopenharmony_ci}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_cistatic int pcd_lock_door(struct cdrom_device_info *cdi, int lock)
5458c2ecf20Sopenharmony_ci{
5468c2ecf20Sopenharmony_ci	char un_cmd[12] = { 0x1e, 0, 0, 0, lock, 0, 0, 0, 0, 0, 0, 0 };
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	return pcd_atapi(cdi->handle, un_cmd, 0, pcd_scratch,
5498c2ecf20Sopenharmony_ci			 lock ? "lock door" : "unlock door");
5508c2ecf20Sopenharmony_ci}
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_cistatic int pcd_tray_move(struct cdrom_device_info *cdi, int position)
5538c2ecf20Sopenharmony_ci{
5548c2ecf20Sopenharmony_ci	char ej_cmd[12] = { 0x1b, 0, 0, 0, 3 - position, 0, 0, 0, 0, 0, 0, 0 };
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	return pcd_atapi(cdi->handle, ej_cmd, 0, pcd_scratch,
5578c2ecf20Sopenharmony_ci			 position ? "eject" : "close tray");
5588c2ecf20Sopenharmony_ci}
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_cistatic void pcd_sleep(int cs)
5618c2ecf20Sopenharmony_ci{
5628c2ecf20Sopenharmony_ci	schedule_timeout_interruptible(cs);
5638c2ecf20Sopenharmony_ci}
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_cistatic int pcd_reset(struct pcd_unit *cd)
5668c2ecf20Sopenharmony_ci{
5678c2ecf20Sopenharmony_ci	int i, k, flg;
5688c2ecf20Sopenharmony_ci	int expect[5] = { 1, 1, 1, 0x14, 0xeb };
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	pi_connect(cd->pi);
5718c2ecf20Sopenharmony_ci	write_reg(cd, 6, 0xa0 + 0x10 * cd->drive);
5728c2ecf20Sopenharmony_ci	write_reg(cd, 7, 8);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	pcd_sleep(20 * HZ / 1000);	/* delay a bit */
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	k = 0;
5778c2ecf20Sopenharmony_ci	while ((k++ < PCD_RESET_TMO) && (status_reg(cd) & IDE_BUSY))
5788c2ecf20Sopenharmony_ci		pcd_sleep(HZ / 10);
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	flg = 1;
5818c2ecf20Sopenharmony_ci	for (i = 0; i < 5; i++)
5828c2ecf20Sopenharmony_ci		flg &= (read_reg(cd, i + 1) == expect[i]);
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	if (verbose) {
5858c2ecf20Sopenharmony_ci		printk("%s: Reset (%d) signature = ", cd->name, k);
5868c2ecf20Sopenharmony_ci		for (i = 0; i < 5; i++)
5878c2ecf20Sopenharmony_ci			printk("%3x", read_reg(cd, i + 1));
5888c2ecf20Sopenharmony_ci		if (!flg)
5898c2ecf20Sopenharmony_ci			printk(" (incorrect)");
5908c2ecf20Sopenharmony_ci		printk("\n");
5918c2ecf20Sopenharmony_ci	}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	pi_disconnect(cd->pi);
5948c2ecf20Sopenharmony_ci	return flg - 1;
5958c2ecf20Sopenharmony_ci}
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_cistatic int pcd_drive_reset(struct cdrom_device_info *cdi)
5988c2ecf20Sopenharmony_ci{
5998c2ecf20Sopenharmony_ci	return pcd_reset(cdi->handle);
6008c2ecf20Sopenharmony_ci}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_cistatic int pcd_ready_wait(struct pcd_unit *cd, int tmo)
6038c2ecf20Sopenharmony_ci{
6048c2ecf20Sopenharmony_ci	char tr_cmd[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
6058c2ecf20Sopenharmony_ci	int k, p;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	k = 0;
6088c2ecf20Sopenharmony_ci	while (k < tmo) {
6098c2ecf20Sopenharmony_ci		cd->last_sense = 0;
6108c2ecf20Sopenharmony_ci		pcd_atapi(cd, tr_cmd, 0, NULL, DBMSG("test unit ready"));
6118c2ecf20Sopenharmony_ci		p = cd->last_sense;
6128c2ecf20Sopenharmony_ci		if (!p)
6138c2ecf20Sopenharmony_ci			return 0;
6148c2ecf20Sopenharmony_ci		if (!(((p & 0xffff) == 0x0402) || ((p & 0xff) == 6)))
6158c2ecf20Sopenharmony_ci			return p;
6168c2ecf20Sopenharmony_ci		k++;
6178c2ecf20Sopenharmony_ci		pcd_sleep(HZ);
6188c2ecf20Sopenharmony_ci	}
6198c2ecf20Sopenharmony_ci	return 0x000020;	/* timeout */
6208c2ecf20Sopenharmony_ci}
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_cistatic int pcd_drive_status(struct cdrom_device_info *cdi, int slot_nr)
6238c2ecf20Sopenharmony_ci{
6248c2ecf20Sopenharmony_ci	char rc_cmd[12] = { 0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
6258c2ecf20Sopenharmony_ci	struct pcd_unit *cd = cdi->handle;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	if (pcd_ready_wait(cd, PCD_READY_TMO))
6288c2ecf20Sopenharmony_ci		return CDS_DRIVE_NOT_READY;
6298c2ecf20Sopenharmony_ci	if (pcd_atapi(cd, rc_cmd, 8, pcd_scratch, DBMSG("check media")))
6308c2ecf20Sopenharmony_ci		return CDS_NO_DISC;
6318c2ecf20Sopenharmony_ci	return CDS_DISC_OK;
6328c2ecf20Sopenharmony_ci}
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_cistatic int pcd_identify(struct pcd_unit *cd, char *id)
6358c2ecf20Sopenharmony_ci{
6368c2ecf20Sopenharmony_ci	int k, s;
6378c2ecf20Sopenharmony_ci	char id_cmd[12] = { 0x12, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0 };
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	pcd_bufblk = -1;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	s = pcd_atapi(cd, id_cmd, 36, pcd_buffer, "identify");
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	if (s)
6448c2ecf20Sopenharmony_ci		return -1;
6458c2ecf20Sopenharmony_ci	if ((pcd_buffer[0] & 0x1f) != 5) {
6468c2ecf20Sopenharmony_ci		if (verbose)
6478c2ecf20Sopenharmony_ci			printk("%s: %s is not a CD-ROM\n",
6488c2ecf20Sopenharmony_ci			       cd->name, cd->drive ? "Slave" : "Master");
6498c2ecf20Sopenharmony_ci		return -1;
6508c2ecf20Sopenharmony_ci	}
6518c2ecf20Sopenharmony_ci	memcpy(id, pcd_buffer + 16, 16);
6528c2ecf20Sopenharmony_ci	id[16] = 0;
6538c2ecf20Sopenharmony_ci	k = 16;
6548c2ecf20Sopenharmony_ci	while ((k >= 0) && (id[k] <= 0x20)) {
6558c2ecf20Sopenharmony_ci		id[k] = 0;
6568c2ecf20Sopenharmony_ci		k--;
6578c2ecf20Sopenharmony_ci	}
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	printk("%s: %s: %s\n", cd->name, cd->drive ? "Slave" : "Master", id);
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	return 0;
6628c2ecf20Sopenharmony_ci}
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci/*
6658c2ecf20Sopenharmony_ci * returns  0, with id set if drive is detected
6668c2ecf20Sopenharmony_ci *	    -1, if drive detection failed
6678c2ecf20Sopenharmony_ci */
6688c2ecf20Sopenharmony_cistatic int pcd_probe(struct pcd_unit *cd, int ms, char *id)
6698c2ecf20Sopenharmony_ci{
6708c2ecf20Sopenharmony_ci	if (ms == -1) {
6718c2ecf20Sopenharmony_ci		for (cd->drive = 0; cd->drive <= 1; cd->drive++)
6728c2ecf20Sopenharmony_ci			if (!pcd_reset(cd) && !pcd_identify(cd, id))
6738c2ecf20Sopenharmony_ci				return 0;
6748c2ecf20Sopenharmony_ci	} else {
6758c2ecf20Sopenharmony_ci		cd->drive = ms;
6768c2ecf20Sopenharmony_ci		if (!pcd_reset(cd) && !pcd_identify(cd, id))
6778c2ecf20Sopenharmony_ci			return 0;
6788c2ecf20Sopenharmony_ci	}
6798c2ecf20Sopenharmony_ci	return -1;
6808c2ecf20Sopenharmony_ci}
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_cistatic void pcd_probe_capabilities(void)
6838c2ecf20Sopenharmony_ci{
6848c2ecf20Sopenharmony_ci	int unit, r;
6858c2ecf20Sopenharmony_ci	char buffer[32];
6868c2ecf20Sopenharmony_ci	char cmd[12] = { 0x5a, 1 << 3, 0x2a, 0, 0, 0, 0, 18, 0, 0, 0, 0 };
6878c2ecf20Sopenharmony_ci	struct pcd_unit *cd;
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++) {
6908c2ecf20Sopenharmony_ci		if (!cd->present)
6918c2ecf20Sopenharmony_ci			continue;
6928c2ecf20Sopenharmony_ci		r = pcd_atapi(cd, cmd, 18, buffer, "mode sense capabilities");
6938c2ecf20Sopenharmony_ci		if (r)
6948c2ecf20Sopenharmony_ci			continue;
6958c2ecf20Sopenharmony_ci		/* we should now have the cap page */
6968c2ecf20Sopenharmony_ci		if ((buffer[11] & 1) == 0)
6978c2ecf20Sopenharmony_ci			cd->info.mask |= CDC_CD_R;
6988c2ecf20Sopenharmony_ci		if ((buffer[11] & 2) == 0)
6998c2ecf20Sopenharmony_ci			cd->info.mask |= CDC_CD_RW;
7008c2ecf20Sopenharmony_ci		if ((buffer[12] & 1) == 0)
7018c2ecf20Sopenharmony_ci			cd->info.mask |= CDC_PLAY_AUDIO;
7028c2ecf20Sopenharmony_ci		if ((buffer[14] & 1) == 0)
7038c2ecf20Sopenharmony_ci			cd->info.mask |= CDC_LOCK;
7048c2ecf20Sopenharmony_ci		if ((buffer[14] & 8) == 0)
7058c2ecf20Sopenharmony_ci			cd->info.mask |= CDC_OPEN_TRAY;
7068c2ecf20Sopenharmony_ci		if ((buffer[14] >> 6) == 0)
7078c2ecf20Sopenharmony_ci			cd->info.mask |= CDC_CLOSE_TRAY;
7088c2ecf20Sopenharmony_ci	}
7098c2ecf20Sopenharmony_ci}
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_cistatic int pcd_detect(void)
7128c2ecf20Sopenharmony_ci{
7138c2ecf20Sopenharmony_ci	char id[18];
7148c2ecf20Sopenharmony_ci	int k, unit;
7158c2ecf20Sopenharmony_ci	struct pcd_unit *cd;
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	printk("%s: %s version %s, major %d, nice %d\n",
7188c2ecf20Sopenharmony_ci	       name, name, PCD_VERSION, major, nice);
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	par_drv = pi_register_driver(name);
7218c2ecf20Sopenharmony_ci	if (!par_drv) {
7228c2ecf20Sopenharmony_ci		pr_err("failed to register %s driver\n", name);
7238c2ecf20Sopenharmony_ci		return -1;
7248c2ecf20Sopenharmony_ci	}
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	k = 0;
7278c2ecf20Sopenharmony_ci	if (pcd_drive_count == 0) { /* nothing spec'd - so autoprobe for 1 */
7288c2ecf20Sopenharmony_ci		cd = pcd;
7298c2ecf20Sopenharmony_ci		if (cd->disk && pi_init(cd->pi, 1, -1, -1, -1, -1, -1,
7308c2ecf20Sopenharmony_ci			    pcd_buffer, PI_PCD, verbose, cd->name)) {
7318c2ecf20Sopenharmony_ci			if (!pcd_probe(cd, -1, id)) {
7328c2ecf20Sopenharmony_ci				cd->present = 1;
7338c2ecf20Sopenharmony_ci				k++;
7348c2ecf20Sopenharmony_ci			} else
7358c2ecf20Sopenharmony_ci				pi_release(cd->pi);
7368c2ecf20Sopenharmony_ci		}
7378c2ecf20Sopenharmony_ci	} else {
7388c2ecf20Sopenharmony_ci		for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++) {
7398c2ecf20Sopenharmony_ci			int *conf = *drives[unit];
7408c2ecf20Sopenharmony_ci			if (!conf[D_PRT])
7418c2ecf20Sopenharmony_ci				continue;
7428c2ecf20Sopenharmony_ci			if (!cd->disk)
7438c2ecf20Sopenharmony_ci				continue;
7448c2ecf20Sopenharmony_ci			if (!pi_init(cd->pi, 0, conf[D_PRT], conf[D_MOD],
7458c2ecf20Sopenharmony_ci				     conf[D_UNI], conf[D_PRO], conf[D_DLY],
7468c2ecf20Sopenharmony_ci				     pcd_buffer, PI_PCD, verbose, cd->name))
7478c2ecf20Sopenharmony_ci				continue;
7488c2ecf20Sopenharmony_ci			if (!pcd_probe(cd, conf[D_SLV], id)) {
7498c2ecf20Sopenharmony_ci				cd->present = 1;
7508c2ecf20Sopenharmony_ci				k++;
7518c2ecf20Sopenharmony_ci			} else
7528c2ecf20Sopenharmony_ci				pi_release(cd->pi);
7538c2ecf20Sopenharmony_ci		}
7548c2ecf20Sopenharmony_ci	}
7558c2ecf20Sopenharmony_ci	if (k)
7568c2ecf20Sopenharmony_ci		return 0;
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	printk("%s: No CD-ROM drive found\n", name);
7598c2ecf20Sopenharmony_ci	for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++) {
7608c2ecf20Sopenharmony_ci		if (!cd->disk)
7618c2ecf20Sopenharmony_ci			continue;
7628c2ecf20Sopenharmony_ci		blk_cleanup_queue(cd->disk->queue);
7638c2ecf20Sopenharmony_ci		cd->disk->queue = NULL;
7648c2ecf20Sopenharmony_ci		blk_mq_free_tag_set(&cd->tag_set);
7658c2ecf20Sopenharmony_ci		put_disk(cd->disk);
7668c2ecf20Sopenharmony_ci	}
7678c2ecf20Sopenharmony_ci	pi_unregister_driver(par_drv);
7688c2ecf20Sopenharmony_ci	return -1;
7698c2ecf20Sopenharmony_ci}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci/* I/O request processing */
7728c2ecf20Sopenharmony_cistatic int pcd_queue;
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_cistatic int set_next_request(void)
7758c2ecf20Sopenharmony_ci{
7768c2ecf20Sopenharmony_ci	struct pcd_unit *cd;
7778c2ecf20Sopenharmony_ci	int old_pos = pcd_queue;
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	do {
7808c2ecf20Sopenharmony_ci		cd = &pcd[pcd_queue];
7818c2ecf20Sopenharmony_ci		if (++pcd_queue == PCD_UNITS)
7828c2ecf20Sopenharmony_ci			pcd_queue = 0;
7838c2ecf20Sopenharmony_ci		if (cd->present && !list_empty(&cd->rq_list)) {
7848c2ecf20Sopenharmony_ci			pcd_req = list_first_entry(&cd->rq_list, struct request,
7858c2ecf20Sopenharmony_ci							queuelist);
7868c2ecf20Sopenharmony_ci			list_del_init(&pcd_req->queuelist);
7878c2ecf20Sopenharmony_ci			blk_mq_start_request(pcd_req);
7888c2ecf20Sopenharmony_ci			break;
7898c2ecf20Sopenharmony_ci		}
7908c2ecf20Sopenharmony_ci	} while (pcd_queue != old_pos);
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	return pcd_req != NULL;
7938c2ecf20Sopenharmony_ci}
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_cistatic void pcd_request(void)
7968c2ecf20Sopenharmony_ci{
7978c2ecf20Sopenharmony_ci	struct pcd_unit *cd;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	if (pcd_busy)
8008c2ecf20Sopenharmony_ci		return;
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	if (!pcd_req && !set_next_request())
8038c2ecf20Sopenharmony_ci		return;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	cd = pcd_req->rq_disk->private_data;
8068c2ecf20Sopenharmony_ci	if (cd != pcd_current)
8078c2ecf20Sopenharmony_ci		pcd_bufblk = -1;
8088c2ecf20Sopenharmony_ci	pcd_current = cd;
8098c2ecf20Sopenharmony_ci	pcd_sector = blk_rq_pos(pcd_req);
8108c2ecf20Sopenharmony_ci	pcd_count = blk_rq_cur_sectors(pcd_req);
8118c2ecf20Sopenharmony_ci	pcd_buf = bio_data(pcd_req->bio);
8128c2ecf20Sopenharmony_ci	pcd_busy = 1;
8138c2ecf20Sopenharmony_ci	ps_set_intr(do_pcd_read, NULL, 0, nice);
8148c2ecf20Sopenharmony_ci}
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_cistatic blk_status_t pcd_queue_rq(struct blk_mq_hw_ctx *hctx,
8178c2ecf20Sopenharmony_ci				 const struct blk_mq_queue_data *bd)
8188c2ecf20Sopenharmony_ci{
8198c2ecf20Sopenharmony_ci	struct pcd_unit *cd = hctx->queue->queuedata;
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	if (rq_data_dir(bd->rq) != READ) {
8228c2ecf20Sopenharmony_ci		blk_mq_start_request(bd->rq);
8238c2ecf20Sopenharmony_ci		return BLK_STS_IOERR;
8248c2ecf20Sopenharmony_ci	}
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	spin_lock_irq(&pcd_lock);
8278c2ecf20Sopenharmony_ci	list_add_tail(&bd->rq->queuelist, &cd->rq_list);
8288c2ecf20Sopenharmony_ci	pcd_request();
8298c2ecf20Sopenharmony_ci	spin_unlock_irq(&pcd_lock);
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	return BLK_STS_OK;
8328c2ecf20Sopenharmony_ci}
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_cistatic inline void next_request(blk_status_t err)
8358c2ecf20Sopenharmony_ci{
8368c2ecf20Sopenharmony_ci	unsigned long saved_flags;
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pcd_lock, saved_flags);
8398c2ecf20Sopenharmony_ci	if (!blk_update_request(pcd_req, err, blk_rq_cur_bytes(pcd_req))) {
8408c2ecf20Sopenharmony_ci		__blk_mq_end_request(pcd_req, err);
8418c2ecf20Sopenharmony_ci		pcd_req = NULL;
8428c2ecf20Sopenharmony_ci	}
8438c2ecf20Sopenharmony_ci	pcd_busy = 0;
8448c2ecf20Sopenharmony_ci	pcd_request();
8458c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pcd_lock, saved_flags);
8468c2ecf20Sopenharmony_ci}
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_cistatic int pcd_ready(void)
8498c2ecf20Sopenharmony_ci{
8508c2ecf20Sopenharmony_ci	return (((status_reg(pcd_current) & (IDE_BUSY | IDE_DRQ)) == IDE_DRQ));
8518c2ecf20Sopenharmony_ci}
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_cistatic void pcd_transfer(void)
8548c2ecf20Sopenharmony_ci{
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	while (pcd_count && (pcd_sector / 4 == pcd_bufblk)) {
8578c2ecf20Sopenharmony_ci		int o = (pcd_sector % 4) * 512;
8588c2ecf20Sopenharmony_ci		memcpy(pcd_buf, pcd_buffer + o, 512);
8598c2ecf20Sopenharmony_ci		pcd_count--;
8608c2ecf20Sopenharmony_ci		pcd_buf += 512;
8618c2ecf20Sopenharmony_ci		pcd_sector++;
8628c2ecf20Sopenharmony_ci	}
8638c2ecf20Sopenharmony_ci}
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_cistatic void pcd_start(void)
8668c2ecf20Sopenharmony_ci{
8678c2ecf20Sopenharmony_ci	int b, i;
8688c2ecf20Sopenharmony_ci	char rd_cmd[12] = { 0xa8, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 };
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	pcd_bufblk = pcd_sector / 4;
8718c2ecf20Sopenharmony_ci	b = pcd_bufblk;
8728c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++) {
8738c2ecf20Sopenharmony_ci		rd_cmd[5 - i] = b & 0xff;
8748c2ecf20Sopenharmony_ci		b = b >> 8;
8758c2ecf20Sopenharmony_ci	}
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	if (pcd_command(pcd_current, rd_cmd, 2048, "read block")) {
8788c2ecf20Sopenharmony_ci		pcd_bufblk = -1;
8798c2ecf20Sopenharmony_ci		next_request(BLK_STS_IOERR);
8808c2ecf20Sopenharmony_ci		return;
8818c2ecf20Sopenharmony_ci	}
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	mdelay(1);
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	ps_set_intr(do_pcd_read_drq, pcd_ready, PCD_TMO, nice);
8868c2ecf20Sopenharmony_ci}
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_cistatic void do_pcd_read(void)
8898c2ecf20Sopenharmony_ci{
8908c2ecf20Sopenharmony_ci	pcd_busy = 1;
8918c2ecf20Sopenharmony_ci	pcd_retries = 0;
8928c2ecf20Sopenharmony_ci	pcd_transfer();
8938c2ecf20Sopenharmony_ci	if (!pcd_count) {
8948c2ecf20Sopenharmony_ci		next_request(0);
8958c2ecf20Sopenharmony_ci		return;
8968c2ecf20Sopenharmony_ci	}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	pi_do_claimed(pcd_current->pi, pcd_start);
8998c2ecf20Sopenharmony_ci}
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_cistatic void do_pcd_read_drq(void)
9028c2ecf20Sopenharmony_ci{
9038c2ecf20Sopenharmony_ci	unsigned long saved_flags;
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	if (pcd_completion(pcd_current, pcd_buffer, "read block")) {
9068c2ecf20Sopenharmony_ci		if (pcd_retries < PCD_RETRIES) {
9078c2ecf20Sopenharmony_ci			mdelay(1);
9088c2ecf20Sopenharmony_ci			pcd_retries++;
9098c2ecf20Sopenharmony_ci			pi_do_claimed(pcd_current->pi, pcd_start);
9108c2ecf20Sopenharmony_ci			return;
9118c2ecf20Sopenharmony_ci		}
9128c2ecf20Sopenharmony_ci		pcd_bufblk = -1;
9138c2ecf20Sopenharmony_ci		next_request(BLK_STS_IOERR);
9148c2ecf20Sopenharmony_ci		return;
9158c2ecf20Sopenharmony_ci	}
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	do_pcd_read();
9188c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pcd_lock, saved_flags);
9198c2ecf20Sopenharmony_ci	pcd_request();
9208c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pcd_lock, saved_flags);
9218c2ecf20Sopenharmony_ci}
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci/* the audio_ioctl stuff is adapted from sr_ioctl.c */
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_cistatic int pcd_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void *arg)
9268c2ecf20Sopenharmony_ci{
9278c2ecf20Sopenharmony_ci	struct pcd_unit *cd = cdi->handle;
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	switch (cmd) {
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	case CDROMREADTOCHDR:
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci		{
9348c2ecf20Sopenharmony_ci			char cmd[12] =
9358c2ecf20Sopenharmony_ci			    { GPCMD_READ_TOC_PMA_ATIP, 0, 0, 0, 0, 0, 0, 0, 12,
9368c2ecf20Sopenharmony_ci			 0, 0, 0 };
9378c2ecf20Sopenharmony_ci			struct cdrom_tochdr *tochdr =
9388c2ecf20Sopenharmony_ci			    (struct cdrom_tochdr *) arg;
9398c2ecf20Sopenharmony_ci			char buffer[32];
9408c2ecf20Sopenharmony_ci			int r;
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci			r = pcd_atapi(cd, cmd, 12, buffer, "read toc header");
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci			tochdr->cdth_trk0 = buffer[2];
9458c2ecf20Sopenharmony_ci			tochdr->cdth_trk1 = buffer[3];
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci			return r ? -EIO : 0;
9488c2ecf20Sopenharmony_ci		}
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	case CDROMREADTOCENTRY:
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci		{
9538c2ecf20Sopenharmony_ci			char cmd[12] =
9548c2ecf20Sopenharmony_ci			    { GPCMD_READ_TOC_PMA_ATIP, 0, 0, 0, 0, 0, 0, 0, 12,
9558c2ecf20Sopenharmony_ci			 0, 0, 0 };
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci			struct cdrom_tocentry *tocentry =
9588c2ecf20Sopenharmony_ci			    (struct cdrom_tocentry *) arg;
9598c2ecf20Sopenharmony_ci			unsigned char buffer[32];
9608c2ecf20Sopenharmony_ci			int r;
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci			cmd[1] =
9638c2ecf20Sopenharmony_ci			    (tocentry->cdte_format == CDROM_MSF ? 0x02 : 0);
9648c2ecf20Sopenharmony_ci			cmd[6] = tocentry->cdte_track;
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci			r = pcd_atapi(cd, cmd, 12, buffer, "read toc entry");
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci			tocentry->cdte_ctrl = buffer[5] & 0xf;
9698c2ecf20Sopenharmony_ci			tocentry->cdte_adr = buffer[5] >> 4;
9708c2ecf20Sopenharmony_ci			tocentry->cdte_datamode =
9718c2ecf20Sopenharmony_ci			    (tocentry->cdte_ctrl & 0x04) ? 1 : 0;
9728c2ecf20Sopenharmony_ci			if (tocentry->cdte_format == CDROM_MSF) {
9738c2ecf20Sopenharmony_ci				tocentry->cdte_addr.msf.minute = buffer[9];
9748c2ecf20Sopenharmony_ci				tocentry->cdte_addr.msf.second = buffer[10];
9758c2ecf20Sopenharmony_ci				tocentry->cdte_addr.msf.frame = buffer[11];
9768c2ecf20Sopenharmony_ci			} else
9778c2ecf20Sopenharmony_ci				tocentry->cdte_addr.lba =
9788c2ecf20Sopenharmony_ci				    (((((buffer[8] << 8) + buffer[9]) << 8)
9798c2ecf20Sopenharmony_ci				      + buffer[10]) << 8) + buffer[11];
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci			return r ? -EIO : 0;
9828c2ecf20Sopenharmony_ci		}
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci	default:
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci		return -ENOSYS;
9878c2ecf20Sopenharmony_ci	}
9888c2ecf20Sopenharmony_ci}
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_cistatic int pcd_get_mcn(struct cdrom_device_info *cdi, struct cdrom_mcn *mcn)
9918c2ecf20Sopenharmony_ci{
9928c2ecf20Sopenharmony_ci	char cmd[12] =
9938c2ecf20Sopenharmony_ci	    { GPCMD_READ_SUBCHANNEL, 0, 0x40, 2, 0, 0, 0, 0, 24, 0, 0, 0 };
9948c2ecf20Sopenharmony_ci	char buffer[32];
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	if (pcd_atapi(cdi->handle, cmd, 24, buffer, "get mcn"))
9978c2ecf20Sopenharmony_ci		return -EIO;
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	memcpy(mcn->medium_catalog_number, buffer + 9, 13);
10008c2ecf20Sopenharmony_ci	mcn->medium_catalog_number[13] = 0;
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	return 0;
10038c2ecf20Sopenharmony_ci}
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_cistatic int __init pcd_init(void)
10068c2ecf20Sopenharmony_ci{
10078c2ecf20Sopenharmony_ci	struct pcd_unit *cd;
10088c2ecf20Sopenharmony_ci	int unit;
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	if (disable)
10118c2ecf20Sopenharmony_ci		return -EINVAL;
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	pcd_init_units();
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci	if (pcd_detect())
10168c2ecf20Sopenharmony_ci		return -ENODEV;
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	/* get the atapi capabilities page */
10198c2ecf20Sopenharmony_ci	pcd_probe_capabilities();
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	if (register_blkdev(major, name)) {
10228c2ecf20Sopenharmony_ci		for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++) {
10238c2ecf20Sopenharmony_ci			if (!cd->disk)
10248c2ecf20Sopenharmony_ci				continue;
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci			blk_cleanup_queue(cd->disk->queue);
10278c2ecf20Sopenharmony_ci			blk_mq_free_tag_set(&cd->tag_set);
10288c2ecf20Sopenharmony_ci			put_disk(cd->disk);
10298c2ecf20Sopenharmony_ci		}
10308c2ecf20Sopenharmony_ci		return -EBUSY;
10318c2ecf20Sopenharmony_ci	}
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++) {
10348c2ecf20Sopenharmony_ci		if (cd->present) {
10358c2ecf20Sopenharmony_ci			register_cdrom(cd->disk, &cd->info);
10368c2ecf20Sopenharmony_ci			cd->disk->private_data = cd;
10378c2ecf20Sopenharmony_ci			add_disk(cd->disk);
10388c2ecf20Sopenharmony_ci		}
10398c2ecf20Sopenharmony_ci	}
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	return 0;
10428c2ecf20Sopenharmony_ci}
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_cistatic void __exit pcd_exit(void)
10458c2ecf20Sopenharmony_ci{
10468c2ecf20Sopenharmony_ci	struct pcd_unit *cd;
10478c2ecf20Sopenharmony_ci	int unit;
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++) {
10508c2ecf20Sopenharmony_ci		if (!cd->disk)
10518c2ecf20Sopenharmony_ci			continue;
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci		if (cd->present) {
10548c2ecf20Sopenharmony_ci			del_gendisk(cd->disk);
10558c2ecf20Sopenharmony_ci			pi_release(cd->pi);
10568c2ecf20Sopenharmony_ci			unregister_cdrom(&cd->info);
10578c2ecf20Sopenharmony_ci		}
10588c2ecf20Sopenharmony_ci		blk_cleanup_queue(cd->disk->queue);
10598c2ecf20Sopenharmony_ci		blk_mq_free_tag_set(&cd->tag_set);
10608c2ecf20Sopenharmony_ci		put_disk(cd->disk);
10618c2ecf20Sopenharmony_ci	}
10628c2ecf20Sopenharmony_ci	unregister_blkdev(major, name);
10638c2ecf20Sopenharmony_ci	pi_unregister_driver(par_drv);
10648c2ecf20Sopenharmony_ci}
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
10678c2ecf20Sopenharmony_cimodule_init(pcd_init)
10688c2ecf20Sopenharmony_cimodule_exit(pcd_exit)
1069