diff options
Diffstat (limited to 'drivers/ide/ide-floppy_ioctl.c')
-rw-r--r-- | drivers/ide/ide-floppy_ioctl.c | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/drivers/ide/ide-floppy_ioctl.c b/drivers/ide/ide-floppy_ioctl.c new file mode 100644 index 000000000000..5ffc4512d14b --- /dev/null +++ b/drivers/ide/ide-floppy_ioctl.c | |||
@@ -0,0 +1,243 @@ | |||
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 | |||
34 | static 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 | |||
93 | static 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 | |||
114 | static 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 | |||
133 | static 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 | |||
175 | out: | ||
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 | |||
191 | static 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_floppy_create_request_sense_cmd(&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 | |||
226 | int 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 | } | ||