ide-floppy_ioctl.c revision 6b0da28b2d0f4f4e2c55689fc062db569075ff60
1/*
2 * ide-floppy IOCTLs handling.
3 */
4
5#include <linux/kernel.h>
6#include <linux/ide.h>
7#include <linux/cdrom.h>
8
9#include <asm/unaligned.h>
10
11#include <scsi/scsi_ioctl.h>
12
13#include "ide-floppy.h"
14
15/*
16 * Obtain the list of formattable capacities.
17 * Very similar to ide_floppy_get_capacity, except that we push the capacity
18 * descriptors to userland, instead of our own structures.
19 *
20 * Userland gives us the following structure:
21 *
22 * struct idefloppy_format_capacities {
23 *	int nformats;
24 *	struct {
25 *		int nblocks;
26 *		int blocksize;
27 *	} formats[];
28 * };
29 *
30 * userland initializes nformats to the number of allocated formats[] records.
31 * On exit we set nformats to the number of records we've actually initialized.
32 */
33
34static int ide_floppy_get_format_capacities(ide_drive_t *drive, int __user *arg)
35{
36	struct ide_floppy_obj *floppy = drive->driver_data;
37	struct ide_atapi_pc pc;
38	u8 header_len, desc_cnt;
39	int i, blocks, length, u_array_size, u_index;
40	int __user *argp;
41
42	if (get_user(u_array_size, arg))
43		return -EFAULT;
44
45	if (u_array_size <= 0)
46		return -EINVAL;
47
48	ide_floppy_create_read_capacity_cmd(&pc);
49	if (ide_queue_pc_tail(drive, floppy->disk, &pc)) {
50		printk(KERN_ERR "ide-floppy: Can't get floppy parameters\n");
51		return -EIO;
52	}
53
54	header_len = pc.buf[3];
55	desc_cnt = header_len / 8; /* capacity descriptor of 8 bytes */
56
57	u_index = 0;
58	argp = arg + 1;
59
60	/*
61	 * We always skip the first capacity descriptor.  That's the current
62	 * capacity.  We are interested in the remaining descriptors, the
63	 * formattable capacities.
64	 */
65	for (i = 1; i < desc_cnt; i++) {
66		unsigned int desc_start = 4 + i*8;
67
68		if (u_index >= u_array_size)
69			break;	/* User-supplied buffer too small */
70
71		blocks = be32_to_cpup((__be32 *)&pc.buf[desc_start]);
72		length = be16_to_cpup((__be16 *)&pc.buf[desc_start + 6]);
73
74		if (put_user(blocks, argp))
75			return -EFAULT;
76
77		++argp;
78
79		if (put_user(length, argp))
80			return -EFAULT;
81
82		++argp;
83
84		++u_index;
85	}
86
87	if (put_user(u_index, arg))
88		return -EFAULT;
89
90	return 0;
91}
92
93static void ide_floppy_create_format_unit_cmd(struct ide_atapi_pc *pc, int b,
94		int l, int flags)
95{
96	ide_init_pc(pc);
97	pc->c[0] = GPCMD_FORMAT_UNIT;
98	pc->c[1] = 0x17;
99
100	memset(pc->buf, 0, 12);
101	pc->buf[1] = 0xA2;
102	/* Default format list header, u8 1: FOV/DCRT/IMM bits set */
103
104	if (flags & 1)				/* Verify bit on... */
105		pc->buf[1] ^= 0x20;		/* ... turn off DCRT bit */
106	pc->buf[3] = 8;
107
108	put_unaligned(cpu_to_be32(b), (unsigned int *)(&pc->buf[4]));
109	put_unaligned(cpu_to_be32(l), (unsigned int *)(&pc->buf[8]));
110	pc->buf_size = 12;
111	pc->flags |= PC_FLAG_WRITING;
112}
113
114static int ide_floppy_get_sfrp_bit(ide_drive_t *drive)
115{
116	idefloppy_floppy_t *floppy = drive->driver_data;
117	struct ide_atapi_pc pc;
118
119	drive->atapi_flags &= ~IDE_AFLAG_SRFP;
120
121	ide_floppy_create_mode_sense_cmd(&pc, IDEFLOPPY_CAPABILITIES_PAGE);
122	pc.flags |= PC_FLAG_SUPPRESS_ERROR;
123
124	if (ide_queue_pc_tail(drive, floppy->disk, &pc))
125		return 1;
126
127	if (pc.buf[8 + 2] & 0x40)
128		drive->atapi_flags |= IDE_AFLAG_SRFP;
129
130	return 0;
131}
132
133static int ide_floppy_format_unit(ide_drive_t *drive, int __user *arg)
134{
135	idefloppy_floppy_t *floppy = drive->driver_data;
136	struct ide_atapi_pc pc;
137	int blocks, length, flags, err = 0;
138
139	if (floppy->openers > 1) {
140		/* Don't format if someone is using the disk */
141		drive->atapi_flags &= ~IDE_AFLAG_FORMAT_IN_PROGRESS;
142		return -EBUSY;
143	}
144
145	drive->atapi_flags |= IDE_AFLAG_FORMAT_IN_PROGRESS;
146
147	/*
148	 * Send ATAPI_FORMAT_UNIT to the drive.
149	 *
150	 * Userland gives us the following structure:
151	 *
152	 * struct idefloppy_format_command {
153	 *        int nblocks;
154	 *        int blocksize;
155	 *        int flags;
156	 *        } ;
157	 *
158	 * flags is a bitmask, currently, the only defined flag is:
159	 *
160	 *        0x01 - verify media after format.
161	 */
162	if (get_user(blocks, arg) ||
163			get_user(length, arg+1) ||
164			get_user(flags, arg+2)) {
165		err = -EFAULT;
166		goto out;
167	}
168
169	(void)ide_floppy_get_sfrp_bit(drive);
170	ide_floppy_create_format_unit_cmd(&pc, blocks, length, flags);
171
172	if (ide_queue_pc_tail(drive, floppy->disk, &pc))
173		err = -EIO;
174
175out:
176	if (err)
177		drive->atapi_flags &= ~IDE_AFLAG_FORMAT_IN_PROGRESS;
178	return err;
179}
180
181/*
182 * Get ATAPI_FORMAT_UNIT progress indication.
183 *
184 * Userland gives a pointer to an int.  The int is set to a progress
185 * indicator 0-65536, with 65536=100%.
186 *
187 * If the drive does not support format progress indication, we just check
188 * the dsc bit, and return either 0 or 65536.
189 */
190
191static int ide_floppy_get_format_progress(ide_drive_t *drive, int __user *arg)
192{
193	idefloppy_floppy_t *floppy = drive->driver_data;
194	struct ide_atapi_pc pc;
195	int progress_indication = 0x10000;
196
197	if (drive->atapi_flags & IDE_AFLAG_SRFP) {
198		ide_create_request_sense_cmd(drive, &pc);
199		if (ide_queue_pc_tail(drive, floppy->disk, &pc))
200			return -EIO;
201
202		if (floppy->sense_key == 2 &&
203		    floppy->asc == 4 &&
204		    floppy->ascq == 4)
205			progress_indication = floppy->progress_indication;
206
207		/* Else assume format_unit has finished, and we're at 0x10000 */
208	} else {
209		ide_hwif_t *hwif = drive->hwif;
210		unsigned long flags;
211		u8 stat;
212
213		local_irq_save(flags);
214		stat = hwif->tp_ops->read_status(hwif);
215		local_irq_restore(flags);
216
217		progress_indication = ((stat & ATA_DSC) == 0) ? 0 : 0x10000;
218	}
219
220	if (put_user(progress_indication, arg))
221		return -EFAULT;
222
223	return 0;
224}
225
226int ide_floppy_format_ioctl(ide_drive_t *drive, struct file *file,
227			    unsigned int cmd, void __user *argp)
228{
229	switch (cmd) {
230	case IDEFLOPPY_IOCTL_FORMAT_SUPPORTED:
231		return 0;
232	case IDEFLOPPY_IOCTL_FORMAT_GET_CAPACITY:
233		return ide_floppy_get_format_capacities(drive, argp);
234	case IDEFLOPPY_IOCTL_FORMAT_START:
235		if (!(file->f_mode & 2))
236			return -EPERM;
237		return ide_floppy_format_unit(drive, (int __user *)argp);
238	case IDEFLOPPY_IOCTL_FORMAT_GET_PROGRESS:
239		return ide_floppy_get_format_progress(drive, argp);
240	default:
241		return -ENOTTY;
242	}
243}
244