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#include "qemu/osdep.h"
  25#include "chardev/char.h"
  26#include "qapi/error.h"
  27#include <sys/ioctl.h>
  28
  29#ifdef CONFIG_BSD
  30#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
  31#include <dev/ppbus/ppi.h>
  32#include <dev/ppbus/ppbconf.h>
  33#elif defined(__DragonFly__)
  34#include <dev/misc/ppi/ppi.h>
  35#include <bus/ppbus/ppbconf.h>
  36#endif
  37#else
  38#ifdef __linux__
  39#include <linux/ppdev.h>
  40#include <linux/parport.h>
  41#endif
  42#endif
  43
  44#include "chardev/char-fd.h"
  45#include "chardev/char-parallel.h"
  46
  47#if defined(__linux__)
  48
  49typedef struct {
  50    Chardev parent;
  51    int fd;
  52    int mode;
  53} ParallelChardev;
  54
  55#define PARALLEL_CHARDEV(obj) \
  56    OBJECT_CHECK(ParallelChardev, (obj), TYPE_CHARDEV_PARALLEL)
  57
  58static int pp_hw_mode(ParallelChardev *s, uint16_t mode)
  59{
  60    if (s->mode != mode) {
  61        int m = mode;
  62        if (ioctl(s->fd, PPSETMODE, &m) < 0) {
  63            return 0;
  64        }
  65        s->mode = mode;
  66    }
  67    return 1;
  68}
  69
  70static int pp_ioctl(Chardev *chr, int cmd, void *arg)
  71{
  72    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
  73    int fd = drv->fd;
  74    uint8_t b;
  75
  76    switch (cmd) {
  77    case CHR_IOCTL_PP_READ_DATA:
  78        if (ioctl(fd, PPRDATA, &b) < 0) {
  79            return -ENOTSUP;
  80        }
  81        *(uint8_t *)arg = b;
  82        break;
  83    case CHR_IOCTL_PP_WRITE_DATA:
  84        b = *(uint8_t *)arg;
  85        if (ioctl(fd, PPWDATA, &b) < 0) {
  86            return -ENOTSUP;
  87        }
  88        break;
  89    case CHR_IOCTL_PP_READ_CONTROL:
  90        if (ioctl(fd, PPRCONTROL, &b) < 0) {
  91            return -ENOTSUP;
  92        }
  93        /* Linux gives only the lowest bits, and no way to know data
  94           direction! For better compatibility set the fixed upper
  95           bits. */
  96        *(uint8_t *)arg = b | 0xc0;
  97        break;
  98    case CHR_IOCTL_PP_WRITE_CONTROL:
  99        b = *(uint8_t *)arg;
 100        if (ioctl(fd, PPWCONTROL, &b) < 0) {
 101            return -ENOTSUP;
 102        }
 103        break;
 104    case CHR_IOCTL_PP_READ_STATUS:
 105        if (ioctl(fd, PPRSTATUS, &b) < 0) {
 106            return -ENOTSUP;
 107        }
 108        *(uint8_t *)arg = b;
 109        break;
 110    case CHR_IOCTL_PP_DATA_DIR:
 111        if (ioctl(fd, PPDATADIR, (int *)arg) < 0) {
 112            return -ENOTSUP;
 113        }
 114        break;
 115    case CHR_IOCTL_PP_EPP_READ_ADDR:
 116        if (pp_hw_mode(drv, IEEE1284_MODE_EPP | IEEE1284_ADDR)) {
 117            struct ParallelIOArg *parg = arg;
 118            int n = read(fd, parg->buffer, parg->count);
 119            if (n != parg->count) {
 120                return -EIO;
 121            }
 122        }
 123        break;
 124    case CHR_IOCTL_PP_EPP_READ:
 125        if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) {
 126            struct ParallelIOArg *parg = arg;
 127            int n = read(fd, parg->buffer, parg->count);
 128            if (n != parg->count) {
 129                return -EIO;
 130            }
 131        }
 132        break;
 133    case CHR_IOCTL_PP_EPP_WRITE_ADDR:
 134        if (pp_hw_mode(drv, IEEE1284_MODE_EPP | IEEE1284_ADDR)) {
 135            struct ParallelIOArg *parg = arg;
 136            int n = write(fd, parg->buffer, parg->count);
 137            if (n != parg->count) {
 138                return -EIO;
 139            }
 140        }
 141        break;
 142    case CHR_IOCTL_PP_EPP_WRITE:
 143        if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) {
 144            struct ParallelIOArg *parg = arg;
 145            int n = write(fd, parg->buffer, parg->count);
 146            if (n != parg->count) {
 147                return -EIO;
 148            }
 149        }
 150        break;
 151    default:
 152        return -ENOTSUP;
 153    }
 154    return 0;
 155}
 156
 157static void qemu_chr_open_pp_fd(Chardev *chr,
 158                                int fd,
 159                                bool *be_opened,
 160                                Error **errp)
 161{
 162    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
 163
 164    if (ioctl(fd, PPCLAIM) < 0) {
 165        error_setg_errno(errp, errno, "not a parallel port");
 166        close(fd);
 167        return;
 168    }
 169
 170    drv->fd = fd;
 171    drv->mode = IEEE1284_MODE_COMPAT;
 172}
 173#endif /* __linux__ */
 174
 175#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
 176
 177typedef struct {
 178    Chardev parent;
 179    int fd;
 180} ParallelChardev;
 181
 182#define PARALLEL_CHARDEV(obj)                                   \
 183    OBJECT_CHECK(ParallelChardev, (obj), TYPE_CHARDEV_PARALLEL)
 184
 185static int pp_ioctl(Chardev *chr, int cmd, void *arg)
 186{
 187    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
 188    uint8_t b;
 189
 190    switch (cmd) {
 191    case CHR_IOCTL_PP_READ_DATA:
 192        if (ioctl(drv->fd, PPIGDATA, &b) < 0) {
 193            return -ENOTSUP;
 194        }
 195        *(uint8_t *)arg = b;
 196        break;
 197    case CHR_IOCTL_PP_WRITE_DATA:
 198        b = *(uint8_t *)arg;
 199        if (ioctl(drv->fd, PPISDATA, &b) < 0) {
 200            return -ENOTSUP;
 201        }
 202        break;
 203    case CHR_IOCTL_PP_READ_CONTROL:
 204        if (ioctl(drv->fd, PPIGCTRL, &b) < 0) {
 205            return -ENOTSUP;
 206        }
 207        *(uint8_t *)arg = b;
 208        break;
 209    case CHR_IOCTL_PP_WRITE_CONTROL:
 210        b = *(uint8_t *)arg;
 211        if (ioctl(drv->fd, PPISCTRL, &b) < 0) {
 212            return -ENOTSUP;
 213        }
 214        break;
 215    case CHR_IOCTL_PP_READ_STATUS:
 216        if (ioctl(drv->fd, PPIGSTATUS, &b) < 0) {
 217            return -ENOTSUP;
 218        }
 219        *(uint8_t *)arg = b;
 220        break;
 221    default:
 222        return -ENOTSUP;
 223    }
 224    return 0;
 225}
 226
 227static void qemu_chr_open_pp_fd(Chardev *chr,
 228                                int fd,
 229                                bool *be_opened,
 230                                Error **errp)
 231{
 232    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
 233    drv->fd = fd;
 234    *be_opened = false;
 235}
 236#endif
 237
 238#ifdef HAVE_CHARDEV_PARPORT
 239static void qmp_chardev_open_parallel(Chardev *chr,
 240                                      ChardevBackend *backend,
 241                                      bool *be_opened,
 242                                      Error **errp)
 243{
 244    ChardevHostdev *parallel = backend->u.parallel.data;
 245    int fd;
 246
 247    fd = qmp_chardev_open_file_source(parallel->device, O_RDWR, errp);
 248    if (fd < 0) {
 249        return;
 250    }
 251    qemu_chr_open_pp_fd(chr, fd, be_opened, errp);
 252}
 253
 254static void qemu_chr_parse_parallel(QemuOpts *opts, ChardevBackend *backend,
 255                                    Error **errp)
 256{
 257    const char *device = qemu_opt_get(opts, "path");
 258    ChardevHostdev *parallel;
 259
 260    if (device == NULL) {
 261        error_setg(errp, "chardev: parallel: no device path given");
 262        return;
 263    }
 264    backend->type = CHARDEV_BACKEND_KIND_PARALLEL;
 265    parallel = backend->u.parallel.data = g_new0(ChardevHostdev, 1);
 266    qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(parallel));
 267    parallel->device = g_strdup(device);
 268}
 269
 270static void char_parallel_class_init(ObjectClass *oc, void *data)
 271{
 272    ChardevClass *cc = CHARDEV_CLASS(oc);
 273
 274    cc->parse = qemu_chr_parse_parallel;
 275    cc->open = qmp_chardev_open_parallel;
 276#if defined(__linux__)
 277    cc->chr_ioctl = pp_ioctl;
 278#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
 279    defined(__DragonFly__)
 280    cc->chr_ioctl = pp_ioctl;
 281#endif
 282}
 283
 284static void char_parallel_finalize(Object *obj)
 285{
 286#if defined(__linux__)
 287    Chardev *chr = CHARDEV(obj);
 288    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
 289    int fd = drv->fd;
 290
 291    pp_hw_mode(drv, IEEE1284_MODE_COMPAT);
 292    ioctl(fd, PPRELEASE);
 293    close(fd);
 294    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 295#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
 296    defined(__DragonFly__)
 297    /* FIXME: close fd? */
 298#endif
 299}
 300
 301static const TypeInfo char_parallel_type_info = {
 302    .name = TYPE_CHARDEV_PARALLEL,
 303    .parent = TYPE_CHARDEV,
 304    .instance_size = sizeof(ParallelChardev),
 305    .instance_finalize = char_parallel_finalize,
 306    .class_init = char_parallel_class_init,
 307};
 308
 309static void register_types(void)
 310{
 311    type_register_static(&char_parallel_type_info);
 312}
 313
 314type_init(register_types);
 315
 316#endif
 317