diff options
Diffstat (limited to 'drivers/ide/ide-proc.c')
-rw-r--r-- | drivers/ide/ide-proc.c | 310 |
1 files changed, 308 insertions, 2 deletions
diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c index a9e0b30fb1f2..949a6f609d84 100644 --- a/drivers/ide/ide-proc.c +++ b/drivers/ide/ide-proc.c | |||
@@ -3,6 +3,8 @@ | |||
3 | * | 3 | * |
4 | * Copyright (C) 1997-1998 Mark Lord | 4 | * Copyright (C) 1997-1998 Mark Lord |
5 | * Copyright (C) 2003 Red Hat <alan@redhat.com> | 5 | * Copyright (C) 2003 Red Hat <alan@redhat.com> |
6 | * | ||
7 | * Some code was moved here from ide.c, see it for original copyrights. | ||
6 | */ | 8 | */ |
7 | 9 | ||
8 | /* | 10 | /* |
@@ -121,6 +123,265 @@ static int proc_ide_read_identify | |||
121 | PROC_IDE_READ_RETURN(page,start,off,count,eof,len); | 123 | PROC_IDE_READ_RETURN(page,start,off,count,eof,len); |
122 | } | 124 | } |
123 | 125 | ||
126 | /** | ||
127 | * __ide_add_setting - add an ide setting option | ||
128 | * @drive: drive to use | ||
129 | * @name: setting name | ||
130 | * @rw: true if the function is read write | ||
131 | * @data_type: type of data | ||
132 | * @min: range minimum | ||
133 | * @max: range maximum | ||
134 | * @mul_factor: multiplication scale | ||
135 | * @div_factor: divison scale | ||
136 | * @data: private data field | ||
137 | * @set: setting | ||
138 | * @auto_remove: setting auto removal flag | ||
139 | * | ||
140 | * Removes the setting named from the device if it is present. | ||
141 | * The function takes the settings_lock to protect against | ||
142 | * parallel changes. This function must not be called from IRQ | ||
143 | * context. Returns 0 on success or -1 on failure. | ||
144 | * | ||
145 | * BUGS: This code is seriously over-engineered. There is also | ||
146 | * magic about how the driver specific features are setup. If | ||
147 | * a driver is attached we assume the driver settings are auto | ||
148 | * remove. | ||
149 | */ | ||
150 | |||
151 | static int __ide_add_setting(ide_drive_t *drive, const char *name, int rw, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set, int auto_remove) | ||
152 | { | ||
153 | ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting = NULL; | ||
154 | |||
155 | down(&ide_setting_sem); | ||
156 | while ((*p) && strcmp((*p)->name, name) < 0) | ||
157 | p = &((*p)->next); | ||
158 | if ((setting = kzalloc(sizeof(*setting), GFP_KERNEL)) == NULL) | ||
159 | goto abort; | ||
160 | if ((setting->name = kmalloc(strlen(name) + 1, GFP_KERNEL)) == NULL) | ||
161 | goto abort; | ||
162 | strcpy(setting->name, name); | ||
163 | setting->rw = rw; | ||
164 | setting->data_type = data_type; | ||
165 | setting->min = min; | ||
166 | setting->max = max; | ||
167 | setting->mul_factor = mul_factor; | ||
168 | setting->div_factor = div_factor; | ||
169 | setting->data = data; | ||
170 | setting->set = set; | ||
171 | |||
172 | setting->next = *p; | ||
173 | if (auto_remove) | ||
174 | setting->auto_remove = 1; | ||
175 | *p = setting; | ||
176 | up(&ide_setting_sem); | ||
177 | return 0; | ||
178 | abort: | ||
179 | up(&ide_setting_sem); | ||
180 | kfree(setting); | ||
181 | return -1; | ||
182 | } | ||
183 | |||
184 | int ide_add_setting(ide_drive_t *drive, const char *name, int rw, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set) | ||
185 | { | ||
186 | return __ide_add_setting(drive, name, rw, data_type, min, max, mul_factor, div_factor, data, set, 1); | ||
187 | } | ||
188 | |||
189 | EXPORT_SYMBOL(ide_add_setting); | ||
190 | |||
191 | /** | ||
192 | * __ide_remove_setting - remove an ide setting option | ||
193 | * @drive: drive to use | ||
194 | * @name: setting name | ||
195 | * | ||
196 | * Removes the setting named from the device if it is present. | ||
197 | * The caller must hold the setting semaphore. | ||
198 | */ | ||
199 | |||
200 | static void __ide_remove_setting (ide_drive_t *drive, char *name) | ||
201 | { | ||
202 | ide_settings_t **p, *setting; | ||
203 | |||
204 | p = (ide_settings_t **) &drive->settings; | ||
205 | |||
206 | while ((*p) && strcmp((*p)->name, name)) | ||
207 | p = &((*p)->next); | ||
208 | if ((setting = (*p)) == NULL) | ||
209 | return; | ||
210 | |||
211 | (*p) = setting->next; | ||
212 | |||
213 | kfree(setting->name); | ||
214 | kfree(setting); | ||
215 | } | ||
216 | |||
217 | /** | ||
218 | * auto_remove_settings - remove driver specific settings | ||
219 | * @drive: drive | ||
220 | * | ||
221 | * Automatically remove all the driver specific settings for this | ||
222 | * drive. This function may not be called from IRQ context. The | ||
223 | * caller must hold ide_setting_sem. | ||
224 | */ | ||
225 | |||
226 | static void auto_remove_settings (ide_drive_t *drive) | ||
227 | { | ||
228 | ide_settings_t *setting; | ||
229 | repeat: | ||
230 | setting = drive->settings; | ||
231 | while (setting) { | ||
232 | if (setting->auto_remove) { | ||
233 | __ide_remove_setting(drive, setting->name); | ||
234 | goto repeat; | ||
235 | } | ||
236 | setting = setting->next; | ||
237 | } | ||
238 | } | ||
239 | |||
240 | /** | ||
241 | * ide_find_setting_by_name - find a drive specific setting | ||
242 | * @drive: drive to scan | ||
243 | * @name: setting name | ||
244 | * | ||
245 | * Scan's the device setting table for a matching entry and returns | ||
246 | * this or NULL if no entry is found. The caller must hold the | ||
247 | * setting semaphore | ||
248 | */ | ||
249 | |||
250 | static ide_settings_t *ide_find_setting_by_name(ide_drive_t *drive, char *name) | ||
251 | { | ||
252 | ide_settings_t *setting = drive->settings; | ||
253 | |||
254 | while (setting) { | ||
255 | if (strcmp(setting->name, name) == 0) | ||
256 | break; | ||
257 | setting = setting->next; | ||
258 | } | ||
259 | return setting; | ||
260 | } | ||
261 | |||
262 | /** | ||
263 | * ide_read_setting - read an IDE setting | ||
264 | * @drive: drive to read from | ||
265 | * @setting: drive setting | ||
266 | * | ||
267 | * Read a drive setting and return the value. The caller | ||
268 | * must hold the ide_setting_sem when making this call. | ||
269 | * | ||
270 | * BUGS: the data return and error are the same return value | ||
271 | * so an error -EINVAL and true return of the same value cannot | ||
272 | * be told apart | ||
273 | */ | ||
274 | |||
275 | static int ide_read_setting(ide_drive_t *drive, ide_settings_t *setting) | ||
276 | { | ||
277 | int val = -EINVAL; | ||
278 | unsigned long flags; | ||
279 | |||
280 | if ((setting->rw & SETTING_READ)) { | ||
281 | spin_lock_irqsave(&ide_lock, flags); | ||
282 | switch(setting->data_type) { | ||
283 | case TYPE_BYTE: | ||
284 | val = *((u8 *) setting->data); | ||
285 | break; | ||
286 | case TYPE_SHORT: | ||
287 | val = *((u16 *) setting->data); | ||
288 | break; | ||
289 | case TYPE_INT: | ||
290 | val = *((u32 *) setting->data); | ||
291 | break; | ||
292 | } | ||
293 | spin_unlock_irqrestore(&ide_lock, flags); | ||
294 | } | ||
295 | return val; | ||
296 | } | ||
297 | |||
298 | /** | ||
299 | * ide_write_setting - read an IDE setting | ||
300 | * @drive: drive to read from | ||
301 | * @setting: drive setting | ||
302 | * @val: value | ||
303 | * | ||
304 | * Write a drive setting if it is possible. The caller | ||
305 | * must hold the ide_setting_sem when making this call. | ||
306 | * | ||
307 | * BUGS: the data return and error are the same return value | ||
308 | * so an error -EINVAL and true return of the same value cannot | ||
309 | * be told apart | ||
310 | * | ||
311 | * FIXME: This should be changed to enqueue a special request | ||
312 | * to the driver to change settings, and then wait on a sema for completion. | ||
313 | * The current scheme of polling is kludgy, though safe enough. | ||
314 | */ | ||
315 | |||
316 | static int ide_write_setting(ide_drive_t *drive, ide_settings_t *setting, int val) | ||
317 | { | ||
318 | if (!capable(CAP_SYS_ADMIN)) | ||
319 | return -EACCES; | ||
320 | if (setting->set) | ||
321 | return setting->set(drive, val); | ||
322 | if (!(setting->rw & SETTING_WRITE)) | ||
323 | return -EPERM; | ||
324 | if (val < setting->min || val > setting->max) | ||
325 | return -EINVAL; | ||
326 | if (ide_spin_wait_hwgroup(drive)) | ||
327 | return -EBUSY; | ||
328 | switch (setting->data_type) { | ||
329 | case TYPE_BYTE: | ||
330 | *((u8 *) setting->data) = val; | ||
331 | break; | ||
332 | case TYPE_SHORT: | ||
333 | *((u16 *) setting->data) = val; | ||
334 | break; | ||
335 | case TYPE_INT: | ||
336 | *((u32 *) setting->data) = val; | ||
337 | break; | ||
338 | } | ||
339 | spin_unlock_irq(&ide_lock); | ||
340 | return 0; | ||
341 | } | ||
342 | |||
343 | static int set_xfer_rate (ide_drive_t *drive, int arg) | ||
344 | { | ||
345 | int err; | ||
346 | |||
347 | if (arg < 0 || arg > 70) | ||
348 | return -EINVAL; | ||
349 | |||
350 | err = ide_wait_cmd(drive, | ||
351 | WIN_SETFEATURES, (u8) arg, | ||
352 | SETFEATURES_XFER, 0, NULL); | ||
353 | |||
354 | if (!err && arg) { | ||
355 | ide_set_xfer_rate(drive, (u8) arg); | ||
356 | ide_driveid_update(drive); | ||
357 | } | ||
358 | return err; | ||
359 | } | ||
360 | |||
361 | /** | ||
362 | * ide_add_generic_settings - generic ide settings | ||
363 | * @drive: drive being configured | ||
364 | * | ||
365 | * Add the generic parts of the system settings to the /proc files. | ||
366 | * The caller must not be holding the ide_setting_sem. | ||
367 | */ | ||
368 | |||
369 | void ide_add_generic_settings (ide_drive_t *drive) | ||
370 | { | ||
371 | /* | ||
372 | * drive setting name read/write access data type min max mul_factor div_factor data pointer set function | ||
373 | */ | ||
374 | __ide_add_setting(drive, "io_32bit", drive->no_io_32bit ? SETTING_READ : SETTING_RW, TYPE_BYTE, 0, 1 + (SUPPORT_VLB_SYNC << 1), 1, 1, &drive->io_32bit, set_io_32bit, 0); | ||
375 | __ide_add_setting(drive, "keepsettings", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->keep_settings, NULL, 0); | ||
376 | __ide_add_setting(drive, "nice1", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->nice1, NULL, 0); | ||
377 | __ide_add_setting(drive, "pio_mode", SETTING_WRITE, TYPE_BYTE, 0, 255, 1, 1, NULL, set_pio_mode, 0); | ||
378 | __ide_add_setting(drive, "unmaskirq", drive->no_unmask ? SETTING_READ : SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->unmask, NULL, 0); | ||
379 | __ide_add_setting(drive, "using_dma", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->using_dma, set_using_dma, 0); | ||
380 | __ide_add_setting(drive, "init_speed", SETTING_RW, TYPE_BYTE, 0, 70, 1, 1, &drive->init_speed, NULL, 0); | ||
381 | __ide_add_setting(drive, "current_speed", SETTING_RW, TYPE_BYTE, 0, 70, 1, 1, &drive->current_speed, set_xfer_rate, 0); | ||
382 | __ide_add_setting(drive, "number", SETTING_RW, TYPE_BYTE, 0, 3, 1, 1, &drive->dn, NULL, 0); | ||
383 | } | ||
384 | |||
124 | static void proc_ide_settings_warn(void) | 385 | static void proc_ide_settings_warn(void) |
125 | { | 386 | { |
126 | static int warned = 0; | 387 | static int warned = 0; |
@@ -399,7 +660,7 @@ static ide_proc_entry_t generic_drive_entries[] = { | |||
399 | { NULL, 0, NULL, NULL } | 660 | { NULL, 0, NULL, NULL } |
400 | }; | 661 | }; |
401 | 662 | ||
402 | void ide_add_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p, void *data) | 663 | static void ide_add_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p, void *data) |
403 | { | 664 | { |
404 | struct proc_dir_entry *ent; | 665 | struct proc_dir_entry *ent; |
405 | 666 | ||
@@ -415,7 +676,7 @@ void ide_add_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p, void | |||
415 | } | 676 | } |
416 | } | 677 | } |
417 | 678 | ||
418 | void ide_remove_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p) | 679 | static void ide_remove_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p) |
419 | { | 680 | { |
420 | if (!dir || !p) | 681 | if (!dir || !p) |
421 | return; | 682 | return; |
@@ -425,6 +686,51 @@ void ide_remove_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p) | |||
425 | } | 686 | } |
426 | } | 687 | } |
427 | 688 | ||
689 | void ide_proc_register_driver(ide_drive_t *drive, ide_driver_t *driver) | ||
690 | { | ||
691 | ide_add_proc_entries(drive->proc, driver->proc, drive); | ||
692 | } | ||
693 | |||
694 | EXPORT_SYMBOL(ide_proc_register_driver); | ||
695 | |||
696 | /** | ||
697 | * ide_proc_unregister_driver - remove driver specific data | ||
698 | * @drive: drive | ||
699 | * @driver: driver | ||
700 | * | ||
701 | * Clean up the driver specific /proc files and IDE settings | ||
702 | * for a given drive. | ||
703 | * | ||
704 | * Takes ide_setting_sem and ide_lock. | ||
705 | * Caller must hold none of the locks. | ||
706 | */ | ||
707 | |||
708 | void ide_proc_unregister_driver(ide_drive_t *drive, ide_driver_t *driver) | ||
709 | { | ||
710 | unsigned long flags; | ||
711 | |||
712 | ide_remove_proc_entries(drive->proc, driver->proc); | ||
713 | |||
714 | down(&ide_setting_sem); | ||
715 | spin_lock_irqsave(&ide_lock, flags); | ||
716 | /* | ||
717 | * ide_setting_sem protects the settings list | ||
718 | * ide_lock protects the use of settings | ||
719 | * | ||
720 | * so we need to hold both, ide_settings_sem because we want to | ||
721 | * modify the settings list, and ide_lock because we cannot take | ||
722 | * a setting out that is being used. | ||
723 | * | ||
724 | * OTOH both ide_{read,write}_setting are only ever used under | ||
725 | * ide_setting_sem. | ||
726 | */ | ||
727 | auto_remove_settings(drive); | ||
728 | spin_unlock_irqrestore(&ide_lock, flags); | ||
729 | up(&ide_setting_sem); | ||
730 | } | ||
731 | |||
732 | EXPORT_SYMBOL(ide_proc_unregister_driver); | ||
733 | |||
428 | static void create_proc_ide_drives(ide_hwif_t *hwif) | 734 | static void create_proc_ide_drives(ide_hwif_t *hwif) |
429 | { | 735 | { |
430 | int d; | 736 | int d; |