linux/drivers/ide/ide-proc.c
<<
>>
Prefs
   1/*
   2 *  Copyright (C) 1997-1998     Mark Lord
   3 *  Copyright (C) 2003          Red Hat
   4 *
   5 *  Some code was moved here from ide.c, see it for original copyrights.
   6 */
   7
   8/*
   9 * This is the /proc/ide/ filesystem implementation.
  10 *
  11 * Drive/Driver settings can be retrieved by reading the drive's
  12 * "settings" files.  e.g.    "cat /proc/ide0/hda/settings"
  13 * To write a new value "val" into a specific setting "name", use:
  14 *   echo "name:val" >/proc/ide/ide0/hda/settings
  15 */
  16
  17#include <linux/module.h>
  18
  19#include <asm/uaccess.h>
  20#include <linux/errno.h>
  21#include <linux/proc_fs.h>
  22#include <linux/stat.h>
  23#include <linux/mm.h>
  24#include <linux/pci.h>
  25#include <linux/ctype.h>
  26#include <linux/ide.h>
  27#include <linux/seq_file.h>
  28
  29#include <asm/io.h>
  30
  31static struct proc_dir_entry *proc_ide_root;
  32
  33static int ide_imodel_proc_show(struct seq_file *m, void *v)
  34{
  35        ide_hwif_t      *hwif = (ide_hwif_t *) m->private;
  36        const char      *name;
  37
  38        switch (hwif->chipset) {
  39        case ide_generic:       name = "generic";       break;
  40        case ide_pci:           name = "pci";           break;
  41        case ide_cmd640:        name = "cmd640";        break;
  42        case ide_dtc2278:       name = "dtc2278";       break;
  43        case ide_ali14xx:       name = "ali14xx";       break;
  44        case ide_qd65xx:        name = "qd65xx";        break;
  45        case ide_umc8672:       name = "umc8672";       break;
  46        case ide_ht6560b:       name = "ht6560b";       break;
  47        case ide_4drives:       name = "4drives";       break;
  48        case ide_pmac:          name = "mac-io";        break;
  49        case ide_au1xxx:        name = "au1xxx";        break;
  50        case ide_palm3710:      name = "palm3710";      break;
  51        case ide_acorn:         name = "acorn";         break;
  52        default:                name = "(unknown)";     break;
  53        }
  54        seq_printf(m, "%s\n", name);
  55        return 0;
  56}
  57
  58static int ide_imodel_proc_open(struct inode *inode, struct file *file)
  59{
  60        return single_open(file, ide_imodel_proc_show, PDE(inode)->data);
  61}
  62
  63static const struct file_operations ide_imodel_proc_fops = {
  64        .owner          = THIS_MODULE,
  65        .open           = ide_imodel_proc_open,
  66        .read           = seq_read,
  67        .llseek         = seq_lseek,
  68        .release        = single_release,
  69};
  70
  71static int ide_mate_proc_show(struct seq_file *m, void *v)
  72{
  73        ide_hwif_t      *hwif = (ide_hwif_t *) m->private;
  74
  75        if (hwif && hwif->mate)
  76                seq_printf(m, "%s\n", hwif->mate->name);
  77        else
  78                seq_printf(m, "(none)\n");
  79        return 0;
  80}
  81
  82static int ide_mate_proc_open(struct inode *inode, struct file *file)
  83{
  84        return single_open(file, ide_mate_proc_show, PDE(inode)->data);
  85}
  86
  87static const struct file_operations ide_mate_proc_fops = {
  88        .owner          = THIS_MODULE,
  89        .open           = ide_mate_proc_open,
  90        .read           = seq_read,
  91        .llseek         = seq_lseek,
  92        .release        = single_release,
  93};
  94
  95static int ide_channel_proc_show(struct seq_file *m, void *v)
  96{
  97        ide_hwif_t      *hwif = (ide_hwif_t *) m->private;
  98
  99        seq_printf(m, "%c\n", hwif->channel ? '1' : '0');
 100        return 0;
 101}
 102
 103static int ide_channel_proc_open(struct inode *inode, struct file *file)
 104{
 105        return single_open(file, ide_channel_proc_show, PDE(inode)->data);
 106}
 107
 108static const struct file_operations ide_channel_proc_fops = {
 109        .owner          = THIS_MODULE,
 110        .open           = ide_channel_proc_open,
 111        .read           = seq_read,
 112        .llseek         = seq_lseek,
 113        .release        = single_release,
 114};
 115
 116static int ide_identify_proc_show(struct seq_file *m, void *v)
 117{
 118        ide_drive_t *drive = (ide_drive_t *)m->private;
 119        u8 *buf;
 120
 121        if (!drive) {
 122                seq_putc(m, '\n');
 123                return 0;
 124        }
 125
 126        buf = kmalloc(SECTOR_SIZE, GFP_KERNEL);
 127        if (!buf)
 128                return -ENOMEM;
 129        if (taskfile_lib_get_identify(drive, buf) == 0) {
 130                __le16 *val = (__le16 *)buf;
 131                int i;
 132
 133                for (i = 0; i < SECTOR_SIZE / 2; i++) {
 134                        seq_printf(m, "%04x%c", le16_to_cpu(val[i]),
 135                                        (i % 8) == 7 ? '\n' : ' ');
 136                }
 137        } else
 138                seq_putc(m, buf[0]);
 139        kfree(buf);
 140        return 0;
 141}
 142
 143static int ide_identify_proc_open(struct inode *inode, struct file *file)
 144{
 145        return single_open(file, ide_identify_proc_show, PDE(inode)->data);
 146}
 147
 148static const struct file_operations ide_identify_proc_fops = {
 149        .owner          = THIS_MODULE,
 150        .open           = ide_identify_proc_open,
 151        .read           = seq_read,
 152        .llseek         = seq_lseek,
 153        .release        = single_release,
 154};
 155
 156/**
 157 *      ide_find_setting        -       find a specific setting
 158 *      @st: setting table pointer
 159 *      @name: setting name
 160 *
 161 *      Scan's the setting table for a matching entry and returns
 162 *      this or NULL if no entry is found. The caller must hold the
 163 *      setting semaphore
 164 */
 165
 166static
 167const struct ide_proc_devset *ide_find_setting(const struct ide_proc_devset *st,
 168                                               char *name)
 169{
 170        while (st->name) {
 171                if (strcmp(st->name, name) == 0)
 172                        break;
 173                st++;
 174        }
 175        return st->name ? st : NULL;
 176}
 177
 178/**
 179 *      ide_read_setting        -       read an IDE setting
 180 *      @drive: drive to read from
 181 *      @setting: drive setting
 182 *
 183 *      Read a drive setting and return the value. The caller
 184 *      must hold the ide_setting_mtx when making this call.
 185 *
 186 *      BUGS: the data return and error are the same return value
 187 *      so an error -EINVAL and true return of the same value cannot
 188 *      be told apart
 189 */
 190
 191static int ide_read_setting(ide_drive_t *drive,
 192                            const struct ide_proc_devset *setting)
 193{
 194        const struct ide_devset *ds = setting->setting;
 195        int val = -EINVAL;
 196
 197        if (ds->get)
 198                val = ds->get(drive);
 199
 200        return val;
 201}
 202
 203/**
 204 *      ide_write_setting       -       read an IDE setting
 205 *      @drive: drive to read from
 206 *      @setting: drive setting
 207 *      @val: value
 208 *
 209 *      Write a drive setting if it is possible. The caller
 210 *      must hold the ide_setting_mtx when making this call.
 211 *
 212 *      BUGS: the data return and error are the same return value
 213 *      so an error -EINVAL and true return of the same value cannot
 214 *      be told apart
 215 *
 216 *      FIXME:  This should be changed to enqueue a special request
 217 *      to the driver to change settings, and then wait on a sema for completion.
 218 *      The current scheme of polling is kludgy, though safe enough.
 219 */
 220
 221static int ide_write_setting(ide_drive_t *drive,
 222                             const struct ide_proc_devset *setting, int val)
 223{
 224        const struct ide_devset *ds = setting->setting;
 225
 226        if (!capable(CAP_SYS_ADMIN))
 227                return -EACCES;
 228        if (!ds->set)
 229                return -EPERM;
 230        if ((ds->flags & DS_SYNC)
 231            && (val < setting->min || val > setting->max))
 232                return -EINVAL;
 233        return ide_devset_execute(drive, ds, val);
 234}
 235
 236ide_devset_get(xfer_rate, current_speed);
 237
 238static int set_xfer_rate (ide_drive_t *drive, int arg)
 239{
 240        struct ide_cmd cmd;
 241
 242        if (arg < XFER_PIO_0 || arg > XFER_UDMA_6)
 243                return -EINVAL;
 244
 245        memset(&cmd, 0, sizeof(cmd));
 246        cmd.tf.command = ATA_CMD_SET_FEATURES;
 247        cmd.tf.feature = SETFEATURES_XFER;
 248        cmd.tf.nsect   = (u8)arg;
 249        cmd.valid.out.tf = IDE_VALID_FEATURE | IDE_VALID_NSECT;
 250        cmd.valid.in.tf  = IDE_VALID_NSECT;
 251        cmd.tf_flags   = IDE_TFLAG_SET_XFER;
 252
 253        return ide_no_data_taskfile(drive, &cmd);
 254}
 255
 256ide_devset_rw(current_speed, xfer_rate);
 257ide_devset_rw_field(init_speed, init_speed);
 258ide_devset_rw_flag(nice1, IDE_DFLAG_NICE1);
 259ide_devset_rw_field(number, dn);
 260
 261static const struct ide_proc_devset ide_generic_settings[] = {
 262        IDE_PROC_DEVSET(current_speed, 0, 70),
 263        IDE_PROC_DEVSET(init_speed, 0, 70),
 264        IDE_PROC_DEVSET(io_32bit,  0, 1 + (SUPPORT_VLB_SYNC << 1)),
 265        IDE_PROC_DEVSET(keepsettings, 0, 1),
 266        IDE_PROC_DEVSET(nice1, 0, 1),
 267        IDE_PROC_DEVSET(number, 0, 3),
 268        IDE_PROC_DEVSET(pio_mode, 0, 255),
 269        IDE_PROC_DEVSET(unmaskirq, 0, 1),
 270        IDE_PROC_DEVSET(using_dma, 0, 1),
 271        { NULL },
 272};
 273
 274static void proc_ide_settings_warn(void)
 275{
 276        printk_once(KERN_WARNING "Warning: /proc/ide/hd?/settings interface is "
 277                            "obsolete, and will be removed soon!\n");
 278}
 279
 280static int ide_settings_proc_show(struct seq_file *m, void *v)
 281{
 282        const struct ide_proc_devset *setting, *g, *d;
 283        const struct ide_devset *ds;
 284        ide_drive_t     *drive = (ide_drive_t *) m->private;
 285        int             rc, mul_factor, div_factor;
 286
 287        proc_ide_settings_warn();
 288
 289        mutex_lock(&ide_setting_mtx);
 290        g = ide_generic_settings;
 291        d = drive->settings;
 292        seq_printf(m, "name\t\t\tvalue\t\tmin\t\tmax\t\tmode\n");
 293        seq_printf(m, "----\t\t\t-----\t\t---\t\t---\t\t----\n");
 294        while (g->name || (d && d->name)) {
 295                /* read settings in the alphabetical order */
 296                if (g->name && d && d->name) {
 297                        if (strcmp(d->name, g->name) < 0)
 298                                setting = d++;
 299                        else
 300                                setting = g++;
 301                } else if (d && d->name) {
 302                        setting = d++;
 303                } else
 304                        setting = g++;
 305                mul_factor = setting->mulf ? setting->mulf(drive) : 1;
 306                div_factor = setting->divf ? setting->divf(drive) : 1;
 307                seq_printf(m, "%-24s", setting->name);
 308                rc = ide_read_setting(drive, setting);
 309                if (rc >= 0)
 310                        seq_printf(m, "%-16d", rc * mul_factor / div_factor);
 311                else
 312                        seq_printf(m, "%-16s", "write-only");
 313                seq_printf(m, "%-16d%-16d", (setting->min * mul_factor + div_factor - 1) / div_factor, setting->max * mul_factor / div_factor);
 314                ds = setting->setting;
 315                if (ds->get)
 316                        seq_printf(m, "r");
 317                if (ds->set)
 318                        seq_printf(m, "w");
 319                seq_printf(m, "\n");
 320        }
 321        mutex_unlock(&ide_setting_mtx);
 322        return 0;
 323}
 324
 325static int ide_settings_proc_open(struct inode *inode, struct file *file)
 326{
 327        return single_open(file, ide_settings_proc_show, PDE(inode)->data);
 328}
 329
 330#define MAX_LEN 30
 331
 332static ssize_t ide_settings_proc_write(struct file *file, const char __user *buffer,
 333                                       size_t count, loff_t *pos)
 334{
 335        ide_drive_t     *drive = (ide_drive_t *) PDE(file->f_path.dentry->d_inode)->data;
 336        char            name[MAX_LEN + 1];
 337        int             for_real = 0, mul_factor, div_factor;
 338        unsigned long   n;
 339
 340        const struct ide_proc_devset *setting;
 341        char *buf, *s;
 342
 343        if (!capable(CAP_SYS_ADMIN))
 344                return -EACCES;
 345
 346        proc_ide_settings_warn();
 347
 348        if (count >= PAGE_SIZE)
 349                return -EINVAL;
 350
 351        s = buf = (char *)__get_free_page(GFP_USER);
 352        if (!buf)
 353                return -ENOMEM;
 354
 355        if (copy_from_user(buf, buffer, count)) {
 356                free_page((unsigned long)buf);
 357                return -EFAULT;
 358        }
 359
 360        buf[count] = '\0';
 361
 362        /*
 363         * Skip over leading whitespace
 364         */
 365        while (count && isspace(*s)) {
 366                --count;
 367                ++s;
 368        }
 369        /*
 370         * Do one full pass to verify all parameters,
 371         * then do another to actually write the new settings.
 372         */
 373        do {
 374                char *p = s;
 375                n = count;
 376                while (n > 0) {
 377                        unsigned val;
 378                        char *q = p;
 379
 380                        while (n > 0 && *p != ':') {
 381                                --n;
 382                                p++;
 383                        }
 384                        if (*p != ':')
 385                                goto parse_error;
 386                        if (p - q > MAX_LEN)
 387                                goto parse_error;
 388                        memcpy(name, q, p - q);
 389                        name[p - q] = 0;
 390
 391                        if (n > 0) {
 392                                --n;
 393                                p++;
 394                        } else
 395                                goto parse_error;
 396
 397                        val = simple_strtoul(p, &q, 10);
 398                        n -= q - p;
 399                        p = q;
 400                        if (n > 0 && !isspace(*p))
 401                                goto parse_error;
 402                        while (n > 0 && isspace(*p)) {
 403                                --n;
 404                                ++p;
 405                        }
 406
 407                        mutex_lock(&ide_setting_mtx);
 408                        /* generic settings first, then driver specific ones */
 409                        setting = ide_find_setting(ide_generic_settings, name);
 410                        if (!setting) {
 411                                if (drive->settings)
 412                                        setting = ide_find_setting(drive->settings, name);
 413                                if (!setting) {
 414                                        mutex_unlock(&ide_setting_mtx);
 415                                        goto parse_error;
 416                                }
 417                        }
 418                        if (for_real) {
 419                                mul_factor = setting->mulf ? setting->mulf(drive) : 1;
 420                                div_factor = setting->divf ? setting->divf(drive) : 1;
 421                                ide_write_setting(drive, setting, val * div_factor / mul_factor);
 422                        }
 423                        mutex_unlock(&ide_setting_mtx);
 424                }
 425        } while (!for_real++);
 426        free_page((unsigned long)buf);
 427        return count;
 428parse_error:
 429        free_page((unsigned long)buf);
 430        printk("%s(): parse error\n", __func__);
 431        return -EINVAL;
 432}
 433
 434static const struct file_operations ide_settings_proc_fops = {
 435        .owner          = THIS_MODULE,
 436        .open           = ide_settings_proc_open,
 437        .read           = seq_read,
 438        .llseek         = seq_lseek,
 439        .release        = single_release,
 440        .write          = ide_settings_proc_write,
 441};
 442
 443static int ide_capacity_proc_show(struct seq_file *m, void *v)
 444{
 445        seq_printf(m, "%llu\n", (long long)0x7fffffff);
 446        return 0;
 447}
 448
 449static int ide_capacity_proc_open(struct inode *inode, struct file *file)
 450{
 451        return single_open(file, ide_capacity_proc_show, NULL);
 452}
 453
 454const struct file_operations ide_capacity_proc_fops = {
 455        .owner          = THIS_MODULE,
 456        .open           = ide_capacity_proc_open,
 457        .read           = seq_read,
 458        .llseek         = seq_lseek,
 459        .release        = single_release,
 460};
 461EXPORT_SYMBOL_GPL(ide_capacity_proc_fops);
 462
 463static int ide_geometry_proc_show(struct seq_file *m, void *v)
 464{
 465        ide_drive_t     *drive = (ide_drive_t *) m->private;
 466
 467        seq_printf(m, "physical     %d/%d/%d\n",
 468                        drive->cyl, drive->head, drive->sect);
 469        seq_printf(m, "logical      %d/%d/%d\n",
 470                        drive->bios_cyl, drive->bios_head, drive->bios_sect);
 471        return 0;
 472}
 473
 474static int ide_geometry_proc_open(struct inode *inode, struct file *file)
 475{
 476        return single_open(file, ide_geometry_proc_show, PDE(inode)->data);
 477}
 478
 479const struct file_operations ide_geometry_proc_fops = {
 480        .owner          = THIS_MODULE,
 481        .open           = ide_geometry_proc_open,
 482        .read           = seq_read,
 483        .llseek         = seq_lseek,
 484        .release        = single_release,
 485};
 486EXPORT_SYMBOL(ide_geometry_proc_fops);
 487
 488static int ide_dmodel_proc_show(struct seq_file *seq, void *v)
 489{
 490        ide_drive_t     *drive = (ide_drive_t *) seq->private;
 491        char            *m = (char *)&drive->id[ATA_ID_PROD];
 492
 493        seq_printf(seq, "%.40s\n", m[0] ? m : "(none)");
 494        return 0;
 495}
 496
 497static int ide_dmodel_proc_open(struct inode *inode, struct file *file)
 498{
 499        return single_open(file, ide_dmodel_proc_show, PDE(inode)->data);
 500}
 501
 502static const struct file_operations ide_dmodel_proc_fops = {
 503        .owner          = THIS_MODULE,
 504        .open           = ide_dmodel_proc_open,
 505        .read           = seq_read,
 506        .llseek         = seq_lseek,
 507        .release        = single_release,
 508};
 509
 510static int ide_driver_proc_show(struct seq_file *m, void *v)
 511{
 512        ide_drive_t             *drive = (ide_drive_t *)m->private;
 513        struct device           *dev = &drive->gendev;
 514        struct ide_driver       *ide_drv;
 515
 516        if (dev->driver) {
 517                ide_drv = to_ide_driver(dev->driver);
 518                seq_printf(m, "%s version %s\n",
 519                                dev->driver->name, ide_drv->version);
 520        } else
 521                seq_printf(m, "ide-default version 0.9.newide\n");
 522        return 0;
 523}
 524
 525static int ide_driver_proc_open(struct inode *inode, struct file *file)
 526{
 527        return single_open(file, ide_driver_proc_show, PDE(inode)->data);
 528}
 529
 530static int ide_replace_subdriver(ide_drive_t *drive, const char *driver)
 531{
 532        struct device *dev = &drive->gendev;
 533        int ret = 1;
 534        int err;
 535
 536        device_release_driver(dev);
 537        /* FIXME: device can still be in use by previous driver */
 538        strlcpy(drive->driver_req, driver, sizeof(drive->driver_req));
 539        err = device_attach(dev);
 540        if (err < 0)
 541                printk(KERN_WARNING "IDE: %s: device_attach error: %d\n",
 542                        __func__, err);
 543        drive->driver_req[0] = 0;
 544        if (dev->driver == NULL) {
 545                err = device_attach(dev);
 546                if (err < 0)
 547                        printk(KERN_WARNING
 548                                "IDE: %s: device_attach(2) error: %d\n",
 549                                __func__, err);
 550        }
 551        if (dev->driver && !strcmp(dev->driver->name, driver))
 552                ret = 0;
 553
 554        return ret;
 555}
 556
 557static ssize_t ide_driver_proc_write(struct file *file, const char __user *buffer,
 558                                     size_t count, loff_t *pos)
 559{
 560        ide_drive_t     *drive = (ide_drive_t *) PDE(file->f_path.dentry->d_inode)->data;
 561        char name[32];
 562
 563        if (!capable(CAP_SYS_ADMIN))
 564                return -EACCES;
 565        if (count > 31)
 566                count = 31;
 567        if (copy_from_user(name, buffer, count))
 568                return -EFAULT;
 569        name[count] = '\0';
 570        if (ide_replace_subdriver(drive, name))
 571                return -EINVAL;
 572        return count;
 573}
 574
 575static const struct file_operations ide_driver_proc_fops = {
 576        .owner          = THIS_MODULE,
 577        .open           = ide_driver_proc_open,
 578        .read           = seq_read,
 579        .llseek         = seq_lseek,
 580        .release        = single_release,
 581        .write          = ide_driver_proc_write,
 582};
 583
 584static int ide_media_proc_show(struct seq_file *m, void *v)
 585{
 586        ide_drive_t     *drive = (ide_drive_t *) m->private;
 587        const char      *media;
 588
 589        switch (drive->media) {
 590        case ide_disk:          media = "disk\n";       break;
 591        case ide_cdrom:         media = "cdrom\n";      break;
 592        case ide_tape:          media = "tape\n";       break;
 593        case ide_floppy:        media = "floppy\n";     break;
 594        case ide_optical:       media = "optical\n";    break;
 595        default:                media = "UNKNOWN\n";    break;
 596        }
 597        seq_puts(m, media);
 598        return 0;
 599}
 600
 601static int ide_media_proc_open(struct inode *inode, struct file *file)
 602{
 603        return single_open(file, ide_media_proc_show, PDE(inode)->data);
 604}
 605
 606static const struct file_operations ide_media_proc_fops = {
 607        .owner          = THIS_MODULE,
 608        .open           = ide_media_proc_open,
 609        .read           = seq_read,
 610        .llseek         = seq_lseek,
 611        .release        = single_release,
 612};
 613
 614static ide_proc_entry_t generic_drive_entries[] = {
 615        { "driver",     S_IFREG|S_IRUGO,         &ide_driver_proc_fops  },
 616        { "identify",   S_IFREG|S_IRUSR,         &ide_identify_proc_fops},
 617        { "media",      S_IFREG|S_IRUGO,         &ide_media_proc_fops   },
 618        { "model",      S_IFREG|S_IRUGO,         &ide_dmodel_proc_fops  },
 619        { "settings",   S_IFREG|S_IRUSR|S_IWUSR, &ide_settings_proc_fops},
 620        {}
 621};
 622
 623static void ide_add_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p, void *data)
 624{
 625        struct proc_dir_entry *ent;
 626
 627        if (!dir || !p)
 628                return;
 629        while (p->name != NULL) {
 630                ent = proc_create_data(p->name, p->mode, dir, p->proc_fops, data);
 631                if (!ent) return;
 632                p++;
 633        }
 634}
 635
 636static void ide_remove_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p)
 637{
 638        if (!dir || !p)
 639                return;
 640        while (p->name != NULL) {
 641                remove_proc_entry(p->name, dir);
 642                p++;
 643        }
 644}
 645
 646void ide_proc_register_driver(ide_drive_t *drive, struct ide_driver *driver)
 647{
 648        mutex_lock(&ide_setting_mtx);
 649        drive->settings = driver->proc_devsets(drive);
 650        mutex_unlock(&ide_setting_mtx);
 651
 652        ide_add_proc_entries(drive->proc, driver->proc_entries(drive), drive);
 653}
 654
 655EXPORT_SYMBOL(ide_proc_register_driver);
 656
 657/**
 658 *      ide_proc_unregister_driver      -       remove driver specific data
 659 *      @drive: drive
 660 *      @driver: driver
 661 *
 662 *      Clean up the driver specific /proc files and IDE settings
 663 *      for a given drive.
 664 *
 665 *      Takes ide_setting_mtx.
 666 */
 667
 668void ide_proc_unregister_driver(ide_drive_t *drive, struct ide_driver *driver)
 669{
 670        ide_remove_proc_entries(drive->proc, driver->proc_entries(drive));
 671
 672        mutex_lock(&ide_setting_mtx);
 673        /*
 674         * ide_setting_mtx protects both the settings list and the use
 675         * of settings (we cannot take a setting out that is being used).
 676         */
 677        drive->settings = NULL;
 678        mutex_unlock(&ide_setting_mtx);
 679}
 680EXPORT_SYMBOL(ide_proc_unregister_driver);
 681
 682void ide_proc_port_register_devices(ide_hwif_t *hwif)
 683{
 684        struct proc_dir_entry *ent;
 685        struct proc_dir_entry *parent = hwif->proc;
 686        ide_drive_t *drive;
 687        char name[64];
 688        int i;
 689
 690        ide_port_for_each_dev(i, drive, hwif) {
 691                if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0)
 692                        continue;
 693
 694                drive->proc = proc_mkdir(drive->name, parent);
 695                if (drive->proc)
 696                        ide_add_proc_entries(drive->proc, generic_drive_entries, drive);
 697                sprintf(name, "ide%d/%s", (drive->name[2]-'a')/2, drive->name);
 698                ent = proc_symlink(drive->name, proc_ide_root, name);
 699                if (!ent) return;
 700        }
 701}
 702
 703void ide_proc_unregister_device(ide_drive_t *drive)
 704{
 705        if (drive->proc) {
 706                ide_remove_proc_entries(drive->proc, generic_drive_entries);
 707                remove_proc_entry(drive->name, proc_ide_root);
 708                remove_proc_entry(drive->name, drive->hwif->proc);
 709                drive->proc = NULL;
 710        }
 711}
 712
 713static ide_proc_entry_t hwif_entries[] = {
 714        { "channel",    S_IFREG|S_IRUGO,        &ide_channel_proc_fops  },
 715        { "mate",       S_IFREG|S_IRUGO,        &ide_mate_proc_fops     },
 716        { "model",      S_IFREG|S_IRUGO,        &ide_imodel_proc_fops   },
 717        {}
 718};
 719
 720void ide_proc_register_port(ide_hwif_t *hwif)
 721{
 722        if (!hwif->proc) {
 723                hwif->proc = proc_mkdir(hwif->name, proc_ide_root);
 724
 725                if (!hwif->proc)
 726                        return;
 727
 728                ide_add_proc_entries(hwif->proc, hwif_entries, hwif);
 729        }
 730}
 731
 732void ide_proc_unregister_port(ide_hwif_t *hwif)
 733{
 734        if (hwif->proc) {
 735                ide_remove_proc_entries(hwif->proc, hwif_entries);
 736                remove_proc_entry(hwif->name, proc_ide_root);
 737                hwif->proc = NULL;
 738        }
 739}
 740
 741static int proc_print_driver(struct device_driver *drv, void *data)
 742{
 743        struct ide_driver *ide_drv = to_ide_driver(drv);
 744        struct seq_file *s = data;
 745
 746        seq_printf(s, "%s version %s\n", drv->name, ide_drv->version);
 747
 748        return 0;
 749}
 750
 751static int ide_drivers_show(struct seq_file *s, void *p)
 752{
 753        int err;
 754
 755        err = bus_for_each_drv(&ide_bus_type, NULL, s, proc_print_driver);
 756        if (err < 0)
 757                printk(KERN_WARNING "IDE: %s: bus_for_each_drv error: %d\n",
 758                        __func__, err);
 759        return 0;
 760}
 761
 762static int ide_drivers_open(struct inode *inode, struct file *file)
 763{
 764        return single_open(file, &ide_drivers_show, NULL);
 765}
 766
 767static const struct file_operations ide_drivers_operations = {
 768        .owner          = THIS_MODULE,
 769        .open           = ide_drivers_open,
 770        .read           = seq_read,
 771        .llseek         = seq_lseek,
 772        .release        = single_release,
 773};
 774
 775void proc_ide_create(void)
 776{
 777        proc_ide_root = proc_mkdir("ide", NULL);
 778
 779        if (!proc_ide_root)
 780                return;
 781
 782        proc_create("drivers", 0, proc_ide_root, &ide_drivers_operations);
 783}
 784
 785void proc_ide_destroy(void)
 786{
 787        remove_proc_entry("drivers", proc_ide_root);
 788        remove_proc_entry("ide", NULL);
 789}
 790
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.