linux/drivers/pnp/interface.c
<<
>>
Prefs
   1/*
   2 * interface.c - contains everything related to the user interface
   3 *
   4 * Some code, especially possible resource dumping is based on isapnp_proc.c (c) Jaroslav Kysela <perex@perex.cz>
   5 * Copyright 2002 Adam Belay <ambx1@neo.rr.com>
   6 * Copyright (C) 2008 Hewlett-Packard Development Company, L.P.
   7 *      Bjorn Helgaas <bjorn.helgaas@hp.com>
   8 */
   9
  10#include <linux/pnp.h>
  11#include <linux/string.h>
  12#include <linux/errno.h>
  13#include <linux/list.h>
  14#include <linux/types.h>
  15#include <linux/stat.h>
  16#include <linux/ctype.h>
  17#include <linux/slab.h>
  18#include <linux/mutex.h>
  19
  20#include <asm/uaccess.h>
  21
  22#include "base.h"
  23
  24struct pnp_info_buffer {
  25        char *buffer;           /* pointer to begin of buffer */
  26        char *curr;             /* current position in buffer */
  27        unsigned long size;     /* current size */
  28        unsigned long len;      /* total length of buffer */
  29        int stop;               /* stop flag */
  30        int error;              /* error code */
  31};
  32
  33typedef struct pnp_info_buffer pnp_info_buffer_t;
  34
  35static int pnp_printf(pnp_info_buffer_t * buffer, char *fmt, ...)
  36{
  37        va_list args;
  38        int res;
  39
  40        if (buffer->stop || buffer->error)
  41                return 0;
  42        va_start(args, fmt);
  43        res = vsnprintf(buffer->curr, buffer->len - buffer->size, fmt, args);
  44        va_end(args);
  45        if (buffer->size + res >= buffer->len) {
  46                buffer->stop = 1;
  47                return 0;
  48        }
  49        buffer->curr += res;
  50        buffer->size += res;
  51        return res;
  52}
  53
  54static void pnp_print_port(pnp_info_buffer_t * buffer, char *space,
  55                           struct pnp_port *port)
  56{
  57        pnp_printf(buffer, "%sport %#llx-%#llx, align %#llx, size %#llx, "
  58                   "%i-bit address decoding\n", space,
  59                   (unsigned long long) port->min,
  60                   (unsigned long long) port->max,
  61                   port->align ? ((unsigned long long) port->align - 1) : 0,
  62                   (unsigned long long) port->size,
  63                   port->flags & IORESOURCE_IO_16BIT_ADDR ? 16 : 10);
  64}
  65
  66static void pnp_print_irq(pnp_info_buffer_t * buffer, char *space,
  67                          struct pnp_irq *irq)
  68{
  69        int first = 1, i;
  70
  71        pnp_printf(buffer, "%sirq ", space);
  72        for (i = 0; i < PNP_IRQ_NR; i++)
  73                if (test_bit(i, irq->map.bits)) {
  74                        if (!first) {
  75                                pnp_printf(buffer, ",");
  76                        } else {
  77                                first = 0;
  78                        }
  79                        if (i == 2 || i == 9)
  80                                pnp_printf(buffer, "2/9");
  81                        else
  82                                pnp_printf(buffer, "%i", i);
  83                }
  84        if (bitmap_empty(irq->map.bits, PNP_IRQ_NR))
  85                pnp_printf(buffer, "<none>");
  86        if (irq->flags & IORESOURCE_IRQ_HIGHEDGE)
  87                pnp_printf(buffer, " High-Edge");
  88        if (irq->flags & IORESOURCE_IRQ_LOWEDGE)
  89                pnp_printf(buffer, " Low-Edge");
  90        if (irq->flags & IORESOURCE_IRQ_HIGHLEVEL)
  91                pnp_printf(buffer, " High-Level");
  92        if (irq->flags & IORESOURCE_IRQ_LOWLEVEL)
  93                pnp_printf(buffer, " Low-Level");
  94        if (irq->flags & IORESOURCE_IRQ_OPTIONAL)
  95                pnp_printf(buffer, " (optional)");
  96        pnp_printf(buffer, "\n");
  97}
  98
  99static void pnp_print_dma(pnp_info_buffer_t * buffer, char *space,
 100                          struct pnp_dma *dma)
 101{
 102        int first = 1, i;
 103        char *s;
 104
 105        pnp_printf(buffer, "%sdma ", space);
 106        for (i = 0; i < 8; i++)
 107                if (dma->map & (1 << i)) {
 108                        if (!first) {
 109                                pnp_printf(buffer, ",");
 110                        } else {
 111                                first = 0;
 112                        }
 113                        pnp_printf(buffer, "%i", i);
 114                }
 115        if (!dma->map)
 116                pnp_printf(buffer, "<none>");
 117        switch (dma->flags & IORESOURCE_DMA_TYPE_MASK) {
 118        case IORESOURCE_DMA_8BIT:
 119                s = "8-bit";
 120                break;
 121        case IORESOURCE_DMA_8AND16BIT:
 122                s = "8-bit&16-bit";
 123                break;
 124        default:
 125                s = "16-bit";
 126        }
 127        pnp_printf(buffer, " %s", s);
 128        if (dma->flags & IORESOURCE_DMA_MASTER)
 129                pnp_printf(buffer, " master");
 130        if (dma->flags & IORESOURCE_DMA_BYTE)
 131                pnp_printf(buffer, " byte-count");
 132        if (dma->flags & IORESOURCE_DMA_WORD)
 133                pnp_printf(buffer, " word-count");
 134        switch (dma->flags & IORESOURCE_DMA_SPEED_MASK) {
 135        case IORESOURCE_DMA_TYPEA:
 136                s = "type-A";
 137                break;
 138        case IORESOURCE_DMA_TYPEB:
 139                s = "type-B";
 140                break;
 141        case IORESOURCE_DMA_TYPEF:
 142                s = "type-F";
 143                break;
 144        default:
 145                s = "compatible";
 146                break;
 147        }
 148        pnp_printf(buffer, " %s\n", s);
 149}
 150
 151static void pnp_print_mem(pnp_info_buffer_t * buffer, char *space,
 152                          struct pnp_mem *mem)
 153{
 154        char *s;
 155
 156        pnp_printf(buffer, "%sMemory %#llx-%#llx, align %#llx, size %#llx",
 157                   space, (unsigned long long) mem->min,
 158                   (unsigned long long) mem->max,
 159                   (unsigned long long) mem->align,
 160                   (unsigned long long) mem->size);
 161        if (mem->flags & IORESOURCE_MEM_WRITEABLE)
 162                pnp_printf(buffer, ", writeable");
 163        if (mem->flags & IORESOURCE_MEM_CACHEABLE)
 164                pnp_printf(buffer, ", cacheable");
 165        if (mem->flags & IORESOURCE_MEM_RANGELENGTH)
 166                pnp_printf(buffer, ", range-length");
 167        if (mem->flags & IORESOURCE_MEM_SHADOWABLE)
 168                pnp_printf(buffer, ", shadowable");
 169        if (mem->flags & IORESOURCE_MEM_EXPANSIONROM)
 170                pnp_printf(buffer, ", expansion ROM");
 171        switch (mem->flags & IORESOURCE_MEM_TYPE_MASK) {
 172        case IORESOURCE_MEM_8BIT:
 173                s = "8-bit";
 174                break;
 175        case IORESOURCE_MEM_8AND16BIT:
 176                s = "8-bit&16-bit";
 177                break;
 178        case IORESOURCE_MEM_32BIT:
 179                s = "32-bit";
 180                break;
 181        default:
 182                s = "16-bit";
 183        }
 184        pnp_printf(buffer, ", %s\n", s);
 185}
 186
 187static void pnp_print_option(pnp_info_buffer_t * buffer, char *space,
 188                             struct pnp_option *option)
 189{
 190        switch (option->type) {
 191        case IORESOURCE_IO:
 192                pnp_print_port(buffer, space, &option->u.port);
 193                break;
 194        case IORESOURCE_MEM:
 195                pnp_print_mem(buffer, space, &option->u.mem);
 196                break;
 197        case IORESOURCE_IRQ:
 198                pnp_print_irq(buffer, space, &option->u.irq);
 199                break;
 200        case IORESOURCE_DMA:
 201                pnp_print_dma(buffer, space, &option->u.dma);
 202                break;
 203        }
 204}
 205
 206static ssize_t options_show(struct device *dmdev, struct device_attribute *attr,
 207                            char *buf)
 208{
 209        struct pnp_dev *dev = to_pnp_dev(dmdev);
 210        pnp_info_buffer_t *buffer;
 211        struct pnp_option *option;
 212        int ret, dep = 0, set = 0;
 213        char *indent;
 214
 215        buffer = pnp_alloc(sizeof(pnp_info_buffer_t));
 216        if (!buffer)
 217                return -ENOMEM;
 218
 219        buffer->len = PAGE_SIZE;
 220        buffer->buffer = buf;
 221        buffer->curr = buffer->buffer;
 222
 223        list_for_each_entry(option, &dev->options, list) {
 224                if (pnp_option_is_dependent(option)) {
 225                        indent = "  ";
 226                        if (!dep || pnp_option_set(option) != set) {
 227                                set = pnp_option_set(option);
 228                                dep = 1;
 229                                pnp_printf(buffer, "Dependent: %02i - "
 230                                           "Priority %s\n", set,
 231                                           pnp_option_priority_name(option));
 232                        }
 233                } else {
 234                        dep = 0;
 235                        indent = "";
 236                }
 237                pnp_print_option(buffer, indent, option);
 238        }
 239
 240        ret = (buffer->curr - buf);
 241        kfree(buffer);
 242        return ret;
 243}
 244static DEVICE_ATTR_RO(options);
 245
 246static ssize_t resources_show(struct device *dmdev,
 247                              struct device_attribute *attr, char *buf)
 248{
 249        struct pnp_dev *dev = to_pnp_dev(dmdev);
 250        pnp_info_buffer_t *buffer;
 251        struct pnp_resource *pnp_res;
 252        struct resource *res;
 253        int ret;
 254
 255        if (!dev)
 256                return -EINVAL;
 257
 258        buffer = pnp_alloc(sizeof(pnp_info_buffer_t));
 259        if (!buffer)
 260                return -ENOMEM;
 261
 262        buffer->len = PAGE_SIZE;
 263        buffer->buffer = buf;
 264        buffer->curr = buffer->buffer;
 265
 266        pnp_printf(buffer, "state = %s\n", dev->active ? "active" : "disabled");
 267
 268        list_for_each_entry(pnp_res, &dev->resources, list) {
 269                res = &pnp_res->res;
 270
 271                pnp_printf(buffer, pnp_resource_type_name(res));
 272
 273                if (res->flags & IORESOURCE_DISABLED) {
 274                        pnp_printf(buffer, " disabled\n");
 275                        continue;
 276                }
 277
 278                switch (pnp_resource_type(res)) {
 279                case IORESOURCE_IO:
 280                case IORESOURCE_MEM:
 281                case IORESOURCE_BUS:
 282                        pnp_printf(buffer, " %#llx-%#llx%s\n",
 283                                   (unsigned long long) res->start,
 284                                   (unsigned long long) res->end,
 285                                   res->flags & IORESOURCE_WINDOW ?
 286                                        " window" : "");
 287                        break;
 288                case IORESOURCE_IRQ:
 289                case IORESOURCE_DMA:
 290                        pnp_printf(buffer, " %lld\n",
 291                                   (unsigned long long) res->start);
 292                        break;
 293                }
 294        }
 295
 296        ret = (buffer->curr - buf);
 297        kfree(buffer);
 298        return ret;
 299}
 300
 301static char *pnp_get_resource_value(char *buf,
 302                                    unsigned long type,
 303                                    resource_size_t *start,
 304                                    resource_size_t *end,
 305                                    unsigned long *flags)
 306{
 307        if (start)
 308                *start = 0;
 309        if (end)
 310                *end = 0;
 311        if (flags)
 312                *flags = 0;
 313
 314        /* TBD: allow for disabled resources */
 315
 316        buf = skip_spaces(buf);
 317        if (start) {
 318                *start = simple_strtoull(buf, &buf, 0);
 319                if (end) {
 320                        buf = skip_spaces(buf);
 321                        if (*buf == '-') {
 322                                buf = skip_spaces(buf + 1);
 323                                *end = simple_strtoull(buf, &buf, 0);
 324                        } else
 325                                *end = *start;
 326                }
 327        }
 328
 329        /* TBD: allow for additional flags, e.g., IORESOURCE_WINDOW */
 330
 331        return buf;
 332}
 333
 334static ssize_t resources_store(struct device *dmdev,
 335                               struct device_attribute *attr, const char *ubuf,
 336                               size_t count)
 337{
 338        struct pnp_dev *dev = to_pnp_dev(dmdev);
 339        char *buf = (void *)ubuf;
 340        int retval = 0;
 341
 342        if (dev->status & PNP_ATTACHED) {
 343                retval = -EBUSY;
 344                dev_info(&dev->dev, "in use; can't configure\n");
 345                goto done;
 346        }
 347
 348        buf = skip_spaces(buf);
 349        if (!strncasecmp(buf, "disable", 7)) {
 350                retval = pnp_disable_dev(dev);
 351                goto done;
 352        }
 353        if (!strncasecmp(buf, "activate", 8)) {
 354                retval = pnp_activate_dev(dev);
 355                goto done;
 356        }
 357        if (!strncasecmp(buf, "fill", 4)) {
 358                if (dev->active)
 359                        goto done;
 360                retval = pnp_auto_config_dev(dev);
 361                goto done;
 362        }
 363        if (!strncasecmp(buf, "auto", 4)) {
 364                if (dev->active)
 365                        goto done;
 366                pnp_init_resources(dev);
 367                retval = pnp_auto_config_dev(dev);
 368                goto done;
 369        }
 370        if (!strncasecmp(buf, "clear", 5)) {
 371                if (dev->active)
 372                        goto done;
 373                pnp_init_resources(dev);
 374                goto done;
 375        }
 376        if (!strncasecmp(buf, "get", 3)) {
 377                mutex_lock(&pnp_res_mutex);
 378                if (pnp_can_read(dev))
 379                        dev->protocol->get(dev);
 380                mutex_unlock(&pnp_res_mutex);
 381                goto done;
 382        }
 383        if (!strncasecmp(buf, "set", 3)) {
 384                resource_size_t start;
 385                resource_size_t end;
 386                unsigned long flags;
 387
 388                if (dev->active)
 389                        goto done;
 390                buf += 3;
 391                pnp_init_resources(dev);
 392                mutex_lock(&pnp_res_mutex);
 393                while (1) {
 394                        buf = skip_spaces(buf);
 395                        if (!strncasecmp(buf, "io", 2)) {
 396                                buf = pnp_get_resource_value(buf + 2,
 397                                                             IORESOURCE_IO,
 398                                                             &start, &end,
 399                                                             &flags);
 400                                pnp_add_io_resource(dev, start, end, flags);
 401                        } else if (!strncasecmp(buf, "mem", 3)) {
 402                                buf = pnp_get_resource_value(buf + 3,
 403                                                             IORESOURCE_MEM,
 404                                                             &start, &end,
 405                                                             &flags);
 406                                pnp_add_mem_resource(dev, start, end, flags);
 407                        } else if (!strncasecmp(buf, "irq", 3)) {
 408                                buf = pnp_get_resource_value(buf + 3,
 409                                                             IORESOURCE_IRQ,
 410                                                             &start, NULL,
 411                                                             &flags);
 412                                pnp_add_irq_resource(dev, start, flags);
 413                        } else if (!strncasecmp(buf, "dma", 3)) {
 414                                buf = pnp_get_resource_value(buf + 3,
 415                                                             IORESOURCE_DMA,
 416                                                             &start, NULL,
 417                                                             &flags);
 418                                pnp_add_dma_resource(dev, start, flags);
 419                        } else if (!strncasecmp(buf, "bus", 3)) {
 420                                buf = pnp_get_resource_value(buf + 3,
 421                                                             IORESOURCE_BUS,
 422                                                             &start, &end,
 423                                                             NULL);
 424                                pnp_add_bus_resource(dev, start, end);
 425                        } else
 426                                break;
 427                }
 428                mutex_unlock(&pnp_res_mutex);
 429                goto done;
 430        }
 431
 432done:
 433        if (retval < 0)
 434                return retval;
 435        return count;
 436}
 437static DEVICE_ATTR_RW(resources);
 438
 439static ssize_t id_show(struct device *dmdev, struct device_attribute *attr,
 440                       char *buf)
 441{
 442        char *str = buf;
 443        struct pnp_dev *dev = to_pnp_dev(dmdev);
 444        struct pnp_id *pos = dev->id;
 445
 446        while (pos) {
 447                str += sprintf(str, "%s\n", pos->id);
 448                pos = pos->next;
 449        }
 450        return (str - buf);
 451}
 452static DEVICE_ATTR_RO(id);
 453
 454static struct attribute *pnp_dev_attrs[] = {
 455        &dev_attr_resources.attr,
 456        &dev_attr_options.attr,
 457        &dev_attr_id.attr,
 458        NULL,
 459};
 460
 461static const struct attribute_group pnp_dev_group = {
 462        .attrs = pnp_dev_attrs,
 463};
 464
 465const struct attribute_group *pnp_dev_groups[] = {
 466        &pnp_dev_group,
 467        NULL,
 468};
 469