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