qemu/chardev/char-parallel.c
<<
>>
Prefs
   1/*
   2 * QEMU System Emulator
   3 *
   4 * Copyright (c) 2003-2008 Fabrice Bellard
   5 *
   6 * Permission is hereby granted, free of charge, to any person obtaining a copy
   7 * of this software and associated documentation files (the "Software"), to deal
   8 * in the Software without restriction, including without limitation the rights
   9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 * copies of the Software, and to permit persons to whom the Software is
  11 * furnished to do so, subject to the following conditions:
  12 *
  13 * The above copyright notice and this permission notice shall be included in
  14 * all copies or substantial portions of the Software.
  15 *
  16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 * THE SOFTWARE.
  23 */
  24
  25#include "qemu/osdep.h"
  26#include "chardev/char.h"
  27#include "qapi/error.h"
  28#include "qemu/option.h"
  29#include <sys/ioctl.h>
  30
  31#ifdef CONFIG_BSD
  32#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
  33#include <dev/ppbus/ppi.h>
  34#include <dev/ppbus/ppbconf.h>
  35#elif defined(__DragonFly__)
  36#include <dev/misc/ppi/ppi.h>
  37#include <bus/ppbus/ppbconf.h>
  38#endif
  39#else
  40#ifdef __linux__
  41#include <linux/ppdev.h>
  42#include <linux/parport.h>
  43#endif
  44#endif
  45
  46#include "chardev/char-fd.h"
  47#include "chardev/char-parallel.h"
  48
  49#if defined(__linux__)
  50
  51typedef struct {
  52    Chardev parent;
  53    int fd;
  54    int mode;
  55} ParallelChardev;
  56
  57#define PARALLEL_CHARDEV(obj) \
  58    OBJECT_CHECK(ParallelChardev, (obj), TYPE_CHARDEV_PARALLEL)
  59
  60static int pp_hw_mode(ParallelChardev *s, uint16_t mode)
  61{
  62    if (s->mode != mode) {
  63        int m = mode;
  64        if (ioctl(s->fd, PPSETMODE, &m) < 0) {
  65            return 0;
  66        }
  67        s->mode = mode;
  68    }
  69    return 1;
  70}
  71
  72static int pp_ioctl(Chardev *chr, int cmd, void *arg)
  73{
  74    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
  75    int fd = drv->fd;
  76    uint8_t b;
  77
  78    switch (cmd) {
  79    case CHR_IOCTL_PP_READ_DATA:
  80        if (ioctl(fd, PPRDATA, &b) < 0) {
  81            return -ENOTSUP;
  82        }
  83        *(uint8_t *)arg = b;
  84        break;
  85    case CHR_IOCTL_PP_WRITE_DATA:
  86        b = *(uint8_t *)arg;
  87        if (ioctl(fd, PPWDATA, &b) < 0) {
  88            return -ENOTSUP;
  89        }
  90        break;
  91    case CHR_IOCTL_PP_READ_CONTROL:
  92        if (ioctl(fd, PPRCONTROL, &b) < 0) {
  93            return -ENOTSUP;
  94        }
  95        /* Linux gives only the lowest bits, and no way to know data
  96           direction! For better compatibility set the fixed upper
  97           bits. */
  98        *(uint8_t *)arg = b | 0xc0;
  99        break;
 100    case CHR_IOCTL_PP_WRITE_CONTROL:
 101        b = *(uint8_t *)arg;
 102        if (ioctl(fd, PPWCONTROL, &b) < 0) {
 103            return -ENOTSUP;
 104        }
 105        break;
 106    case CHR_IOCTL_PP_READ_STATUS:
 107        if (ioctl(fd, PPRSTATUS, &b) < 0) {
 108            return -ENOTSUP;
 109        }
 110        *(uint8_t *)arg = b;
 111        break;
 112    case CHR_IOCTL_PP_DATA_DIR:
 113        if (ioctl(fd, PPDATADIR, (int *)arg) < 0) {
 114            return -ENOTSUP;
 115        }
 116        break;
 117    case CHR_IOCTL_PP_EPP_READ_ADDR:
 118        if (pp_hw_mode(drv, IEEE1284_MODE_EPP | IEEE1284_ADDR)) {
 119            struct ParallelIOArg *parg = arg;
 120            int n = read(fd, parg->buffer, parg->count);
 121            if (n != parg->count) {
 122                return -EIO;
 123            }
 124        }
 125        break;
 126    case CHR_IOCTL_PP_EPP_READ:
 127        if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) {
 128            struct ParallelIOArg *parg = arg;
 129            int n = read(fd, parg->buffer, parg->count);
 130            if (n != parg->count) {
 131                return -EIO;
 132            }
 133        }
 134        break;
 135    case CHR_IOCTL_PP_EPP_WRITE_ADDR:
 136        if (pp_hw_mode(drv, IEEE1284_MODE_EPP | IEEE1284_ADDR)) {
 137            struct ParallelIOArg *parg = arg;
 138            int n = write(fd, parg->buffer, parg->count);
 139            if (n != parg->count) {
 140                return -EIO;
 141            }
 142        }
 143        break;
 144    case CHR_IOCTL_PP_EPP_WRITE:
 145        if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) {
 146            struct ParallelIOArg *parg = arg;
 147            int n = write(fd, parg->buffer, parg->count);
 148            if (n != parg->count) {
 149                return -EIO;
 150            }
 151        }
 152        break;
 153    default:
 154        return -ENOTSUP;
 155    }
 156    return 0;
 157}
 158
 159static void qemu_chr_open_pp_fd(Chardev *chr,
 160                                int fd,
 161                                bool *be_opened,
 162                                Error **errp)
 163{
 164    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
 165
 166    if (ioctl(fd, PPCLAIM) < 0) {
 167        error_setg_errno(errp, errno, "not a parallel port");
 168        close(fd);
 169        return;
 170    }
 171
 172    drv->fd = fd;
 173    drv->mode = IEEE1284_MODE_COMPAT;
 174}
 175#endif /* __linux__ */
 176
 177#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
 178
 179typedef struct {
 180    Chardev parent;
 181    int fd;
 182} ParallelChardev;
 183
 184#define PARALLEL_CHARDEV(obj)                                   \
 185    OBJECT_CHECK(ParallelChardev, (obj), TYPE_CHARDEV_PARALLEL)
 186
 187static int pp_ioctl(Chardev *chr, int cmd, void *arg)
 188{
 189    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
 190    uint8_t b;
 191
 192    switch (cmd) {
 193    case CHR_IOCTL_PP_READ_DATA:
 194        if (ioctl(drv->fd, PPIGDATA, &b) < 0) {
 195            return -ENOTSUP;
 196        }
 197        *(uint8_t *)arg = b;
 198        break;
 199    case CHR_IOCTL_PP_WRITE_DATA:
 200        b = *(uint8_t *)arg;
 201        if (ioctl(drv->fd, PPISDATA, &b) < 0) {
 202            return -ENOTSUP;
 203        }
 204        break;
 205    case CHR_IOCTL_PP_READ_CONTROL:
 206        if (ioctl(drv->fd, PPIGCTRL, &b) < 0) {
 207            return -ENOTSUP;
 208        }
 209        *(uint8_t *)arg = b;
 210        break;
 211    case CHR_IOCTL_PP_WRITE_CONTROL:
 212        b = *(uint8_t *)arg;
 213        if (ioctl(drv->fd, PPISCTRL, &b) < 0) {
 214            return -ENOTSUP;
 215        }
 216        break;
 217    case CHR_IOCTL_PP_READ_STATUS:
 218        if (ioctl(drv->fd, PPIGSTATUS, &b) < 0) {
 219            return -ENOTSUP;
 220        }
 221        *(uint8_t *)arg = b;
 222        break;
 223    default:
 224        return -ENOTSUP;
 225    }
 226    return 0;
 227}
 228
 229static void qemu_chr_open_pp_fd(Chardev *chr,
 230                                int fd,
 231                                bool *be_opened,
 232                                Error **errp)
 233{
 234    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
 235    drv->fd = fd;
 236    *be_opened = false;
 237}
 238#endif
 239
 240#ifdef HAVE_CHARDEV_PARPORT
 241static void qmp_chardev_open_parallel(Chardev *chr,
 242                                      ChardevBackend *backend,
 243                                      bool *be_opened,
 244                                      Error **errp)
 245{
 246    ChardevHostdev *parallel = backend->u.parallel.data;
 247    int fd;
 248
 249    fd = qmp_chardev_open_file_source(parallel->device, O_RDWR, errp);
 250    if (fd < 0) {
 251        return;
 252    }
 253    qemu_chr_open_pp_fd(chr, fd, be_opened, errp);
 254}
 255
 256static void qemu_chr_parse_parallel(QemuOpts *opts, ChardevBackend *backend,
 257                                    Error **errp)
 258{
 259    const char *device = qemu_opt_get(opts, "path");
 260    ChardevHostdev *parallel;
 261
 262    if (device == NULL) {
 263        error_setg(errp, "chardev: parallel: no device path given");
 264        return;
 265    }
 266    backend->type = CHARDEV_BACKEND_KIND_PARALLEL;
 267    parallel = backend->u.parallel.data = g_new0(ChardevHostdev, 1);
 268    qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(parallel));
 269    parallel->device = g_strdup(device);
 270}
 271
 272static void char_parallel_class_init(ObjectClass *oc, void *data)
 273{
 274    ChardevClass *cc = CHARDEV_CLASS(oc);
 275
 276    cc->parse = qemu_chr_parse_parallel;
 277    cc->open = qmp_chardev_open_parallel;
 278#if defined(__linux__)
 279    cc->chr_ioctl = pp_ioctl;
 280#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
 281    defined(__DragonFly__)
 282    cc->chr_ioctl = pp_ioctl;
 283#endif
 284}
 285
 286static void char_parallel_finalize(Object *obj)
 287{
 288#if defined(__linux__)
 289    Chardev *chr = CHARDEV(obj);
 290    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
 291    int fd = drv->fd;
 292
 293    pp_hw_mode(drv, IEEE1284_MODE_COMPAT);
 294    ioctl(fd, PPRELEASE);
 295    close(fd);
 296    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 297#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
 298    defined(__DragonFly__)
 299    /* FIXME: close fd? */
 300#endif
 301}
 302
 303static const TypeInfo char_parallel_type_info = {
 304    .name = TYPE_CHARDEV_PARALLEL,
 305    .parent = TYPE_CHARDEV,
 306    .instance_size = sizeof(ParallelChardev),
 307    .instance_finalize = char_parallel_finalize,
 308    .class_init = char_parallel_class_init,
 309};
 310
 311static void register_types(void)
 312{
 313    type_register_static(&char_parallel_type_info);
 314}
 315
 316type_init(register_types);
 317
 318#endif
 319