1*b430a2bdSLongpeng /* 2*b430a2bdSLongpeng * Vhost Vdpa Device 3*b430a2bdSLongpeng * 4*b430a2bdSLongpeng * Copyright (c) Huawei Technologies Co., Ltd. 2022. All Rights Reserved. 5*b430a2bdSLongpeng * 6*b430a2bdSLongpeng * Authors: 7*b430a2bdSLongpeng * Longpeng <longpeng2@huawei.com> 8*b430a2bdSLongpeng * 9*b430a2bdSLongpeng * Largely based on the "vhost-user-blk-pci.c" and "vhost-user-blk.c" 10*b430a2bdSLongpeng * implemented by: 11*b430a2bdSLongpeng * Changpeng Liu <changpeng.liu@intel.com> 12*b430a2bdSLongpeng * 13*b430a2bdSLongpeng * This work is licensed under the terms of the GNU LGPL, version 2 or later. 14*b430a2bdSLongpeng * See the COPYING.LIB file in the top-level directory. 15*b430a2bdSLongpeng */ 16*b430a2bdSLongpeng #include "qemu/osdep.h" 17*b430a2bdSLongpeng #include <sys/ioctl.h> 18*b430a2bdSLongpeng #include <linux/vhost.h> 19*b430a2bdSLongpeng #include "qapi/error.h" 20*b430a2bdSLongpeng #include "qemu/error-report.h" 21*b430a2bdSLongpeng #include "qemu/cutils.h" 22*b430a2bdSLongpeng #include "hw/qdev-core.h" 23*b430a2bdSLongpeng #include "hw/qdev-properties.h" 24*b430a2bdSLongpeng #include "hw/qdev-properties-system.h" 25*b430a2bdSLongpeng #include "hw/virtio/vhost.h" 26*b430a2bdSLongpeng #include "hw/virtio/virtio.h" 27*b430a2bdSLongpeng #include "hw/virtio/virtio-bus.h" 28*b430a2bdSLongpeng #include "hw/virtio/virtio-access.h" 29*b430a2bdSLongpeng #include "hw/virtio/vdpa-dev.h" 30*b430a2bdSLongpeng #include "sysemu/sysemu.h" 31*b430a2bdSLongpeng #include "sysemu/runstate.h" 32*b430a2bdSLongpeng 33*b430a2bdSLongpeng static void 34*b430a2bdSLongpeng vhost_vdpa_device_dummy_handle_output(VirtIODevice *vdev, VirtQueue *vq) 35*b430a2bdSLongpeng { 36*b430a2bdSLongpeng /* Nothing to do */ 37*b430a2bdSLongpeng } 38*b430a2bdSLongpeng 39*b430a2bdSLongpeng static uint32_t 40*b430a2bdSLongpeng vhost_vdpa_device_get_u32(int fd, unsigned long int cmd, Error **errp) 41*b430a2bdSLongpeng { 42*b430a2bdSLongpeng uint32_t val = (uint32_t)-1; 43*b430a2bdSLongpeng 44*b430a2bdSLongpeng if (ioctl(fd, cmd, &val) < 0) { 45*b430a2bdSLongpeng error_setg(errp, "vhost-vdpa-device: cmd 0x%lx failed: %s", 46*b430a2bdSLongpeng cmd, strerror(errno)); 47*b430a2bdSLongpeng } 48*b430a2bdSLongpeng 49*b430a2bdSLongpeng return val; 50*b430a2bdSLongpeng } 51*b430a2bdSLongpeng 52*b430a2bdSLongpeng static void vhost_vdpa_device_realize(DeviceState *dev, Error **errp) 53*b430a2bdSLongpeng { 54*b430a2bdSLongpeng VirtIODevice *vdev = VIRTIO_DEVICE(dev); 55*b430a2bdSLongpeng VhostVdpaDevice *v = VHOST_VDPA_DEVICE(vdev); 56*b430a2bdSLongpeng uint16_t max_queue_size; 57*b430a2bdSLongpeng struct vhost_virtqueue *vqs; 58*b430a2bdSLongpeng int i, ret; 59*b430a2bdSLongpeng 60*b430a2bdSLongpeng if (!v->vhostdev) { 61*b430a2bdSLongpeng error_setg(errp, "vhost-vdpa-device: vhostdev are missing"); 62*b430a2bdSLongpeng return; 63*b430a2bdSLongpeng } 64*b430a2bdSLongpeng 65*b430a2bdSLongpeng v->vhostfd = qemu_open(v->vhostdev, O_RDWR, errp); 66*b430a2bdSLongpeng if (*errp) { 67*b430a2bdSLongpeng return; 68*b430a2bdSLongpeng } 69*b430a2bdSLongpeng v->vdpa.device_fd = v->vhostfd; 70*b430a2bdSLongpeng 71*b430a2bdSLongpeng v->vdev_id = vhost_vdpa_device_get_u32(v->vhostfd, 72*b430a2bdSLongpeng VHOST_VDPA_GET_DEVICE_ID, errp); 73*b430a2bdSLongpeng if (*errp) { 74*b430a2bdSLongpeng goto out; 75*b430a2bdSLongpeng } 76*b430a2bdSLongpeng 77*b430a2bdSLongpeng max_queue_size = vhost_vdpa_device_get_u32(v->vhostfd, 78*b430a2bdSLongpeng VHOST_VDPA_GET_VRING_NUM, errp); 79*b430a2bdSLongpeng if (*errp) { 80*b430a2bdSLongpeng goto out; 81*b430a2bdSLongpeng } 82*b430a2bdSLongpeng 83*b430a2bdSLongpeng if (v->queue_size > max_queue_size) { 84*b430a2bdSLongpeng error_setg(errp, "vhost-vdpa-device: invalid queue_size: %u (max:%u)", 85*b430a2bdSLongpeng v->queue_size, max_queue_size); 86*b430a2bdSLongpeng goto out; 87*b430a2bdSLongpeng } else if (!v->queue_size) { 88*b430a2bdSLongpeng v->queue_size = max_queue_size; 89*b430a2bdSLongpeng } 90*b430a2bdSLongpeng 91*b430a2bdSLongpeng v->num_queues = vhost_vdpa_device_get_u32(v->vhostfd, 92*b430a2bdSLongpeng VHOST_VDPA_GET_VQS_COUNT, errp); 93*b430a2bdSLongpeng if (*errp) { 94*b430a2bdSLongpeng goto out; 95*b430a2bdSLongpeng } 96*b430a2bdSLongpeng 97*b430a2bdSLongpeng if (!v->num_queues || v->num_queues > VIRTIO_QUEUE_MAX) { 98*b430a2bdSLongpeng error_setg(errp, "invalid number of virtqueues: %u (max:%u)", 99*b430a2bdSLongpeng v->num_queues, VIRTIO_QUEUE_MAX); 100*b430a2bdSLongpeng goto out; 101*b430a2bdSLongpeng } 102*b430a2bdSLongpeng 103*b430a2bdSLongpeng v->dev.nvqs = v->num_queues; 104*b430a2bdSLongpeng vqs = g_new0(struct vhost_virtqueue, v->dev.nvqs); 105*b430a2bdSLongpeng v->dev.vqs = vqs; 106*b430a2bdSLongpeng v->dev.vq_index = 0; 107*b430a2bdSLongpeng v->dev.vq_index_end = v->dev.nvqs; 108*b430a2bdSLongpeng v->dev.backend_features = 0; 109*b430a2bdSLongpeng v->started = false; 110*b430a2bdSLongpeng 111*b430a2bdSLongpeng ret = vhost_dev_init(&v->dev, &v->vdpa, VHOST_BACKEND_TYPE_VDPA, 0, NULL); 112*b430a2bdSLongpeng if (ret < 0) { 113*b430a2bdSLongpeng error_setg(errp, "vhost-vdpa-device: vhost initialization failed: %s", 114*b430a2bdSLongpeng strerror(-ret)); 115*b430a2bdSLongpeng goto free_vqs; 116*b430a2bdSLongpeng } 117*b430a2bdSLongpeng 118*b430a2bdSLongpeng v->config_size = vhost_vdpa_device_get_u32(v->vhostfd, 119*b430a2bdSLongpeng VHOST_VDPA_GET_CONFIG_SIZE, 120*b430a2bdSLongpeng errp); 121*b430a2bdSLongpeng if (*errp) { 122*b430a2bdSLongpeng goto vhost_cleanup; 123*b430a2bdSLongpeng } 124*b430a2bdSLongpeng 125*b430a2bdSLongpeng /* 126*b430a2bdSLongpeng * Invoke .post_init() to initialize the transport-specific fields 127*b430a2bdSLongpeng * before calling virtio_init(). 128*b430a2bdSLongpeng */ 129*b430a2bdSLongpeng if (v->post_init && v->post_init(v, errp) < 0) { 130*b430a2bdSLongpeng goto vhost_cleanup; 131*b430a2bdSLongpeng } 132*b430a2bdSLongpeng 133*b430a2bdSLongpeng v->config = g_malloc0(v->config_size); 134*b430a2bdSLongpeng 135*b430a2bdSLongpeng ret = vhost_dev_get_config(&v->dev, v->config, v->config_size, NULL); 136*b430a2bdSLongpeng if (ret < 0) { 137*b430a2bdSLongpeng error_setg(errp, "vhost-vdpa-device: get config failed"); 138*b430a2bdSLongpeng goto free_config; 139*b430a2bdSLongpeng } 140*b430a2bdSLongpeng 141*b430a2bdSLongpeng virtio_init(vdev, v->vdev_id, v->config_size); 142*b430a2bdSLongpeng 143*b430a2bdSLongpeng v->virtqs = g_new0(VirtQueue *, v->dev.nvqs); 144*b430a2bdSLongpeng for (i = 0; i < v->dev.nvqs; i++) { 145*b430a2bdSLongpeng v->virtqs[i] = virtio_add_queue(vdev, v->queue_size, 146*b430a2bdSLongpeng vhost_vdpa_device_dummy_handle_output); 147*b430a2bdSLongpeng } 148*b430a2bdSLongpeng 149*b430a2bdSLongpeng return; 150*b430a2bdSLongpeng 151*b430a2bdSLongpeng free_config: 152*b430a2bdSLongpeng g_free(v->config); 153*b430a2bdSLongpeng vhost_cleanup: 154*b430a2bdSLongpeng vhost_dev_cleanup(&v->dev); 155*b430a2bdSLongpeng free_vqs: 156*b430a2bdSLongpeng g_free(vqs); 157*b430a2bdSLongpeng out: 158*b430a2bdSLongpeng qemu_close(v->vhostfd); 159*b430a2bdSLongpeng v->vhostfd = -1; 160*b430a2bdSLongpeng } 161*b430a2bdSLongpeng 162*b430a2bdSLongpeng static void vhost_vdpa_device_unrealize(DeviceState *dev) 163*b430a2bdSLongpeng { 164*b430a2bdSLongpeng VirtIODevice *vdev = VIRTIO_DEVICE(dev); 165*b430a2bdSLongpeng VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); 166*b430a2bdSLongpeng int i; 167*b430a2bdSLongpeng 168*b430a2bdSLongpeng virtio_set_status(vdev, 0); 169*b430a2bdSLongpeng 170*b430a2bdSLongpeng for (i = 0; i < s->num_queues; i++) { 171*b430a2bdSLongpeng virtio_delete_queue(s->virtqs[i]); 172*b430a2bdSLongpeng } 173*b430a2bdSLongpeng g_free(s->virtqs); 174*b430a2bdSLongpeng virtio_cleanup(vdev); 175*b430a2bdSLongpeng 176*b430a2bdSLongpeng g_free(s->config); 177*b430a2bdSLongpeng g_free(s->dev.vqs); 178*b430a2bdSLongpeng vhost_dev_cleanup(&s->dev); 179*b430a2bdSLongpeng qemu_close(s->vhostfd); 180*b430a2bdSLongpeng s->vhostfd = -1; 181*b430a2bdSLongpeng } 182*b430a2bdSLongpeng 183*b430a2bdSLongpeng static void 184*b430a2bdSLongpeng vhost_vdpa_device_get_config(VirtIODevice *vdev, uint8_t *config) 185*b430a2bdSLongpeng { 186*b430a2bdSLongpeng VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); 187*b430a2bdSLongpeng 188*b430a2bdSLongpeng memcpy(config, s->config, s->config_size); 189*b430a2bdSLongpeng } 190*b430a2bdSLongpeng 191*b430a2bdSLongpeng static void 192*b430a2bdSLongpeng vhost_vdpa_device_set_config(VirtIODevice *vdev, const uint8_t *config) 193*b430a2bdSLongpeng { 194*b430a2bdSLongpeng VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); 195*b430a2bdSLongpeng int ret; 196*b430a2bdSLongpeng 197*b430a2bdSLongpeng ret = vhost_dev_set_config(&s->dev, s->config, 0, s->config_size, 198*b430a2bdSLongpeng VHOST_SET_CONFIG_TYPE_MASTER); 199*b430a2bdSLongpeng if (ret) { 200*b430a2bdSLongpeng error_report("set device config space failed"); 201*b430a2bdSLongpeng return; 202*b430a2bdSLongpeng } 203*b430a2bdSLongpeng } 204*b430a2bdSLongpeng 205*b430a2bdSLongpeng static uint64_t vhost_vdpa_device_get_features(VirtIODevice *vdev, 206*b430a2bdSLongpeng uint64_t features, 207*b430a2bdSLongpeng Error **errp) 208*b430a2bdSLongpeng { 209*b430a2bdSLongpeng VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); 210*b430a2bdSLongpeng uint64_t backend_features = s->dev.features; 211*b430a2bdSLongpeng 212*b430a2bdSLongpeng if (!virtio_has_feature(features, VIRTIO_F_IOMMU_PLATFORM)) { 213*b430a2bdSLongpeng virtio_clear_feature(&backend_features, VIRTIO_F_IOMMU_PLATFORM); 214*b430a2bdSLongpeng } 215*b430a2bdSLongpeng 216*b430a2bdSLongpeng return backend_features; 217*b430a2bdSLongpeng } 218*b430a2bdSLongpeng 219*b430a2bdSLongpeng static int vhost_vdpa_device_start(VirtIODevice *vdev, Error **errp) 220*b430a2bdSLongpeng { 221*b430a2bdSLongpeng VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); 222*b430a2bdSLongpeng BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); 223*b430a2bdSLongpeng VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 224*b430a2bdSLongpeng int i, ret; 225*b430a2bdSLongpeng 226*b430a2bdSLongpeng if (!k->set_guest_notifiers) { 227*b430a2bdSLongpeng error_setg(errp, "binding does not support guest notifiers"); 228*b430a2bdSLongpeng return -ENOSYS; 229*b430a2bdSLongpeng } 230*b430a2bdSLongpeng 231*b430a2bdSLongpeng ret = vhost_dev_enable_notifiers(&s->dev, vdev); 232*b430a2bdSLongpeng if (ret < 0) { 233*b430a2bdSLongpeng error_setg_errno(errp, -ret, "Error enabling host notifiers"); 234*b430a2bdSLongpeng return ret; 235*b430a2bdSLongpeng } 236*b430a2bdSLongpeng 237*b430a2bdSLongpeng ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, true); 238*b430a2bdSLongpeng if (ret < 0) { 239*b430a2bdSLongpeng error_setg_errno(errp, -ret, "Error binding guest notifier"); 240*b430a2bdSLongpeng goto err_host_notifiers; 241*b430a2bdSLongpeng } 242*b430a2bdSLongpeng 243*b430a2bdSLongpeng s->dev.acked_features = vdev->guest_features; 244*b430a2bdSLongpeng 245*b430a2bdSLongpeng ret = vhost_dev_start(&s->dev, vdev, false); 246*b430a2bdSLongpeng if (ret < 0) { 247*b430a2bdSLongpeng error_setg_errno(errp, -ret, "Error starting vhost"); 248*b430a2bdSLongpeng goto err_guest_notifiers; 249*b430a2bdSLongpeng } 250*b430a2bdSLongpeng s->started = true; 251*b430a2bdSLongpeng 252*b430a2bdSLongpeng /* 253*b430a2bdSLongpeng * guest_notifier_mask/pending not used yet, so just unmask 254*b430a2bdSLongpeng * everything here. virtio-pci will do the right thing by 255*b430a2bdSLongpeng * enabling/disabling irqfd. 256*b430a2bdSLongpeng */ 257*b430a2bdSLongpeng for (i = 0; i < s->dev.nvqs; i++) { 258*b430a2bdSLongpeng vhost_virtqueue_mask(&s->dev, vdev, i, false); 259*b430a2bdSLongpeng } 260*b430a2bdSLongpeng 261*b430a2bdSLongpeng return ret; 262*b430a2bdSLongpeng 263*b430a2bdSLongpeng err_guest_notifiers: 264*b430a2bdSLongpeng k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false); 265*b430a2bdSLongpeng err_host_notifiers: 266*b430a2bdSLongpeng vhost_dev_disable_notifiers(&s->dev, vdev); 267*b430a2bdSLongpeng return ret; 268*b430a2bdSLongpeng } 269*b430a2bdSLongpeng 270*b430a2bdSLongpeng static void vhost_vdpa_device_stop(VirtIODevice *vdev) 271*b430a2bdSLongpeng { 272*b430a2bdSLongpeng VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); 273*b430a2bdSLongpeng BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); 274*b430a2bdSLongpeng VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 275*b430a2bdSLongpeng int ret; 276*b430a2bdSLongpeng 277*b430a2bdSLongpeng if (!s->started) { 278*b430a2bdSLongpeng return; 279*b430a2bdSLongpeng } 280*b430a2bdSLongpeng s->started = false; 281*b430a2bdSLongpeng 282*b430a2bdSLongpeng if (!k->set_guest_notifiers) { 283*b430a2bdSLongpeng return; 284*b430a2bdSLongpeng } 285*b430a2bdSLongpeng 286*b430a2bdSLongpeng vhost_dev_stop(&s->dev, vdev, false); 287*b430a2bdSLongpeng 288*b430a2bdSLongpeng ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false); 289*b430a2bdSLongpeng if (ret < 0) { 290*b430a2bdSLongpeng error_report("vhost guest notifier cleanup failed: %d", ret); 291*b430a2bdSLongpeng return; 292*b430a2bdSLongpeng } 293*b430a2bdSLongpeng 294*b430a2bdSLongpeng vhost_dev_disable_notifiers(&s->dev, vdev); 295*b430a2bdSLongpeng } 296*b430a2bdSLongpeng 297*b430a2bdSLongpeng static void vhost_vdpa_device_set_status(VirtIODevice *vdev, uint8_t status) 298*b430a2bdSLongpeng { 299*b430a2bdSLongpeng VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); 300*b430a2bdSLongpeng bool should_start = virtio_device_started(vdev, status); 301*b430a2bdSLongpeng Error *local_err = NULL; 302*b430a2bdSLongpeng int ret; 303*b430a2bdSLongpeng 304*b430a2bdSLongpeng if (!vdev->vm_running) { 305*b430a2bdSLongpeng should_start = false; 306*b430a2bdSLongpeng } 307*b430a2bdSLongpeng 308*b430a2bdSLongpeng if (s->started == should_start) { 309*b430a2bdSLongpeng return; 310*b430a2bdSLongpeng } 311*b430a2bdSLongpeng 312*b430a2bdSLongpeng if (should_start) { 313*b430a2bdSLongpeng ret = vhost_vdpa_device_start(vdev, &local_err); 314*b430a2bdSLongpeng if (ret < 0) { 315*b430a2bdSLongpeng error_reportf_err(local_err, "vhost-vdpa-device: start failed: "); 316*b430a2bdSLongpeng } 317*b430a2bdSLongpeng } else { 318*b430a2bdSLongpeng vhost_vdpa_device_stop(vdev); 319*b430a2bdSLongpeng } 320*b430a2bdSLongpeng } 321*b430a2bdSLongpeng 322*b430a2bdSLongpeng static Property vhost_vdpa_device_properties[] = { 323*b430a2bdSLongpeng DEFINE_PROP_STRING("vhostdev", VhostVdpaDevice, vhostdev), 324*b430a2bdSLongpeng DEFINE_PROP_UINT16("queue-size", VhostVdpaDevice, queue_size, 0), 325*b430a2bdSLongpeng DEFINE_PROP_END_OF_LIST(), 326*b430a2bdSLongpeng }; 327*b430a2bdSLongpeng 328*b430a2bdSLongpeng static const VMStateDescription vmstate_vhost_vdpa_device = { 329*b430a2bdSLongpeng .name = "vhost-vdpa-device", 330*b430a2bdSLongpeng .minimum_version_id = 1, 331*b430a2bdSLongpeng .version_id = 1, 332*b430a2bdSLongpeng .fields = (VMStateField[]) { 333*b430a2bdSLongpeng VMSTATE_VIRTIO_DEVICE, 334*b430a2bdSLongpeng VMSTATE_END_OF_LIST() 335*b430a2bdSLongpeng }, 336*b430a2bdSLongpeng }; 337*b430a2bdSLongpeng 338*b430a2bdSLongpeng static void vhost_vdpa_device_class_init(ObjectClass *klass, void *data) 339*b430a2bdSLongpeng { 340*b430a2bdSLongpeng DeviceClass *dc = DEVICE_CLASS(klass); 341*b430a2bdSLongpeng VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); 342*b430a2bdSLongpeng 343*b430a2bdSLongpeng device_class_set_props(dc, vhost_vdpa_device_properties); 344*b430a2bdSLongpeng dc->desc = "VDPA-based generic device assignment"; 345*b430a2bdSLongpeng dc->vmsd = &vmstate_vhost_vdpa_device; 346*b430a2bdSLongpeng set_bit(DEVICE_CATEGORY_MISC, dc->categories); 347*b430a2bdSLongpeng vdc->realize = vhost_vdpa_device_realize; 348*b430a2bdSLongpeng vdc->unrealize = vhost_vdpa_device_unrealize; 349*b430a2bdSLongpeng vdc->get_config = vhost_vdpa_device_get_config; 350*b430a2bdSLongpeng vdc->set_config = vhost_vdpa_device_set_config; 351*b430a2bdSLongpeng vdc->get_features = vhost_vdpa_device_get_features; 352*b430a2bdSLongpeng vdc->set_status = vhost_vdpa_device_set_status; 353*b430a2bdSLongpeng } 354*b430a2bdSLongpeng 355*b430a2bdSLongpeng static void vhost_vdpa_device_instance_init(Object *obj) 356*b430a2bdSLongpeng { 357*b430a2bdSLongpeng VhostVdpaDevice *s = VHOST_VDPA_DEVICE(obj); 358*b430a2bdSLongpeng 359*b430a2bdSLongpeng device_add_bootindex_property(obj, &s->bootindex, "bootindex", 360*b430a2bdSLongpeng NULL, DEVICE(obj)); 361*b430a2bdSLongpeng } 362*b430a2bdSLongpeng 363*b430a2bdSLongpeng static const TypeInfo vhost_vdpa_device_info = { 364*b430a2bdSLongpeng .name = TYPE_VHOST_VDPA_DEVICE, 365*b430a2bdSLongpeng .parent = TYPE_VIRTIO_DEVICE, 366*b430a2bdSLongpeng .instance_size = sizeof(VhostVdpaDevice), 367*b430a2bdSLongpeng .class_init = vhost_vdpa_device_class_init, 368*b430a2bdSLongpeng .instance_init = vhost_vdpa_device_instance_init, 369*b430a2bdSLongpeng }; 370*b430a2bdSLongpeng 371*b430a2bdSLongpeng static void register_vhost_vdpa_device_type(void) 372*b430a2bdSLongpeng { 373*b430a2bdSLongpeng type_register_static(&vhost_vdpa_device_info); 374*b430a2bdSLongpeng } 375*b430a2bdSLongpeng 376*b430a2bdSLongpeng type_init(register_vhost_vdpa_device_type); 377