18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci        pf.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 the high-level driver for parallel port ATAPI disk
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 disk 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 pf 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-7 integers as follows:
188c2ecf20Sopenharmony_ci            drive2
198c2ecf20Sopenharmony_ci            drive3      <prt>,<pro>,<uni>,<mod>,<slv>,<lun>,<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 CDroms 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		<lun>   Some ATAPI devices support multiple LUNs.
478c2ecf20Sopenharmony_ci                        One example is the ATAPI PD/CD drive from
488c2ecf20Sopenharmony_ci                        Matshita/Panasonic.  This device has a
498c2ecf20Sopenharmony_ci                        CD drive on LUN 0 and a PD drive on LUN 1.
508c2ecf20Sopenharmony_ci                        By default, the driver will search for the
518c2ecf20Sopenharmony_ci                        first LUN with a supported device.  Set
528c2ecf20Sopenharmony_ci                        this parameter to force it to use a specific
538c2ecf20Sopenharmony_ci                        LUN.  (default -1)
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci                <dly>   some parallel ports require the driver to
568c2ecf20Sopenharmony_ci                        go more slowly.  -1 sets a default value that
578c2ecf20Sopenharmony_ci                        should work with the chosen protocol.  Otherwise,
588c2ecf20Sopenharmony_ci                        set this to a small integer, the larger it is
598c2ecf20Sopenharmony_ci                        the slower the port i/o.  In some cases, setting
608c2ecf20Sopenharmony_ci                        this to zero will speed up the device. (default -1)
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	    major	You may use this parameter to override the
638c2ecf20Sopenharmony_ci			default major number (47) that this driver
648c2ecf20Sopenharmony_ci			will use.  Be sure to change the device
658c2ecf20Sopenharmony_ci			name as well.
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	    name	This parameter is a character string that
688c2ecf20Sopenharmony_ci			contains the name the kernel will use for this
698c2ecf20Sopenharmony_ci			device (in /proc output, for instance).
708c2ecf20Sopenharmony_ci			(default "pf").
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci            cluster     The driver will attempt to aggregate requests
738c2ecf20Sopenharmony_ci                        for adjacent blocks into larger multi-block
748c2ecf20Sopenharmony_ci                        clusters.  The maximum cluster size (in 512
758c2ecf20Sopenharmony_ci                        byte sectors) is set with this parameter.
768c2ecf20Sopenharmony_ci                        (default 64)
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci            verbose     This parameter controls the amount of logging
798c2ecf20Sopenharmony_ci                        that the driver will do.  Set it to 0 for
808c2ecf20Sopenharmony_ci                        normal operation, 1 to see autoprobe progress
818c2ecf20Sopenharmony_ci                        messages, or 2 to see additional debugging
828c2ecf20Sopenharmony_ci                        output.  (default 0)
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	    nice        This parameter controls the driver's use of
858c2ecf20Sopenharmony_ci			idle CPU time, at the expense of some speed.
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci        If this driver is built into the kernel, you can use the
888c2ecf20Sopenharmony_ci        following command line parameters, with the same values
898c2ecf20Sopenharmony_ci        as the corresponding module parameters listed above:
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci            pf.drive0
928c2ecf20Sopenharmony_ci            pf.drive1
938c2ecf20Sopenharmony_ci            pf.drive2
948c2ecf20Sopenharmony_ci            pf.drive3
958c2ecf20Sopenharmony_ci	    pf.cluster
968c2ecf20Sopenharmony_ci            pf.nice
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci        In addition, you can use the parameter pf.disable to disable
998c2ecf20Sopenharmony_ci        the driver entirely.
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci*/
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci/* Changes:
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	1.01	GRG 1998.05.03  Changes for SMP.  Eliminate sti().
1068c2ecf20Sopenharmony_ci				Fix for drives that don't clear STAT_ERR
1078c2ecf20Sopenharmony_ci			        until after next CDB delivered.
1088c2ecf20Sopenharmony_ci				Small change in pf_completion to round
1098c2ecf20Sopenharmony_ci				up transfer size.
1108c2ecf20Sopenharmony_ci	1.02    GRG 1998.06.16  Eliminated an Ugh
1118c2ecf20Sopenharmony_ci	1.03    GRG 1998.08.16  Use HZ in loop timings, extra debugging
1128c2ecf20Sopenharmony_ci	1.04    GRG 1998.09.24  Added jumbo support
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci*/
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci#define PF_VERSION      "1.04"
1178c2ecf20Sopenharmony_ci#define PF_MAJOR	47
1188c2ecf20Sopenharmony_ci#define PF_NAME		"pf"
1198c2ecf20Sopenharmony_ci#define PF_UNITS	4
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci#include <linux/types.h>
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci/* Here are things one can override from the insmod command.
1248c2ecf20Sopenharmony_ci   Most are autoprobed by paride unless set here.  Verbose is off
1258c2ecf20Sopenharmony_ci   by default.
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci*/
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic bool verbose = 0;
1308c2ecf20Sopenharmony_cistatic int major = PF_MAJOR;
1318c2ecf20Sopenharmony_cistatic char *name = PF_NAME;
1328c2ecf20Sopenharmony_cistatic int cluster = 64;
1338c2ecf20Sopenharmony_cistatic int nice = 0;
1348c2ecf20Sopenharmony_cistatic int disable = 0;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic int drive0[7] = { 0, 0, 0, -1, -1, -1, -1 };
1378c2ecf20Sopenharmony_cistatic int drive1[7] = { 0, 0, 0, -1, -1, -1, -1 };
1388c2ecf20Sopenharmony_cistatic int drive2[7] = { 0, 0, 0, -1, -1, -1, -1 };
1398c2ecf20Sopenharmony_cistatic int drive3[7] = { 0, 0, 0, -1, -1, -1, -1 };
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic int (*drives[4])[7] = {&drive0, &drive1, &drive2, &drive3};
1428c2ecf20Sopenharmony_cistatic int pf_drive_count;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cienum {D_PRT, D_PRO, D_UNI, D_MOD, D_SLV, D_LUN, D_DLY};
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci/* end of parameters */
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci#include <linux/module.h>
1498c2ecf20Sopenharmony_ci#include <linux/init.h>
1508c2ecf20Sopenharmony_ci#include <linux/fs.h>
1518c2ecf20Sopenharmony_ci#include <linux/delay.h>
1528c2ecf20Sopenharmony_ci#include <linux/hdreg.h>
1538c2ecf20Sopenharmony_ci#include <linux/cdrom.h>
1548c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
1558c2ecf20Sopenharmony_ci#include <linux/blk-mq.h>
1568c2ecf20Sopenharmony_ci#include <linux/blkpg.h>
1578c2ecf20Sopenharmony_ci#include <linux/mutex.h>
1588c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(pf_mutex);
1618c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(pf_spin_lock);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cimodule_param(verbose, bool, 0644);
1648c2ecf20Sopenharmony_cimodule_param(major, int, 0);
1658c2ecf20Sopenharmony_cimodule_param(name, charp, 0);
1668c2ecf20Sopenharmony_cimodule_param(cluster, int, 0);
1678c2ecf20Sopenharmony_cimodule_param(nice, int, 0);
1688c2ecf20Sopenharmony_cimodule_param_array(drive0, int, NULL, 0);
1698c2ecf20Sopenharmony_cimodule_param_array(drive1, int, NULL, 0);
1708c2ecf20Sopenharmony_cimodule_param_array(drive2, int, NULL, 0);
1718c2ecf20Sopenharmony_cimodule_param_array(drive3, int, NULL, 0);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci#include "paride.h"
1748c2ecf20Sopenharmony_ci#include "pseudo.h"
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci/* constants for faking geometry numbers */
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci#define PF_FD_MAX	8192	/* use FD geometry under this size */
1798c2ecf20Sopenharmony_ci#define PF_FD_HDS	2
1808c2ecf20Sopenharmony_ci#define PF_FD_SPT	18
1818c2ecf20Sopenharmony_ci#define PF_HD_HDS	64
1828c2ecf20Sopenharmony_ci#define PF_HD_SPT	32
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci#define PF_MAX_RETRIES  5
1858c2ecf20Sopenharmony_ci#define PF_TMO          800	/* interrupt timeout in jiffies */
1868c2ecf20Sopenharmony_ci#define PF_SPIN_DEL     50	/* spin delay in micro-seconds  */
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci#define PF_SPIN         (1000000*PF_TMO)/(HZ*PF_SPIN_DEL)
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci#define STAT_ERR        0x00001
1918c2ecf20Sopenharmony_ci#define STAT_INDEX      0x00002
1928c2ecf20Sopenharmony_ci#define STAT_ECC        0x00004
1938c2ecf20Sopenharmony_ci#define STAT_DRQ        0x00008
1948c2ecf20Sopenharmony_ci#define STAT_SEEK       0x00010
1958c2ecf20Sopenharmony_ci#define STAT_WRERR      0x00020
1968c2ecf20Sopenharmony_ci#define STAT_READY      0x00040
1978c2ecf20Sopenharmony_ci#define STAT_BUSY       0x00080
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci#define ATAPI_REQ_SENSE		0x03
2008c2ecf20Sopenharmony_ci#define ATAPI_LOCK		0x1e
2018c2ecf20Sopenharmony_ci#define ATAPI_DOOR		0x1b
2028c2ecf20Sopenharmony_ci#define ATAPI_MODE_SENSE	0x5a
2038c2ecf20Sopenharmony_ci#define ATAPI_CAPACITY		0x25
2048c2ecf20Sopenharmony_ci#define ATAPI_IDENTIFY		0x12
2058c2ecf20Sopenharmony_ci#define ATAPI_READ_10		0x28
2068c2ecf20Sopenharmony_ci#define ATAPI_WRITE_10		0x2a
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic int pf_open(struct block_device *bdev, fmode_t mode);
2098c2ecf20Sopenharmony_cistatic blk_status_t pf_queue_rq(struct blk_mq_hw_ctx *hctx,
2108c2ecf20Sopenharmony_ci				const struct blk_mq_queue_data *bd);
2118c2ecf20Sopenharmony_cistatic int pf_ioctl(struct block_device *bdev, fmode_t mode,
2128c2ecf20Sopenharmony_ci		    unsigned int cmd, unsigned long arg);
2138c2ecf20Sopenharmony_cistatic int pf_getgeo(struct block_device *bdev, struct hd_geometry *geo);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic void pf_release(struct gendisk *disk, fmode_t mode);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic int pf_detect(void);
2188c2ecf20Sopenharmony_cistatic void do_pf_read(void);
2198c2ecf20Sopenharmony_cistatic void do_pf_read_start(void);
2208c2ecf20Sopenharmony_cistatic void do_pf_write(void);
2218c2ecf20Sopenharmony_cistatic void do_pf_write_start(void);
2228c2ecf20Sopenharmony_cistatic void do_pf_read_drq(void);
2238c2ecf20Sopenharmony_cistatic void do_pf_write_done(void);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci#define PF_NM           0
2268c2ecf20Sopenharmony_ci#define PF_RO           1
2278c2ecf20Sopenharmony_ci#define PF_RW           2
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci#define PF_NAMELEN      8
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistruct pf_unit {
2328c2ecf20Sopenharmony_ci	struct pi_adapter pia;	/* interface to paride layer */
2338c2ecf20Sopenharmony_ci	struct pi_adapter *pi;
2348c2ecf20Sopenharmony_ci	int removable;		/* removable media device  ?  */
2358c2ecf20Sopenharmony_ci	int media_status;	/* media present ?  WP ? */
2368c2ecf20Sopenharmony_ci	int drive;		/* drive */
2378c2ecf20Sopenharmony_ci	int lun;
2388c2ecf20Sopenharmony_ci	int access;		/* count of active opens ... */
2398c2ecf20Sopenharmony_ci	int present;		/* device present ? */
2408c2ecf20Sopenharmony_ci	char name[PF_NAMELEN];	/* pf0, pf1, ... */
2418c2ecf20Sopenharmony_ci	struct gendisk *disk;
2428c2ecf20Sopenharmony_ci	struct blk_mq_tag_set tag_set;
2438c2ecf20Sopenharmony_ci	struct list_head rq_list;
2448c2ecf20Sopenharmony_ci};
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic struct pf_unit units[PF_UNITS];
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic int pf_identify(struct pf_unit *pf);
2498c2ecf20Sopenharmony_cistatic void pf_lock(struct pf_unit *pf, int func);
2508c2ecf20Sopenharmony_cistatic void pf_eject(struct pf_unit *pf);
2518c2ecf20Sopenharmony_cistatic unsigned int pf_check_events(struct gendisk *disk,
2528c2ecf20Sopenharmony_ci				    unsigned int clearing);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_cistatic char pf_scratch[512];	/* scratch block buffer */
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci/* the variables below are used mainly in the I/O request engine, which
2578c2ecf20Sopenharmony_ci   processes only one request at a time.
2588c2ecf20Sopenharmony_ci*/
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic int pf_retries = 0;	/* i/o error retry count */
2618c2ecf20Sopenharmony_cistatic int pf_busy = 0;		/* request being processed ? */
2628c2ecf20Sopenharmony_cistatic struct request *pf_req;	/* current request */
2638c2ecf20Sopenharmony_cistatic int pf_block;		/* address of next requested block */
2648c2ecf20Sopenharmony_cistatic int pf_count;		/* number of blocks still to do */
2658c2ecf20Sopenharmony_cistatic int pf_run;		/* sectors in current cluster */
2668c2ecf20Sopenharmony_cistatic int pf_cmd;		/* current command READ/WRITE */
2678c2ecf20Sopenharmony_cistatic struct pf_unit *pf_current;/* unit of current request */
2688c2ecf20Sopenharmony_cistatic int pf_mask;		/* stopper for pseudo-int */
2698c2ecf20Sopenharmony_cistatic char *pf_buf;		/* buffer for request in progress */
2708c2ecf20Sopenharmony_cistatic void *par_drv;		/* reference of parport driver */
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci/* kernel glue structures */
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic const struct block_device_operations pf_fops = {
2758c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
2768c2ecf20Sopenharmony_ci	.open		= pf_open,
2778c2ecf20Sopenharmony_ci	.release	= pf_release,
2788c2ecf20Sopenharmony_ci	.ioctl		= pf_ioctl,
2798c2ecf20Sopenharmony_ci	.compat_ioctl	= pf_ioctl,
2808c2ecf20Sopenharmony_ci	.getgeo		= pf_getgeo,
2818c2ecf20Sopenharmony_ci	.check_events	= pf_check_events,
2828c2ecf20Sopenharmony_ci};
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic const struct blk_mq_ops pf_mq_ops = {
2858c2ecf20Sopenharmony_ci	.queue_rq	= pf_queue_rq,
2868c2ecf20Sopenharmony_ci};
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cistatic void __init pf_init_units(void)
2898c2ecf20Sopenharmony_ci{
2908c2ecf20Sopenharmony_ci	struct pf_unit *pf;
2918c2ecf20Sopenharmony_ci	int unit;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	pf_drive_count = 0;
2948c2ecf20Sopenharmony_ci	for (unit = 0, pf = units; unit < PF_UNITS; unit++, pf++) {
2958c2ecf20Sopenharmony_ci		struct gendisk *disk;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci		disk = alloc_disk(1);
2988c2ecf20Sopenharmony_ci		if (!disk)
2998c2ecf20Sopenharmony_ci			continue;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci		disk->queue = blk_mq_init_sq_queue(&pf->tag_set, &pf_mq_ops,
3028c2ecf20Sopenharmony_ci							1, BLK_MQ_F_SHOULD_MERGE);
3038c2ecf20Sopenharmony_ci		if (IS_ERR(disk->queue)) {
3048c2ecf20Sopenharmony_ci			disk->queue = NULL;
3058c2ecf20Sopenharmony_ci			put_disk(disk);
3068c2ecf20Sopenharmony_ci			continue;
3078c2ecf20Sopenharmony_ci		}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&pf->rq_list);
3108c2ecf20Sopenharmony_ci		disk->queue->queuedata = pf;
3118c2ecf20Sopenharmony_ci		blk_queue_max_segments(disk->queue, cluster);
3128c2ecf20Sopenharmony_ci		blk_queue_bounce_limit(disk->queue, BLK_BOUNCE_HIGH);
3138c2ecf20Sopenharmony_ci		pf->disk = disk;
3148c2ecf20Sopenharmony_ci		pf->pi = &pf->pia;
3158c2ecf20Sopenharmony_ci		pf->media_status = PF_NM;
3168c2ecf20Sopenharmony_ci		pf->drive = (*drives[unit])[D_SLV];
3178c2ecf20Sopenharmony_ci		pf->lun = (*drives[unit])[D_LUN];
3188c2ecf20Sopenharmony_ci		snprintf(pf->name, PF_NAMELEN, "%s%d", name, unit);
3198c2ecf20Sopenharmony_ci		disk->major = major;
3208c2ecf20Sopenharmony_ci		disk->first_minor = unit;
3218c2ecf20Sopenharmony_ci		strcpy(disk->disk_name, pf->name);
3228c2ecf20Sopenharmony_ci		disk->fops = &pf_fops;
3238c2ecf20Sopenharmony_ci		disk->events = DISK_EVENT_MEDIA_CHANGE;
3248c2ecf20Sopenharmony_ci		if (!(*drives[unit])[D_PRT])
3258c2ecf20Sopenharmony_ci			pf_drive_count++;
3268c2ecf20Sopenharmony_ci	}
3278c2ecf20Sopenharmony_ci}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_cistatic int pf_open(struct block_device *bdev, fmode_t mode)
3308c2ecf20Sopenharmony_ci{
3318c2ecf20Sopenharmony_ci	struct pf_unit *pf = bdev->bd_disk->private_data;
3328c2ecf20Sopenharmony_ci	int ret;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	mutex_lock(&pf_mutex);
3358c2ecf20Sopenharmony_ci	pf_identify(pf);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	ret = -ENODEV;
3388c2ecf20Sopenharmony_ci	if (pf->media_status == PF_NM)
3398c2ecf20Sopenharmony_ci		goto out;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	ret = -EROFS;
3428c2ecf20Sopenharmony_ci	if ((pf->media_status == PF_RO) && (mode & FMODE_WRITE))
3438c2ecf20Sopenharmony_ci		goto out;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	ret = 0;
3468c2ecf20Sopenharmony_ci	pf->access++;
3478c2ecf20Sopenharmony_ci	if (pf->removable)
3488c2ecf20Sopenharmony_ci		pf_lock(pf, 1);
3498c2ecf20Sopenharmony_ciout:
3508c2ecf20Sopenharmony_ci	mutex_unlock(&pf_mutex);
3518c2ecf20Sopenharmony_ci	return ret;
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic int pf_getgeo(struct block_device *bdev, struct hd_geometry *geo)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	struct pf_unit *pf = bdev->bd_disk->private_data;
3578c2ecf20Sopenharmony_ci	sector_t capacity = get_capacity(pf->disk);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	if (capacity < PF_FD_MAX) {
3608c2ecf20Sopenharmony_ci		geo->cylinders = sector_div(capacity, PF_FD_HDS * PF_FD_SPT);
3618c2ecf20Sopenharmony_ci		geo->heads = PF_FD_HDS;
3628c2ecf20Sopenharmony_ci		geo->sectors = PF_FD_SPT;
3638c2ecf20Sopenharmony_ci	} else {
3648c2ecf20Sopenharmony_ci		geo->cylinders = sector_div(capacity, PF_HD_HDS * PF_HD_SPT);
3658c2ecf20Sopenharmony_ci		geo->heads = PF_HD_HDS;
3668c2ecf20Sopenharmony_ci		geo->sectors = PF_HD_SPT;
3678c2ecf20Sopenharmony_ci	}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	return 0;
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistatic int pf_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	struct pf_unit *pf = bdev->bd_disk->private_data;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	if (cmd != CDROMEJECT)
3778c2ecf20Sopenharmony_ci		return -EINVAL;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	if (pf->access != 1)
3808c2ecf20Sopenharmony_ci		return -EBUSY;
3818c2ecf20Sopenharmony_ci	mutex_lock(&pf_mutex);
3828c2ecf20Sopenharmony_ci	pf_eject(pf);
3838c2ecf20Sopenharmony_ci	mutex_unlock(&pf_mutex);
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	return 0;
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cistatic void pf_release(struct gendisk *disk, fmode_t mode)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	struct pf_unit *pf = disk->private_data;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	mutex_lock(&pf_mutex);
3938c2ecf20Sopenharmony_ci	if (pf->access <= 0) {
3948c2ecf20Sopenharmony_ci		mutex_unlock(&pf_mutex);
3958c2ecf20Sopenharmony_ci		WARN_ON(1);
3968c2ecf20Sopenharmony_ci		return;
3978c2ecf20Sopenharmony_ci	}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	pf->access--;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	if (!pf->access && pf->removable)
4028c2ecf20Sopenharmony_ci		pf_lock(pf, 0);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	mutex_unlock(&pf_mutex);
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic unsigned int pf_check_events(struct gendisk *disk, unsigned int clearing)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	return DISK_EVENT_MEDIA_CHANGE;
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistatic inline int status_reg(struct pf_unit *pf)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	return pi_read_regr(pf->pi, 1, 6);
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cistatic inline int read_reg(struct pf_unit *pf, int reg)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	return pi_read_regr(pf->pi, 0, reg);
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cistatic inline void write_reg(struct pf_unit *pf, int reg, int val)
4238c2ecf20Sopenharmony_ci{
4248c2ecf20Sopenharmony_ci	pi_write_regr(pf->pi, 0, reg, val);
4258c2ecf20Sopenharmony_ci}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_cistatic int pf_wait(struct pf_unit *pf, int go, int stop, char *fun, char *msg)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	int j, r, e, s, p;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	j = 0;
4328c2ecf20Sopenharmony_ci	while ((((r = status_reg(pf)) & go) || (stop && (!(r & stop))))
4338c2ecf20Sopenharmony_ci	       && (j++ < PF_SPIN))
4348c2ecf20Sopenharmony_ci		udelay(PF_SPIN_DEL);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	if ((r & (STAT_ERR & stop)) || (j > PF_SPIN)) {
4378c2ecf20Sopenharmony_ci		s = read_reg(pf, 7);
4388c2ecf20Sopenharmony_ci		e = read_reg(pf, 1);
4398c2ecf20Sopenharmony_ci		p = read_reg(pf, 2);
4408c2ecf20Sopenharmony_ci		if (j > PF_SPIN)
4418c2ecf20Sopenharmony_ci			e |= 0x100;
4428c2ecf20Sopenharmony_ci		if (fun)
4438c2ecf20Sopenharmony_ci			printk("%s: %s %s: alt=0x%x stat=0x%x err=0x%x"
4448c2ecf20Sopenharmony_ci			       " loop=%d phase=%d\n",
4458c2ecf20Sopenharmony_ci			       pf->name, fun, msg, r, s, e, j, p);
4468c2ecf20Sopenharmony_ci		return (e << 8) + s;
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci	return 0;
4498c2ecf20Sopenharmony_ci}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_cistatic int pf_command(struct pf_unit *pf, char *cmd, int dlen, char *fun)
4528c2ecf20Sopenharmony_ci{
4538c2ecf20Sopenharmony_ci	pi_connect(pf->pi);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	write_reg(pf, 6, 0xa0+0x10*pf->drive);
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	if (pf_wait(pf, STAT_BUSY | STAT_DRQ, 0, fun, "before command")) {
4588c2ecf20Sopenharmony_ci		pi_disconnect(pf->pi);
4598c2ecf20Sopenharmony_ci		return -1;
4608c2ecf20Sopenharmony_ci	}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	write_reg(pf, 4, dlen % 256);
4638c2ecf20Sopenharmony_ci	write_reg(pf, 5, dlen / 256);
4648c2ecf20Sopenharmony_ci	write_reg(pf, 7, 0xa0);	/* ATAPI packet command */
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	if (pf_wait(pf, STAT_BUSY, STAT_DRQ, fun, "command DRQ")) {
4678c2ecf20Sopenharmony_ci		pi_disconnect(pf->pi);
4688c2ecf20Sopenharmony_ci		return -1;
4698c2ecf20Sopenharmony_ci	}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	if (read_reg(pf, 2) != 1) {
4728c2ecf20Sopenharmony_ci		printk("%s: %s: command phase error\n", pf->name, fun);
4738c2ecf20Sopenharmony_ci		pi_disconnect(pf->pi);
4748c2ecf20Sopenharmony_ci		return -1;
4758c2ecf20Sopenharmony_ci	}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	pi_write_block(pf->pi, cmd, 12);
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	return 0;
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_cistatic int pf_completion(struct pf_unit *pf, char *buf, char *fun)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	int r, s, n;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	r = pf_wait(pf, STAT_BUSY, STAT_DRQ | STAT_READY | STAT_ERR,
4878c2ecf20Sopenharmony_ci		    fun, "completion");
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	if ((read_reg(pf, 2) & 2) && (read_reg(pf, 7) & STAT_DRQ)) {
4908c2ecf20Sopenharmony_ci		n = (((read_reg(pf, 4) + 256 * read_reg(pf, 5)) +
4918c2ecf20Sopenharmony_ci		      3) & 0xfffc);
4928c2ecf20Sopenharmony_ci		pi_read_block(pf->pi, buf, n);
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	s = pf_wait(pf, STAT_BUSY, STAT_READY | STAT_ERR, fun, "data done");
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	pi_disconnect(pf->pi);
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	return (r ? r : s);
5008c2ecf20Sopenharmony_ci}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_cistatic void pf_req_sense(struct pf_unit *pf, int quiet)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	char rs_cmd[12] =
5058c2ecf20Sopenharmony_ci	    { ATAPI_REQ_SENSE, pf->lun << 5, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0 };
5068c2ecf20Sopenharmony_ci	char buf[16];
5078c2ecf20Sopenharmony_ci	int r;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	r = pf_command(pf, rs_cmd, 16, "Request sense");
5108c2ecf20Sopenharmony_ci	mdelay(1);
5118c2ecf20Sopenharmony_ci	if (!r)
5128c2ecf20Sopenharmony_ci		pf_completion(pf, buf, "Request sense");
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	if ((!r) && (!quiet))
5158c2ecf20Sopenharmony_ci		printk("%s: Sense key: %x, ASC: %x, ASQ: %x\n",
5168c2ecf20Sopenharmony_ci		       pf->name, buf[2] & 0xf, buf[12], buf[13]);
5178c2ecf20Sopenharmony_ci}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_cistatic int pf_atapi(struct pf_unit *pf, char *cmd, int dlen, char *buf, char *fun)
5208c2ecf20Sopenharmony_ci{
5218c2ecf20Sopenharmony_ci	int r;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	r = pf_command(pf, cmd, dlen, fun);
5248c2ecf20Sopenharmony_ci	mdelay(1);
5258c2ecf20Sopenharmony_ci	if (!r)
5268c2ecf20Sopenharmony_ci		r = pf_completion(pf, buf, fun);
5278c2ecf20Sopenharmony_ci	if (r)
5288c2ecf20Sopenharmony_ci		pf_req_sense(pf, !fun);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	return r;
5318c2ecf20Sopenharmony_ci}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_cistatic void pf_lock(struct pf_unit *pf, int func)
5348c2ecf20Sopenharmony_ci{
5358c2ecf20Sopenharmony_ci	char lo_cmd[12] = { ATAPI_LOCK, pf->lun << 5, 0, 0, func, 0, 0, 0, 0, 0, 0, 0 };
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	pf_atapi(pf, lo_cmd, 0, pf_scratch, func ? "lock" : "unlock");
5388c2ecf20Sopenharmony_ci}
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_cistatic void pf_eject(struct pf_unit *pf)
5418c2ecf20Sopenharmony_ci{
5428c2ecf20Sopenharmony_ci	char ej_cmd[12] = { ATAPI_DOOR, pf->lun << 5, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0 };
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	pf_lock(pf, 0);
5458c2ecf20Sopenharmony_ci	pf_atapi(pf, ej_cmd, 0, pf_scratch, "eject");
5468c2ecf20Sopenharmony_ci}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci#define PF_RESET_TMO   30	/* in tenths of a second */
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic void pf_sleep(int cs)
5518c2ecf20Sopenharmony_ci{
5528c2ecf20Sopenharmony_ci	schedule_timeout_interruptible(cs);
5538c2ecf20Sopenharmony_ci}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci/* the ATAPI standard actually specifies the contents of all 7 registers
5568c2ecf20Sopenharmony_ci   after a reset, but the specification is ambiguous concerning the last
5578c2ecf20Sopenharmony_ci   two bytes, and different drives interpret the standard differently.
5588c2ecf20Sopenharmony_ci */
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_cistatic int pf_reset(struct pf_unit *pf)
5618c2ecf20Sopenharmony_ci{
5628c2ecf20Sopenharmony_ci	int i, k, flg;
5638c2ecf20Sopenharmony_ci	int expect[5] = { 1, 1, 1, 0x14, 0xeb };
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	pi_connect(pf->pi);
5668c2ecf20Sopenharmony_ci	write_reg(pf, 6, 0xa0+0x10*pf->drive);
5678c2ecf20Sopenharmony_ci	write_reg(pf, 7, 8);
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	pf_sleep(20 * HZ / 1000);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	k = 0;
5728c2ecf20Sopenharmony_ci	while ((k++ < PF_RESET_TMO) && (status_reg(pf) & STAT_BUSY))
5738c2ecf20Sopenharmony_ci		pf_sleep(HZ / 10);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	flg = 1;
5768c2ecf20Sopenharmony_ci	for (i = 0; i < 5; i++)
5778c2ecf20Sopenharmony_ci		flg &= (read_reg(pf, i + 1) == expect[i]);
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	if (verbose) {
5808c2ecf20Sopenharmony_ci		printk("%s: Reset (%d) signature = ", pf->name, k);
5818c2ecf20Sopenharmony_ci		for (i = 0; i < 5; i++)
5828c2ecf20Sopenharmony_ci			printk("%3x", read_reg(pf, i + 1));
5838c2ecf20Sopenharmony_ci		if (!flg)
5848c2ecf20Sopenharmony_ci			printk(" (incorrect)");
5858c2ecf20Sopenharmony_ci		printk("\n");
5868c2ecf20Sopenharmony_ci	}
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	pi_disconnect(pf->pi);
5898c2ecf20Sopenharmony_ci	return flg - 1;
5908c2ecf20Sopenharmony_ci}
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_cistatic void pf_mode_sense(struct pf_unit *pf)
5938c2ecf20Sopenharmony_ci{
5948c2ecf20Sopenharmony_ci	char ms_cmd[12] =
5958c2ecf20Sopenharmony_ci	    { ATAPI_MODE_SENSE, pf->lun << 5, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0 };
5968c2ecf20Sopenharmony_ci	char buf[8];
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	pf_atapi(pf, ms_cmd, 8, buf, "mode sense");
5998c2ecf20Sopenharmony_ci	pf->media_status = PF_RW;
6008c2ecf20Sopenharmony_ci	if (buf[3] & 0x80)
6018c2ecf20Sopenharmony_ci		pf->media_status = PF_RO;
6028c2ecf20Sopenharmony_ci}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_cistatic void xs(char *buf, char *targ, int offs, int len)
6058c2ecf20Sopenharmony_ci{
6068c2ecf20Sopenharmony_ci	int j, k, l;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	j = 0;
6098c2ecf20Sopenharmony_ci	l = 0;
6108c2ecf20Sopenharmony_ci	for (k = 0; k < len; k++)
6118c2ecf20Sopenharmony_ci		if ((buf[k + offs] != 0x20) || (buf[k + offs] != l))
6128c2ecf20Sopenharmony_ci			l = targ[j++] = buf[k + offs];
6138c2ecf20Sopenharmony_ci	if (l == 0x20)
6148c2ecf20Sopenharmony_ci		j--;
6158c2ecf20Sopenharmony_ci	targ[j] = 0;
6168c2ecf20Sopenharmony_ci}
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_cistatic int xl(char *buf, int offs)
6198c2ecf20Sopenharmony_ci{
6208c2ecf20Sopenharmony_ci	int v, k;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	v = 0;
6238c2ecf20Sopenharmony_ci	for (k = 0; k < 4; k++)
6248c2ecf20Sopenharmony_ci		v = v * 256 + (buf[k + offs] & 0xff);
6258c2ecf20Sopenharmony_ci	return v;
6268c2ecf20Sopenharmony_ci}
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_cistatic void pf_get_capacity(struct pf_unit *pf)
6298c2ecf20Sopenharmony_ci{
6308c2ecf20Sopenharmony_ci	char rc_cmd[12] = { ATAPI_CAPACITY, pf->lun << 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
6318c2ecf20Sopenharmony_ci	char buf[8];
6328c2ecf20Sopenharmony_ci	int bs;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	if (pf_atapi(pf, rc_cmd, 8, buf, "get capacity")) {
6358c2ecf20Sopenharmony_ci		pf->media_status = PF_NM;
6368c2ecf20Sopenharmony_ci		return;
6378c2ecf20Sopenharmony_ci	}
6388c2ecf20Sopenharmony_ci	set_capacity(pf->disk, xl(buf, 0) + 1);
6398c2ecf20Sopenharmony_ci	bs = xl(buf, 4);
6408c2ecf20Sopenharmony_ci	if (bs != 512) {
6418c2ecf20Sopenharmony_ci		set_capacity(pf->disk, 0);
6428c2ecf20Sopenharmony_ci		if (verbose)
6438c2ecf20Sopenharmony_ci			printk("%s: Drive %d, LUN %d,"
6448c2ecf20Sopenharmony_ci			       " unsupported block size %d\n",
6458c2ecf20Sopenharmony_ci			       pf->name, pf->drive, pf->lun, bs);
6468c2ecf20Sopenharmony_ci	}
6478c2ecf20Sopenharmony_ci}
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_cistatic int pf_identify(struct pf_unit *pf)
6508c2ecf20Sopenharmony_ci{
6518c2ecf20Sopenharmony_ci	int dt, s;
6528c2ecf20Sopenharmony_ci	char *ms[2] = { "master", "slave" };
6538c2ecf20Sopenharmony_ci	char mf[10], id[18];
6548c2ecf20Sopenharmony_ci	char id_cmd[12] =
6558c2ecf20Sopenharmony_ci	    { ATAPI_IDENTIFY, pf->lun << 5, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0 };
6568c2ecf20Sopenharmony_ci	char buf[36];
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	s = pf_atapi(pf, id_cmd, 36, buf, "identify");
6598c2ecf20Sopenharmony_ci	if (s)
6608c2ecf20Sopenharmony_ci		return -1;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	dt = buf[0] & 0x1f;
6638c2ecf20Sopenharmony_ci	if ((dt != 0) && (dt != 7)) {
6648c2ecf20Sopenharmony_ci		if (verbose)
6658c2ecf20Sopenharmony_ci			printk("%s: Drive %d, LUN %d, unsupported type %d\n",
6668c2ecf20Sopenharmony_ci			       pf->name, pf->drive, pf->lun, dt);
6678c2ecf20Sopenharmony_ci		return -1;
6688c2ecf20Sopenharmony_ci	}
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	xs(buf, mf, 8, 8);
6718c2ecf20Sopenharmony_ci	xs(buf, id, 16, 16);
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	pf->removable = (buf[1] & 0x80);
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	pf_mode_sense(pf);
6768c2ecf20Sopenharmony_ci	pf_mode_sense(pf);
6778c2ecf20Sopenharmony_ci	pf_mode_sense(pf);
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	pf_get_capacity(pf);
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	printk("%s: %s %s, %s LUN %d, type %d",
6828c2ecf20Sopenharmony_ci	       pf->name, mf, id, ms[pf->drive], pf->lun, dt);
6838c2ecf20Sopenharmony_ci	if (pf->removable)
6848c2ecf20Sopenharmony_ci		printk(", removable");
6858c2ecf20Sopenharmony_ci	if (pf->media_status == PF_NM)
6868c2ecf20Sopenharmony_ci		printk(", no media\n");
6878c2ecf20Sopenharmony_ci	else {
6888c2ecf20Sopenharmony_ci		if (pf->media_status == PF_RO)
6898c2ecf20Sopenharmony_ci			printk(", RO");
6908c2ecf20Sopenharmony_ci		printk(", %llu blocks\n",
6918c2ecf20Sopenharmony_ci			(unsigned long long)get_capacity(pf->disk));
6928c2ecf20Sopenharmony_ci	}
6938c2ecf20Sopenharmony_ci	return 0;
6948c2ecf20Sopenharmony_ci}
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci/*	returns  0, with id set if drive is detected
6978c2ecf20Sopenharmony_ci	        -1, if drive detection failed
6988c2ecf20Sopenharmony_ci*/
6998c2ecf20Sopenharmony_cistatic int pf_probe(struct pf_unit *pf)
7008c2ecf20Sopenharmony_ci{
7018c2ecf20Sopenharmony_ci	if (pf->drive == -1) {
7028c2ecf20Sopenharmony_ci		for (pf->drive = 0; pf->drive <= 1; pf->drive++)
7038c2ecf20Sopenharmony_ci			if (!pf_reset(pf)) {
7048c2ecf20Sopenharmony_ci				if (pf->lun != -1)
7058c2ecf20Sopenharmony_ci					return pf_identify(pf);
7068c2ecf20Sopenharmony_ci				else
7078c2ecf20Sopenharmony_ci					for (pf->lun = 0; pf->lun < 8; pf->lun++)
7088c2ecf20Sopenharmony_ci						if (!pf_identify(pf))
7098c2ecf20Sopenharmony_ci							return 0;
7108c2ecf20Sopenharmony_ci			}
7118c2ecf20Sopenharmony_ci	} else {
7128c2ecf20Sopenharmony_ci		if (pf_reset(pf))
7138c2ecf20Sopenharmony_ci			return -1;
7148c2ecf20Sopenharmony_ci		if (pf->lun != -1)
7158c2ecf20Sopenharmony_ci			return pf_identify(pf);
7168c2ecf20Sopenharmony_ci		for (pf->lun = 0; pf->lun < 8; pf->lun++)
7178c2ecf20Sopenharmony_ci			if (!pf_identify(pf))
7188c2ecf20Sopenharmony_ci				return 0;
7198c2ecf20Sopenharmony_ci	}
7208c2ecf20Sopenharmony_ci	return -1;
7218c2ecf20Sopenharmony_ci}
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_cistatic int pf_detect(void)
7248c2ecf20Sopenharmony_ci{
7258c2ecf20Sopenharmony_ci	struct pf_unit *pf = units;
7268c2ecf20Sopenharmony_ci	int k, unit;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	printk("%s: %s version %s, major %d, cluster %d, nice %d\n",
7298c2ecf20Sopenharmony_ci	       name, name, PF_VERSION, major, cluster, nice);
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	par_drv = pi_register_driver(name);
7328c2ecf20Sopenharmony_ci	if (!par_drv) {
7338c2ecf20Sopenharmony_ci		pr_err("failed to register %s driver\n", name);
7348c2ecf20Sopenharmony_ci		return -1;
7358c2ecf20Sopenharmony_ci	}
7368c2ecf20Sopenharmony_ci	k = 0;
7378c2ecf20Sopenharmony_ci	if (pf_drive_count == 0) {
7388c2ecf20Sopenharmony_ci		if (pi_init(pf->pi, 1, -1, -1, -1, -1, -1, pf_scratch, PI_PF,
7398c2ecf20Sopenharmony_ci			    verbose, pf->name)) {
7408c2ecf20Sopenharmony_ci			if (!pf_probe(pf) && pf->disk) {
7418c2ecf20Sopenharmony_ci				pf->present = 1;
7428c2ecf20Sopenharmony_ci				k++;
7438c2ecf20Sopenharmony_ci			} else
7448c2ecf20Sopenharmony_ci				pi_release(pf->pi);
7458c2ecf20Sopenharmony_ci		}
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	} else
7488c2ecf20Sopenharmony_ci		for (unit = 0; unit < PF_UNITS; unit++, pf++) {
7498c2ecf20Sopenharmony_ci			int *conf = *drives[unit];
7508c2ecf20Sopenharmony_ci			if (!conf[D_PRT])
7518c2ecf20Sopenharmony_ci				continue;
7528c2ecf20Sopenharmony_ci			if (pi_init(pf->pi, 0, conf[D_PRT], conf[D_MOD],
7538c2ecf20Sopenharmony_ci				    conf[D_UNI], conf[D_PRO], conf[D_DLY],
7548c2ecf20Sopenharmony_ci				    pf_scratch, PI_PF, verbose, pf->name)) {
7558c2ecf20Sopenharmony_ci				if (pf->disk && !pf_probe(pf)) {
7568c2ecf20Sopenharmony_ci					pf->present = 1;
7578c2ecf20Sopenharmony_ci					k++;
7588c2ecf20Sopenharmony_ci				} else
7598c2ecf20Sopenharmony_ci					pi_release(pf->pi);
7608c2ecf20Sopenharmony_ci			}
7618c2ecf20Sopenharmony_ci		}
7628c2ecf20Sopenharmony_ci	if (k)
7638c2ecf20Sopenharmony_ci		return 0;
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	printk("%s: No ATAPI disk detected\n", name);
7668c2ecf20Sopenharmony_ci	for (pf = units, unit = 0; unit < PF_UNITS; pf++, unit++) {
7678c2ecf20Sopenharmony_ci		if (!pf->disk)
7688c2ecf20Sopenharmony_ci			continue;
7698c2ecf20Sopenharmony_ci		blk_cleanup_queue(pf->disk->queue);
7708c2ecf20Sopenharmony_ci		pf->disk->queue = NULL;
7718c2ecf20Sopenharmony_ci		blk_mq_free_tag_set(&pf->tag_set);
7728c2ecf20Sopenharmony_ci		put_disk(pf->disk);
7738c2ecf20Sopenharmony_ci	}
7748c2ecf20Sopenharmony_ci	pi_unregister_driver(par_drv);
7758c2ecf20Sopenharmony_ci	return -1;
7768c2ecf20Sopenharmony_ci}
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci/* The i/o request engine */
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_cistatic int pf_start(struct pf_unit *pf, int cmd, int b, int c)
7818c2ecf20Sopenharmony_ci{
7828c2ecf20Sopenharmony_ci	int i;
7838c2ecf20Sopenharmony_ci	char io_cmd[12] = { cmd, pf->lun << 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++) {
7868c2ecf20Sopenharmony_ci		io_cmd[5 - i] = b & 0xff;
7878c2ecf20Sopenharmony_ci		b = b >> 8;
7888c2ecf20Sopenharmony_ci	}
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	io_cmd[8] = c & 0xff;
7918c2ecf20Sopenharmony_ci	io_cmd[7] = (c >> 8) & 0xff;
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	i = pf_command(pf, io_cmd, c * 512, "start i/o");
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	mdelay(1);
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	return i;
7988c2ecf20Sopenharmony_ci}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_cistatic int pf_ready(void)
8018c2ecf20Sopenharmony_ci{
8028c2ecf20Sopenharmony_ci	return (((status_reg(pf_current) & (STAT_BUSY | pf_mask)) == pf_mask));
8038c2ecf20Sopenharmony_ci}
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_cistatic int pf_queue;
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_cistatic int set_next_request(void)
8088c2ecf20Sopenharmony_ci{
8098c2ecf20Sopenharmony_ci	struct pf_unit *pf;
8108c2ecf20Sopenharmony_ci	int old_pos = pf_queue;
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	do {
8138c2ecf20Sopenharmony_ci		pf = &units[pf_queue];
8148c2ecf20Sopenharmony_ci		if (++pf_queue == PF_UNITS)
8158c2ecf20Sopenharmony_ci			pf_queue = 0;
8168c2ecf20Sopenharmony_ci		if (pf->present && !list_empty(&pf->rq_list)) {
8178c2ecf20Sopenharmony_ci			pf_req = list_first_entry(&pf->rq_list, struct request,
8188c2ecf20Sopenharmony_ci							queuelist);
8198c2ecf20Sopenharmony_ci			list_del_init(&pf_req->queuelist);
8208c2ecf20Sopenharmony_ci			blk_mq_start_request(pf_req);
8218c2ecf20Sopenharmony_ci			break;
8228c2ecf20Sopenharmony_ci		}
8238c2ecf20Sopenharmony_ci	} while (pf_queue != old_pos);
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	return pf_req != NULL;
8268c2ecf20Sopenharmony_ci}
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_cistatic void pf_end_request(blk_status_t err)
8298c2ecf20Sopenharmony_ci{
8308c2ecf20Sopenharmony_ci	if (!pf_req)
8318c2ecf20Sopenharmony_ci		return;
8328c2ecf20Sopenharmony_ci	if (!blk_update_request(pf_req, err, blk_rq_cur_bytes(pf_req))) {
8338c2ecf20Sopenharmony_ci		__blk_mq_end_request(pf_req, err);
8348c2ecf20Sopenharmony_ci		pf_req = NULL;
8358c2ecf20Sopenharmony_ci	}
8368c2ecf20Sopenharmony_ci}
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_cistatic void pf_request(void)
8398c2ecf20Sopenharmony_ci{
8408c2ecf20Sopenharmony_ci	if (pf_busy)
8418c2ecf20Sopenharmony_ci		return;
8428c2ecf20Sopenharmony_cirepeat:
8438c2ecf20Sopenharmony_ci	if (!pf_req && !set_next_request())
8448c2ecf20Sopenharmony_ci		return;
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	pf_current = pf_req->rq_disk->private_data;
8478c2ecf20Sopenharmony_ci	pf_block = blk_rq_pos(pf_req);
8488c2ecf20Sopenharmony_ci	pf_run = blk_rq_sectors(pf_req);
8498c2ecf20Sopenharmony_ci	pf_count = blk_rq_cur_sectors(pf_req);
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	if (pf_block + pf_count > get_capacity(pf_req->rq_disk)) {
8528c2ecf20Sopenharmony_ci		pf_end_request(BLK_STS_IOERR);
8538c2ecf20Sopenharmony_ci		goto repeat;
8548c2ecf20Sopenharmony_ci	}
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	pf_cmd = rq_data_dir(pf_req);
8578c2ecf20Sopenharmony_ci	pf_buf = bio_data(pf_req->bio);
8588c2ecf20Sopenharmony_ci	pf_retries = 0;
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci	pf_busy = 1;
8618c2ecf20Sopenharmony_ci	if (pf_cmd == READ)
8628c2ecf20Sopenharmony_ci		pi_do_claimed(pf_current->pi, do_pf_read);
8638c2ecf20Sopenharmony_ci	else if (pf_cmd == WRITE)
8648c2ecf20Sopenharmony_ci		pi_do_claimed(pf_current->pi, do_pf_write);
8658c2ecf20Sopenharmony_ci	else {
8668c2ecf20Sopenharmony_ci		pf_busy = 0;
8678c2ecf20Sopenharmony_ci		pf_end_request(BLK_STS_IOERR);
8688c2ecf20Sopenharmony_ci		goto repeat;
8698c2ecf20Sopenharmony_ci	}
8708c2ecf20Sopenharmony_ci}
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_cistatic blk_status_t pf_queue_rq(struct blk_mq_hw_ctx *hctx,
8738c2ecf20Sopenharmony_ci				const struct blk_mq_queue_data *bd)
8748c2ecf20Sopenharmony_ci{
8758c2ecf20Sopenharmony_ci	struct pf_unit *pf = hctx->queue->queuedata;
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	spin_lock_irq(&pf_spin_lock);
8788c2ecf20Sopenharmony_ci	list_add_tail(&bd->rq->queuelist, &pf->rq_list);
8798c2ecf20Sopenharmony_ci	pf_request();
8808c2ecf20Sopenharmony_ci	spin_unlock_irq(&pf_spin_lock);
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	return BLK_STS_OK;
8838c2ecf20Sopenharmony_ci}
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_cistatic int pf_next_buf(void)
8868c2ecf20Sopenharmony_ci{
8878c2ecf20Sopenharmony_ci	unsigned long saved_flags;
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	pf_count--;
8908c2ecf20Sopenharmony_ci	pf_run--;
8918c2ecf20Sopenharmony_ci	pf_buf += 512;
8928c2ecf20Sopenharmony_ci	pf_block++;
8938c2ecf20Sopenharmony_ci	if (!pf_run)
8948c2ecf20Sopenharmony_ci		return 1;
8958c2ecf20Sopenharmony_ci	if (!pf_count) {
8968c2ecf20Sopenharmony_ci		spin_lock_irqsave(&pf_spin_lock, saved_flags);
8978c2ecf20Sopenharmony_ci		pf_end_request(0);
8988c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&pf_spin_lock, saved_flags);
8998c2ecf20Sopenharmony_ci		if (!pf_req)
9008c2ecf20Sopenharmony_ci			return 1;
9018c2ecf20Sopenharmony_ci		pf_count = blk_rq_cur_sectors(pf_req);
9028c2ecf20Sopenharmony_ci		pf_buf = bio_data(pf_req->bio);
9038c2ecf20Sopenharmony_ci	}
9048c2ecf20Sopenharmony_ci	return 0;
9058c2ecf20Sopenharmony_ci}
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_cistatic inline void next_request(blk_status_t err)
9088c2ecf20Sopenharmony_ci{
9098c2ecf20Sopenharmony_ci	unsigned long saved_flags;
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pf_spin_lock, saved_flags);
9128c2ecf20Sopenharmony_ci	pf_end_request(err);
9138c2ecf20Sopenharmony_ci	pf_busy = 0;
9148c2ecf20Sopenharmony_ci	pf_request();
9158c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pf_spin_lock, saved_flags);
9168c2ecf20Sopenharmony_ci}
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci/* detach from the calling context - in case the spinlock is held */
9198c2ecf20Sopenharmony_cistatic void do_pf_read(void)
9208c2ecf20Sopenharmony_ci{
9218c2ecf20Sopenharmony_ci	ps_set_intr(do_pf_read_start, NULL, 0, nice);
9228c2ecf20Sopenharmony_ci}
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_cistatic void do_pf_read_start(void)
9258c2ecf20Sopenharmony_ci{
9268c2ecf20Sopenharmony_ci	pf_busy = 1;
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	if (pf_start(pf_current, ATAPI_READ_10, pf_block, pf_run)) {
9298c2ecf20Sopenharmony_ci		pi_disconnect(pf_current->pi);
9308c2ecf20Sopenharmony_ci		if (pf_retries < PF_MAX_RETRIES) {
9318c2ecf20Sopenharmony_ci			pf_retries++;
9328c2ecf20Sopenharmony_ci			pi_do_claimed(pf_current->pi, do_pf_read_start);
9338c2ecf20Sopenharmony_ci			return;
9348c2ecf20Sopenharmony_ci		}
9358c2ecf20Sopenharmony_ci		next_request(BLK_STS_IOERR);
9368c2ecf20Sopenharmony_ci		return;
9378c2ecf20Sopenharmony_ci	}
9388c2ecf20Sopenharmony_ci	pf_mask = STAT_DRQ;
9398c2ecf20Sopenharmony_ci	ps_set_intr(do_pf_read_drq, pf_ready, PF_TMO, nice);
9408c2ecf20Sopenharmony_ci}
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_cistatic void do_pf_read_drq(void)
9438c2ecf20Sopenharmony_ci{
9448c2ecf20Sopenharmony_ci	while (1) {
9458c2ecf20Sopenharmony_ci		if (pf_wait(pf_current, STAT_BUSY, STAT_DRQ | STAT_ERR,
9468c2ecf20Sopenharmony_ci			    "read block", "completion") & STAT_ERR) {
9478c2ecf20Sopenharmony_ci			pi_disconnect(pf_current->pi);
9488c2ecf20Sopenharmony_ci			if (pf_retries < PF_MAX_RETRIES) {
9498c2ecf20Sopenharmony_ci				pf_req_sense(pf_current, 0);
9508c2ecf20Sopenharmony_ci				pf_retries++;
9518c2ecf20Sopenharmony_ci				pi_do_claimed(pf_current->pi, do_pf_read_start);
9528c2ecf20Sopenharmony_ci				return;
9538c2ecf20Sopenharmony_ci			}
9548c2ecf20Sopenharmony_ci			next_request(BLK_STS_IOERR);
9558c2ecf20Sopenharmony_ci			return;
9568c2ecf20Sopenharmony_ci		}
9578c2ecf20Sopenharmony_ci		pi_read_block(pf_current->pi, pf_buf, 512);
9588c2ecf20Sopenharmony_ci		if (pf_next_buf())
9598c2ecf20Sopenharmony_ci			break;
9608c2ecf20Sopenharmony_ci	}
9618c2ecf20Sopenharmony_ci	pi_disconnect(pf_current->pi);
9628c2ecf20Sopenharmony_ci	next_request(0);
9638c2ecf20Sopenharmony_ci}
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_cistatic void do_pf_write(void)
9668c2ecf20Sopenharmony_ci{
9678c2ecf20Sopenharmony_ci	ps_set_intr(do_pf_write_start, NULL, 0, nice);
9688c2ecf20Sopenharmony_ci}
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_cistatic void do_pf_write_start(void)
9718c2ecf20Sopenharmony_ci{
9728c2ecf20Sopenharmony_ci	pf_busy = 1;
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	if (pf_start(pf_current, ATAPI_WRITE_10, pf_block, pf_run)) {
9758c2ecf20Sopenharmony_ci		pi_disconnect(pf_current->pi);
9768c2ecf20Sopenharmony_ci		if (pf_retries < PF_MAX_RETRIES) {
9778c2ecf20Sopenharmony_ci			pf_retries++;
9788c2ecf20Sopenharmony_ci			pi_do_claimed(pf_current->pi, do_pf_write_start);
9798c2ecf20Sopenharmony_ci			return;
9808c2ecf20Sopenharmony_ci		}
9818c2ecf20Sopenharmony_ci		next_request(BLK_STS_IOERR);
9828c2ecf20Sopenharmony_ci		return;
9838c2ecf20Sopenharmony_ci	}
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	while (1) {
9868c2ecf20Sopenharmony_ci		if (pf_wait(pf_current, STAT_BUSY, STAT_DRQ | STAT_ERR,
9878c2ecf20Sopenharmony_ci			    "write block", "data wait") & STAT_ERR) {
9888c2ecf20Sopenharmony_ci			pi_disconnect(pf_current->pi);
9898c2ecf20Sopenharmony_ci			if (pf_retries < PF_MAX_RETRIES) {
9908c2ecf20Sopenharmony_ci				pf_retries++;
9918c2ecf20Sopenharmony_ci				pi_do_claimed(pf_current->pi, do_pf_write_start);
9928c2ecf20Sopenharmony_ci				return;
9938c2ecf20Sopenharmony_ci			}
9948c2ecf20Sopenharmony_ci			next_request(BLK_STS_IOERR);
9958c2ecf20Sopenharmony_ci			return;
9968c2ecf20Sopenharmony_ci		}
9978c2ecf20Sopenharmony_ci		pi_write_block(pf_current->pi, pf_buf, 512);
9988c2ecf20Sopenharmony_ci		if (pf_next_buf())
9998c2ecf20Sopenharmony_ci			break;
10008c2ecf20Sopenharmony_ci	}
10018c2ecf20Sopenharmony_ci	pf_mask = 0;
10028c2ecf20Sopenharmony_ci	ps_set_intr(do_pf_write_done, pf_ready, PF_TMO, nice);
10038c2ecf20Sopenharmony_ci}
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_cistatic void do_pf_write_done(void)
10068c2ecf20Sopenharmony_ci{
10078c2ecf20Sopenharmony_ci	if (pf_wait(pf_current, STAT_BUSY, 0, "write block", "done") & STAT_ERR) {
10088c2ecf20Sopenharmony_ci		pi_disconnect(pf_current->pi);
10098c2ecf20Sopenharmony_ci		if (pf_retries < PF_MAX_RETRIES) {
10108c2ecf20Sopenharmony_ci			pf_retries++;
10118c2ecf20Sopenharmony_ci			pi_do_claimed(pf_current->pi, do_pf_write_start);
10128c2ecf20Sopenharmony_ci			return;
10138c2ecf20Sopenharmony_ci		}
10148c2ecf20Sopenharmony_ci		next_request(BLK_STS_IOERR);
10158c2ecf20Sopenharmony_ci		return;
10168c2ecf20Sopenharmony_ci	}
10178c2ecf20Sopenharmony_ci	pi_disconnect(pf_current->pi);
10188c2ecf20Sopenharmony_ci	next_request(0);
10198c2ecf20Sopenharmony_ci}
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_cistatic int __init pf_init(void)
10228c2ecf20Sopenharmony_ci{				/* preliminary initialisation */
10238c2ecf20Sopenharmony_ci	struct pf_unit *pf;
10248c2ecf20Sopenharmony_ci	int unit;
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci	if (disable)
10278c2ecf20Sopenharmony_ci		return -EINVAL;
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci	pf_init_units();
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	if (pf_detect())
10328c2ecf20Sopenharmony_ci		return -ENODEV;
10338c2ecf20Sopenharmony_ci	pf_busy = 0;
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	if (register_blkdev(major, name)) {
10368c2ecf20Sopenharmony_ci		for (pf = units, unit = 0; unit < PF_UNITS; pf++, unit++) {
10378c2ecf20Sopenharmony_ci			if (!pf->disk)
10388c2ecf20Sopenharmony_ci				continue;
10398c2ecf20Sopenharmony_ci			blk_cleanup_queue(pf->disk->queue);
10408c2ecf20Sopenharmony_ci			blk_mq_free_tag_set(&pf->tag_set);
10418c2ecf20Sopenharmony_ci			put_disk(pf->disk);
10428c2ecf20Sopenharmony_ci		}
10438c2ecf20Sopenharmony_ci		return -EBUSY;
10448c2ecf20Sopenharmony_ci	}
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	for (pf = units, unit = 0; unit < PF_UNITS; pf++, unit++) {
10478c2ecf20Sopenharmony_ci		struct gendisk *disk = pf->disk;
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci		if (!pf->present)
10508c2ecf20Sopenharmony_ci			continue;
10518c2ecf20Sopenharmony_ci		disk->private_data = pf;
10528c2ecf20Sopenharmony_ci		add_disk(disk);
10538c2ecf20Sopenharmony_ci	}
10548c2ecf20Sopenharmony_ci	return 0;
10558c2ecf20Sopenharmony_ci}
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_cistatic void __exit pf_exit(void)
10588c2ecf20Sopenharmony_ci{
10598c2ecf20Sopenharmony_ci	struct pf_unit *pf;
10608c2ecf20Sopenharmony_ci	int unit;
10618c2ecf20Sopenharmony_ci	unregister_blkdev(major, name);
10628c2ecf20Sopenharmony_ci	for (pf = units, unit = 0; unit < PF_UNITS; pf++, unit++) {
10638c2ecf20Sopenharmony_ci		if (!pf->disk)
10648c2ecf20Sopenharmony_ci			continue;
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci		if (pf->present)
10678c2ecf20Sopenharmony_ci			del_gendisk(pf->disk);
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci		blk_cleanup_queue(pf->disk->queue);
10708c2ecf20Sopenharmony_ci		blk_mq_free_tag_set(&pf->tag_set);
10718c2ecf20Sopenharmony_ci		put_disk(pf->disk);
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci		if (pf->present)
10748c2ecf20Sopenharmony_ci			pi_release(pf->pi);
10758c2ecf20Sopenharmony_ci	}
10768c2ecf20Sopenharmony_ci}
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
10798c2ecf20Sopenharmony_cimodule_init(pf_init)
10808c2ecf20Sopenharmony_cimodule_exit(pf_exit)
1081