diff options
Diffstat (limited to 'drivers/ide/ide-proc.c')
-rw-r--r-- | drivers/ide/ide-proc.c | 344 |
1 files changed, 325 insertions, 19 deletions
diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c index a9e0b30fb1f2..d50bd996ff22 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 | /* |
@@ -37,6 +39,8 @@ | |||
37 | 39 | ||
38 | #include <asm/io.h> | 40 | #include <asm/io.h> |
39 | 41 | ||
42 | static struct proc_dir_entry *proc_ide_root; | ||
43 | |||
40 | static int proc_ide_read_imodel | 44 | static int proc_ide_read_imodel |
41 | (char *page, char **start, off_t off, int count, int *eof, void *data) | 45 | (char *page, char **start, off_t off, int count, int *eof, void *data) |
42 | { | 46 | { |
@@ -121,6 +125,265 @@ static int proc_ide_read_identify | |||
121 | PROC_IDE_READ_RETURN(page,start,off,count,eof,len); | 125 | PROC_IDE_READ_RETURN(page,start,off,count,eof,len); |
122 | } | 126 | } |
123 | 127 | ||
128 | /** | ||
129 | * __ide_add_setting - add an ide setting option | ||
130 | * @drive: drive to use | ||
131 | * @name: setting name | ||
132 | * @rw: true if the function is read write | ||
133 | * @data_type: type of data | ||
134 | * @min: range minimum | ||
135 | * @max: range maximum | ||
136 | * @mul_factor: multiplication scale | ||
137 | * @div_factor: divison scale | ||
138 | * @data: private data field | ||
139 | * @set: setting | ||
140 | * @auto_remove: setting auto removal flag | ||
141 | * | ||
142 | * Removes the setting named from the device if it is present. | ||
143 | * The function takes the settings_lock to protect against | ||
144 | * parallel changes. This function must not be called from IRQ | ||
145 | * context. Returns 0 on success or -1 on failure. | ||
146 | * | ||
147 | * BUGS: This code is seriously over-engineered. There is also | ||
148 | * magic about how the driver specific features are setup. If | ||
149 | * a driver is attached we assume the driver settings are auto | ||
150 | * remove. | ||
151 | */ | ||
152 | |||
153 | 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) | ||
154 | { | ||
155 | ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting = NULL; | ||
156 | |||
157 | down(&ide_setting_sem); | ||
158 | while ((*p) && strcmp((*p)->name, name) < 0) | ||
159 | p = &((*p)->next); | ||
160 | if ((setting = kzalloc(sizeof(*setting), GFP_KERNEL)) == NULL) | ||
161 | goto abort; | ||
162 | if ((setting->name = kmalloc(strlen(name) + 1, GFP_KERNEL)) == NULL) | ||
163 | goto abort; | ||
164 | strcpy(setting->name, name); | ||
165 | setting->rw = rw; | ||
166 | setting->data_type = data_type; | ||
167 | setting->min = min; | ||
168 | setting->max = max; | ||
169 | setting->mul_factor = mul_factor; | ||
170 | setting->div_factor = div_factor; | ||
171 | setting->data = data; | ||
172 | setting->set = set; | ||
173 | |||
174 | setting->next = *p; | ||
175 | if (auto_remove) | ||
176 | setting->auto_remove = 1; | ||
177 | *p = setting; | ||
178 | up(&ide_setting_sem); | ||
179 | return 0; | ||
180 | abort: | ||
181 | up(&ide_setting_sem); | ||
182 | kfree(setting); | ||
183 | return -1; | ||
184 | } | ||
185 | |||
186 | 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) | ||
187 | { | ||
188 | return __ide_add_setting(drive, name, rw, data_type, min, max, mul_factor, div_factor, data, set, 1); | ||
189 | } | ||
190 | |||
191 | EXPORT_SYMBOL(ide_add_setting); | ||
192 | |||
193 | /** | ||
194 | * __ide_remove_setting - remove an ide setting option | ||
195 | * @drive: drive to use | ||
196 | * @name: setting name | ||
197 | * | ||
198 | * Removes the setting named from the device if it is present. | ||
199 | * The caller must hold the setting semaphore. | ||
200 | */ | ||
201 | |||
202 | static void __ide_remove_setting (ide_drive_t *drive, char *name) | ||
203 | { | ||
204 | ide_settings_t **p, *setting; | ||
205 | |||
206 | p = (ide_settings_t **) &drive->settings; | ||
207 | |||
208 | while ((*p) && strcmp((*p)->name, name)) | ||
209 | p = &((*p)->next); | ||
210 | if ((setting = (*p)) == NULL) | ||
211 | return; | ||
212 | |||
213 | (*p) = setting->next; | ||
214 | |||
215 | kfree(setting->name); | ||
216 | kfree(setting); | ||
217 | } | ||
218 | |||
219 | /** | ||
220 | * auto_remove_settings - remove driver specific settings | ||
221 | * @drive: drive | ||
222 | * | ||
223 | * Automatically remove all the driver specific settings for this | ||
224 | * drive. This function may not be called from IRQ context. The | ||
225 | * caller must hold ide_setting_sem. | ||
226 | */ | ||
227 | |||
228 | static void auto_remove_settings (ide_drive_t *drive) | ||
229 | { | ||
230 | ide_settings_t *setting; | ||
231 | repeat: | ||
232 | setting = drive->settings; | ||
233 | while (setting) { | ||
234 | if (setting->auto_remove) { | ||
235 | __ide_remove_setting(drive, setting->name); | ||
236 | goto repeat; | ||
237 | } | ||
238 | setting = setting->next; | ||
239 | } | ||
240 | } | ||
241 | |||
242 | /** | ||
243 | * ide_find_setting_by_name - find a drive specific setting | ||
244 | * @drive: drive to scan | ||
245 | * @name: setting name | ||
246 | * | ||
247 | * Scan's the device setting table for a matching entry and returns | ||
248 | * this or NULL if no entry is found. The caller must hold the | ||
249 | * setting semaphore | ||
250 | */ | ||
251 | |||
252 | static ide_settings_t *ide_find_setting_by_name(ide_drive_t *drive, char *name) | ||
253 | { | ||
254 | ide_settings_t *setting = drive->settings; | ||
255 | |||
256 | while (setting) { | ||
257 | if (strcmp(setting->name, name) == 0) | ||
258 | break; | ||
259 | setting = setting->next; | ||
260 | } | ||
261 | return setting; | ||
262 | } | ||
263 | |||
264 | /** | ||
265 | * ide_read_setting - read an IDE setting | ||
266 | * @drive: drive to read from | ||
267 | * @setting: drive setting | ||
268 | * | ||
269 | * Read a drive setting and return the value. The caller | ||
270 | * must hold the ide_setting_sem when making this call. | ||
271 | * | ||
272 | * BUGS: the data return and error are the same return value | ||
273 | * so an error -EINVAL and true return of the same value cannot | ||
274 | * be told apart | ||
275 | */ | ||
276 | |||
277 | static int ide_read_setting(ide_drive_t *drive, ide_settings_t *setting) | ||
278 | { | ||
279 | int val = -EINVAL; | ||
280 | unsigned long flags; | ||
281 | |||
282 | if ((setting->rw & SETTING_READ)) { | ||
283 | spin_lock_irqsave(&ide_lock, flags); | ||
284 | switch(setting->data_type) { | ||
285 | case TYPE_BYTE: | ||
286 | val = *((u8 *) setting->data); | ||
287 | break; | ||
288 | case TYPE_SHORT: | ||
289 | val = *((u16 *) setting->data); | ||
290 | break; | ||
291 | case TYPE_INT: | ||
292 | val = *((u32 *) setting->data); | ||
293 | break; | ||
294 | } | ||
295 | spin_unlock_irqrestore(&ide_lock, flags); | ||
296 | } | ||
297 | return val; | ||
298 | } | ||
299 | |||
300 | /** | ||
301 | * ide_write_setting - read an IDE setting | ||
302 | * @drive: drive to read from | ||
303 | * @setting: drive setting | ||
304 | * @val: value | ||
305 | * | ||
306 | * Write a drive setting if it is possible. The caller | ||
307 | * must hold the ide_setting_sem when making this call. | ||
308 | * | ||
309 | * BUGS: the data return and error are the same return value | ||
310 | * so an error -EINVAL and true return of the same value cannot | ||
311 | * be told apart | ||
312 | * | ||
313 | * FIXME: This should be changed to enqueue a special request | ||
314 | * to the driver to change settings, and then wait on a sema for completion. | ||
315 | * The current scheme of polling is kludgy, though safe enough. | ||
316 | */ | ||
317 | |||
318 | static int ide_write_setting(ide_drive_t *drive, ide_settings_t *setting, int val) | ||
319 | { | ||
320 | if (!capable(CAP_SYS_ADMIN)) | ||
321 | return -EACCES; | ||
322 | if (setting->set) | ||
323 | return setting->set(drive, val); | ||
324 | if (!(setting->rw & SETTING_WRITE)) | ||
325 | return -EPERM; | ||
326 | if (val < setting->min || val > setting->max) | ||
327 | return -EINVAL; | ||
328 | if (ide_spin_wait_hwgroup(drive)) | ||
329 | return -EBUSY; | ||
330 | switch (setting->data_type) { | ||
331 | case TYPE_BYTE: | ||
332 | *((u8 *) setting->data) = val; | ||
333 | break; | ||
334 | case TYPE_SHORT: | ||
335 | *((u16 *) setting->data) = val; | ||
336 | break; | ||
337 | case TYPE_INT: | ||
338 | *((u32 *) setting->data) = val; | ||
339 | break; | ||
340 | } | ||
341 | spin_unlock_irq(&ide_lock); | ||
342 | return 0; | ||
343 | } | ||
344 | |||
345 | static int set_xfer_rate (ide_drive_t *drive, int arg) | ||
346 | { | ||
347 | int err; | ||
348 | |||
349 | if (arg < 0 || arg > 70) | ||
350 | return -EINVAL; | ||
351 | |||
352 | err = ide_wait_cmd(drive, | ||
353 | WIN_SETFEATURES, (u8) arg, | ||
354 | SETFEATURES_XFER, 0, NULL); | ||
355 | |||
356 | if (!err && arg) { | ||
357 | ide_set_xfer_rate(drive, (u8) arg); | ||
358 | ide_driveid_update(drive); | ||
359 | } | ||
360 | return err; | ||
361 | } | ||
362 | |||
363 | /** | ||
364 | * ide_add_generic_settings - generic ide settings | ||
365 | * @drive: drive being configured | ||
366 | * | ||
367 | * Add the generic parts of the system settings to the /proc files. | ||
368 | * The caller must not be holding the ide_setting_sem. | ||
369 | */ | ||
370 | |||
371 | void ide_add_generic_settings (ide_drive_t *drive) | ||
372 | { | ||
373 | /* | ||
374 | * drive setting name read/write access data type min max mul_factor div_factor data pointer set function | ||
375 | */ | ||
376 | __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); | ||
377 | __ide_add_setting(drive, "keepsettings", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->keep_settings, NULL, 0); | ||
378 | __ide_add_setting(drive, "nice1", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->nice1, NULL, 0); | ||
379 | __ide_add_setting(drive, "pio_mode", SETTING_WRITE, TYPE_BYTE, 0, 255, 1, 1, NULL, set_pio_mode, 0); | ||
380 | __ide_add_setting(drive, "unmaskirq", drive->no_unmask ? SETTING_READ : SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->unmask, NULL, 0); | ||
381 | __ide_add_setting(drive, "using_dma", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->using_dma, set_using_dma, 0); | ||
382 | __ide_add_setting(drive, "init_speed", SETTING_RW, TYPE_BYTE, 0, 70, 1, 1, &drive->init_speed, NULL, 0); | ||
383 | __ide_add_setting(drive, "current_speed", SETTING_RW, TYPE_BYTE, 0, 70, 1, 1, &drive->current_speed, set_xfer_rate, 0); | ||
384 | __ide_add_setting(drive, "number", SETTING_RW, TYPE_BYTE, 0, 3, 1, 1, &drive->dn, NULL, 0); | ||
385 | } | ||
386 | |||
124 | static void proc_ide_settings_warn(void) | 387 | static void proc_ide_settings_warn(void) |
125 | { | 388 | { |
126 | static int warned = 0; | 389 | static int warned = 0; |
@@ -399,7 +662,7 @@ static ide_proc_entry_t generic_drive_entries[] = { | |||
399 | { NULL, 0, NULL, NULL } | 662 | { NULL, 0, NULL, NULL } |
400 | }; | 663 | }; |
401 | 664 | ||
402 | void ide_add_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p, void *data) | 665 | static void ide_add_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p, void *data) |
403 | { | 666 | { |
404 | struct proc_dir_entry *ent; | 667 | struct proc_dir_entry *ent; |
405 | 668 | ||
@@ -415,7 +678,7 @@ void ide_add_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p, void | |||
415 | } | 678 | } |
416 | } | 679 | } |
417 | 680 | ||
418 | void ide_remove_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p) | 681 | static void ide_remove_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p) |
419 | { | 682 | { |
420 | if (!dir || !p) | 683 | if (!dir || !p) |
421 | return; | 684 | return; |
@@ -425,6 +688,51 @@ void ide_remove_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p) | |||
425 | } | 688 | } |
426 | } | 689 | } |
427 | 690 | ||
691 | void ide_proc_register_driver(ide_drive_t *drive, ide_driver_t *driver) | ||
692 | { | ||
693 | ide_add_proc_entries(drive->proc, driver->proc, drive); | ||
694 | } | ||
695 | |||
696 | EXPORT_SYMBOL(ide_proc_register_driver); | ||
697 | |||
698 | /** | ||
699 | * ide_proc_unregister_driver - remove driver specific data | ||
700 | * @drive: drive | ||
701 | * @driver: driver | ||
702 | * | ||
703 | * Clean up the driver specific /proc files and IDE settings | ||
704 | * for a given drive. | ||
705 | * | ||
706 | * Takes ide_setting_sem and ide_lock. | ||
707 | * Caller must hold none of the locks. | ||
708 | */ | ||
709 | |||
710 | void ide_proc_unregister_driver(ide_drive_t *drive, ide_driver_t *driver) | ||
711 | { | ||
712 | unsigned long flags; | ||
713 | |||
714 | ide_remove_proc_entries(drive->proc, driver->proc); | ||
715 | |||
716 | down(&ide_setting_sem); | ||
717 | spin_lock_irqsave(&ide_lock, flags); | ||
718 | /* | ||
719 | * ide_setting_sem protects the settings list | ||
720 | * ide_lock protects the use of settings | ||
721 | * | ||
722 | * so we need to hold both, ide_settings_sem because we want to | ||
723 | * modify the settings list, and ide_lock because we cannot take | ||
724 | * a setting out that is being used. | ||
725 | * | ||
726 | * OTOH both ide_{read,write}_setting are only ever used under | ||
727 | * ide_setting_sem. | ||
728 | */ | ||
729 | auto_remove_settings(drive); | ||
730 | spin_unlock_irqrestore(&ide_lock, flags); | ||
731 | up(&ide_setting_sem); | ||
732 | } | ||
733 | |||
734 | EXPORT_SYMBOL(ide_proc_unregister_driver); | ||
735 | |||
428 | static void create_proc_ide_drives(ide_hwif_t *hwif) | 736 | static void create_proc_ide_drives(ide_hwif_t *hwif) |
429 | { | 737 | { |
430 | int d; | 738 | int d; |
@@ -477,26 +785,24 @@ static ide_proc_entry_t hwif_entries[] = { | |||
477 | { NULL, 0, NULL, NULL } | 785 | { NULL, 0, NULL, NULL } |
478 | }; | 786 | }; |
479 | 787 | ||
480 | void create_proc_ide_interfaces(void) | 788 | void ide_proc_register_port(ide_hwif_t *hwif) |
481 | { | 789 | { |
482 | int h; | 790 | if (!hwif->present) |
791 | return; | ||
483 | 792 | ||
484 | for (h = 0; h < MAX_HWIFS; h++) { | 793 | if (!hwif->proc) { |
485 | ide_hwif_t *hwif = &ide_hwifs[h]; | 794 | hwif->proc = proc_mkdir(hwif->name, proc_ide_root); |
486 | 795 | ||
487 | if (!hwif->present) | 796 | if (!hwif->proc) |
488 | continue; | 797 | return; |
489 | if (!hwif->proc) { | 798 | |
490 | hwif->proc = proc_mkdir(hwif->name, proc_ide_root); | 799 | ide_add_proc_entries(hwif->proc, hwif_entries, hwif); |
491 | if (!hwif->proc) | ||
492 | return; | ||
493 | ide_add_proc_entries(hwif->proc, hwif_entries, hwif); | ||
494 | } | ||
495 | create_proc_ide_drives(hwif); | ||
496 | } | 800 | } |
801 | |||
802 | create_proc_ide_drives(hwif); | ||
497 | } | 803 | } |
498 | 804 | ||
499 | EXPORT_SYMBOL(create_proc_ide_interfaces); | 805 | EXPORT_SYMBOL_GPL(ide_proc_register_port); |
500 | 806 | ||
501 | #ifdef CONFIG_BLK_DEV_IDEPCI | 807 | #ifdef CONFIG_BLK_DEV_IDEPCI |
502 | void ide_pci_create_host_proc(const char *name, get_info_t *get_info) | 808 | void ide_pci_create_host_proc(const char *name, get_info_t *get_info) |
@@ -507,7 +813,7 @@ void ide_pci_create_host_proc(const char *name, get_info_t *get_info) | |||
507 | EXPORT_SYMBOL_GPL(ide_pci_create_host_proc); | 813 | EXPORT_SYMBOL_GPL(ide_pci_create_host_proc); |
508 | #endif | 814 | #endif |
509 | 815 | ||
510 | void destroy_proc_ide_interface(ide_hwif_t *hwif) | 816 | void ide_proc_unregister_port(ide_hwif_t *hwif) |
511 | { | 817 | { |
512 | if (hwif->proc) { | 818 | if (hwif->proc) { |
513 | destroy_proc_ide_drives(hwif); | 819 | destroy_proc_ide_drives(hwif); |
@@ -554,11 +860,11 @@ void proc_ide_create(void) | |||
554 | { | 860 | { |
555 | struct proc_dir_entry *entry; | 861 | struct proc_dir_entry *entry; |
556 | 862 | ||
863 | proc_ide_root = proc_mkdir("ide", NULL); | ||
864 | |||
557 | if (!proc_ide_root) | 865 | if (!proc_ide_root) |
558 | return; | 866 | return; |
559 | 867 | ||
560 | create_proc_ide_interfaces(); | ||
561 | |||
562 | entry = create_proc_entry("drivers", 0, proc_ide_root); | 868 | entry = create_proc_entry("drivers", 0, proc_ide_root); |
563 | if (entry) | 869 | if (entry) |
564 | entry->proc_fops = &ide_drivers_operations; | 870 | entry->proc_fops = &ide_drivers_operations; |