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 pnp_show_options(struct device *dmdev,
 207                                struct device_attribute *attr, 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}
 244
 245static ssize_t pnp_show_current_resources(struct device *dmdev,
 246                                          struct device_attribute *attr,
 247                                          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 ssize_t pnp_set_current_resources(struct device *dmdev,
 302                                         struct device_attribute *attr,
 303                                         const char *ubuf, size_t count)
 304{
 305        struct pnp_dev *dev = to_pnp_dev(dmdev);
 306        char *buf = (void *)ubuf;
 307        int retval = 0;
 308        resource_size_t start, end;
 309
 310        if (dev->status & PNP_ATTACHED) {
 311                retval = -EBUSY;
 312                dev_info(&dev->dev, "in use; can't configure\n");
 313                goto done;
 314        }
 315
 316        buf = skip_spaces(buf);
 317        if (!strnicmp(buf, "disable", 7)) {
 318                retval = pnp_disable_dev(dev);
 319                goto done;
 320        }
 321        if (!strnicmp(buf, "activate", 8)) {
 322                retval = pnp_activate_dev(dev);
 323                goto done;
 324        }
 325        if (!strnicmp(buf, "fill", 4)) {
 326                if (dev->active)
 327                        goto done;
 328                retval = pnp_auto_config_dev(dev);
 329                goto done;
 330        }
 331        if (!strnicmp(buf, "auto", 4)) {
 332                if (dev->active)
 333                        goto done;
 334                pnp_init_resources(dev);
 335                retval = pnp_auto_config_dev(dev);
 336                goto done;
 337        }
 338        if (!strnicmp(buf, "clear", 5)) {
 339                if (dev->active)
 340                        goto done;
 341                pnp_init_resources(dev);
 342                goto done;
 343        }
 344        if (!strnicmp(buf, "get", 3)) {
 345                mutex_lock(&pnp_res_mutex);
 346                if (pnp_can_read(dev))
 347                        dev->protocol->get(dev);
 348                mutex_unlock(&pnp_res_mutex);
 349                goto done;
 350        }
 351        if (!strnicmp(buf, "set", 3)) {
 352                if (dev->active)
 353                        goto done;
 354                buf += 3;
 355                pnp_init_resources(dev);
 356                mutex_lock(&pnp_res_mutex);
 357                while (1) {
 358                        buf = skip_spaces(buf);
 359                        if (!strnicmp(buf, "io", 2)) {
 360                                buf = skip_spaces(buf + 2);
 361                                start = simple_strtoul(buf, &buf, 0);
 362                                buf = skip_spaces(buf);
 363                                if (*buf == '-') {
 364                                        buf = skip_spaces(buf + 1);
 365                                        end = simple_strtoul(buf, &buf, 0);
 366                                } else
 367                                        end = start;
 368                                pnp_add_io_resource(dev, start, end, 0);
 369                                continue;
 370                        }
 371                        if (!strnicmp(buf, "mem", 3)) {
 372                                buf = skip_spaces(buf + 3);
 373                                start = simple_strtoul(buf, &buf, 0);
 374                                buf = skip_spaces(buf);
 375                                if (*buf == '-') {
 376                                        buf = skip_spaces(buf + 1);
 377                                        end = simple_strtoul(buf, &buf, 0);
 378                                } else
 379                                        end = start;
 380                                pnp_add_mem_resource(dev, start, end, 0);
 381                                continue;
 382                        }
 383                        if (!strnicmp(buf, "irq", 3)) {
 384                                buf = skip_spaces(buf + 3);
 385                                start = simple_strtoul(buf, &buf, 0);
 386                                pnp_add_irq_resource(dev, start, 0);
 387                                continue;
 388                        }
 389                        if (!strnicmp(buf, "dma", 3)) {
 390                                buf = skip_spaces(buf + 3);
 391                                start = simple_strtoul(buf, &buf, 0);
 392                                pnp_add_dma_resource(dev, start, 0);
 393                                continue;
 394                        }
 395                        break;
 396                }
 397                mutex_unlock(&pnp_res_mutex);
 398                goto done;
 399        }
 400
 401done:
 402        if (retval < 0)
 403                return retval;
 404        return count;
 405}
 406
 407static ssize_t pnp_show_current_ids(struct device *dmdev,
 408                                    struct device_attribute *attr, char *buf)
 409{
 410        char *str = buf;
 411        struct pnp_dev *dev = to_pnp_dev(dmdev);
 412        struct pnp_id *pos = dev->id;
 413
 414        while (pos) {
 415                str += sprintf(str, "%s\n", pos->id);
 416                pos = pos->next;
 417        }
 418        return (str - buf);
 419}
 420
 421struct device_attribute pnp_interface_attrs[] = {
 422        __ATTR(resources, S_IRUGO | S_IWUSR,
 423                   pnp_show_current_resources,
 424                   pnp_set_current_resources),
 425        __ATTR(options, S_IRUGO, pnp_show_options, NULL),
 426        __ATTR(id, S_IRUGO, pnp_show_current_ids, NULL),
 427        __ATTR_NULL,
 428};
 429
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.