/* * Copyright (C) 2019-2022 Intel Corporation. * * SPDX-License-Identifier: BSD-3-Clause * */ #include #include #include #include #include #include #include #include #if OPENSSL_VERSION_NUMBER >= 0x30000000L #include #endif #include #include #include #include #include #include #include #include #include "dm.h" #include "pci_core.h" #include "virtio.h" #include "acpi.h" /* I2c adapter virtualization architecture * * +-----------------------------+ * | ACRN DM | * | +----------------------+ | virtqueue * | | |<--+-----------+ * | | virtio i2c mediator | | | * | | | | | * | +--+-----+-----+-------+ | | * +-----+-----+-----+-----------+ | * User space +-------+ | +-----------+ | * v v v | * +---------+----+ +-----+--------+ +-----+------+ | +-----------+ * ---+ /dev/i2c-0 +--+ /dev/i2c-1 +--+ /dev/i2c-n +----+--+User VM: | * | | | | | | |/dev/i2c-n | * +----------+---+ +-------+------+ +-----+------+ | +-----+-----+ * Kernel space v v v | v * +-----+-------+ +----+--------+ +----+--------+ | +-----+------------+ * |i2c adapter 0| |i2c adapter 1| |i2c adapter n| +->|User VM: | * | | | | | |virtio i2c adapter| * +-----+-------+ +-------------+ +-------------+ +------------------+ * --------------+----------------------------------------- * Hardware +----------+ * | | * bus 0v v .... * +-----+----+ +----+-----+ * |i2c client| |i2c client| .... * +----------+ +----------+ */ static int virtio_i2c_debug=0; #define VIRTIO_I2C_PREF "virtio_i2c: " #define DPRINTF(fmt, args...) \ do { if (virtio_i2c_debug) pr_info(VIRTIO_I2C_PREF fmt, ##args); } while (0) #define WPRINTF(fmt, args...) pr_err(VIRTIO_I2C_PREF fmt, ##args) #define MAX_NODE_NAME_LEN 20 #define MAX_I2C_VDEV 128 #define MAX_NATIVE_I2C_ADAPTER 16 #define I2C_MSG_OK 0 #define I2C_MSG_ERR 1 #define I2C_NO_DEV 2 #define I2C_NO_FLAGS 0 #define VIRTIO_I2C_FLAGS_FAIL_NEXT 1 << 0 #define VIRTIO_I2C_FLAGS_M_RD 1 << 1 #define VIRTIO_I2C_F_ZERO_LENGTH_REQUEST 0 #define VIRTIO_I2C_HOSTCAPS (1UL << VIRTIO_F_VERSION_1) | \ (1UL << VIRTIO_I2C_F_ZERO_LENGTH_REQUEST) static int acpi_i2c_adapter_num = 0; static void acpi_add_i2c_adapter(struct pci_vdev *dev, int i2c_bus); static void acpi_add_cam1(struct pci_vdev *dev, int i2c_bus); static void acpi_add_cam2(struct pci_vdev *dev, int i2c_bus); static void acpi_add_hdac(struct pci_vdev *dev, int i2c_bus); static void acpi_add_default(struct pci_vdev *dev, int i2c_bus); struct acpi_node { char node_name[MAX_NODE_NAME_LEN]; void (*add_node_fn)(struct pci_vdev *, int); }; static struct acpi_node acpi_node_table[] = { /* cam1, cam2 and hdac is dump from MRB board */ {"cam1", acpi_add_cam1}, {"cam2", acpi_add_cam2}, {"hdac", acpi_add_hdac}, {"default", acpi_add_default}, }; struct virtio_i2c_out_hdr { uint16_t addr; /* client address */ uint16_t padding; uint32_t flags; }; struct virtio_i2c_in_hdr { uint8_t status; }; struct native_i2c_adapter { int fd; int bus; bool i2cdev_enable[MAX_I2C_VDEV]; }; /* * Per-device struct */ struct virtio_i2c { struct virtio_base base; pthread_mutex_t mtx; struct native_i2c_adapter *native_adapter[MAX_NATIVE_I2C_ADAPTER]; int native_adapter_num; uint16_t adapter_map[MAX_I2C_VDEV]; char acpi_nodes[MAX_I2C_VDEV][MAX_NODE_NAME_LEN]; struct virtio_vq_info vq; char ident[256]; pthread_t req_tid; pthread_mutex_t req_mtx; pthread_cond_t req_cond; int in_process; int closing; }; static void virtio_i2c_reset(void *); static void virtio_i2c_notify(void *, struct virtio_vq_info *); static struct virtio_ops virtio_i2c_ops = { "virtio_i2c", /* our name */ 1, /* we support 1 virtqueue */ 0, /* config reg size */ virtio_i2c_reset, /* reset */ virtio_i2c_notify, /* device-wide qnotify */ NULL, /* read PCI config */ NULL, /* write PCI config */ NULL, /* apply negotiated features */ NULL, /* called on guest set status */ }; static void acpi_add_i2c_adapter(struct pci_vdev *dev, int i2c_bus) { dsdt_line("Device (I2C%d)", i2c_bus); dsdt_line("{"); dsdt_line(" Name (_ADR, 0x%04X%04X)", dev->slot, dev->func); dsdt_line(" Name (_DDN, \"Intel(R) I2C Controller #%d\")", i2c_bus); dsdt_line(" Name (_UID, One)"); dsdt_line(" Name (LINK, \"\\\\_SB.PCI%d.I2C%d\")", dev->bus, i2c_bus); dsdt_line(" Name (RBUF, ResourceTemplate ()"); dsdt_line(" {"); dsdt_line(" })"); dsdt_line(" Name (IC0S, 0x00061A80)"); dsdt_line(" Name (_DSD, Package (0x02)"); dsdt_line(" {"); dsdt_line(" ToUUID (\"daffd814-6eba-4d8c-8a91-bc9bbf4aa301\")" " ,"); dsdt_line(" Package (0x01)"); dsdt_line(" {"); dsdt_line(" Package (0x02)"); dsdt_line(" {"); dsdt_line(" \"clock-frequency\", "); dsdt_line(" IC0S"); dsdt_line(" }"); dsdt_line(" }"); dsdt_line(" })"); dsdt_line(""); dsdt_line("}"); } static void acpi_add_cam1(struct pci_vdev *dev, int i2c_bus) { dsdt_line("Scope(I2C%d)", i2c_bus); dsdt_line("{"); dsdt_line(" Device (CAM1)"); dsdt_line(" {"); dsdt_line(" Name (_ADR, Zero) // _ADR: Address"); dsdt_line(" Name (_HID, \"ADV7481A\") // _HID: Hardware ID"); dsdt_line(" Name (_CID, \"ADV7481A\") // _CID: Compatible ID"); dsdt_line(" Name (_UID, One) // _UID: Unique ID"); dsdt_line(" Method (_CRS, 0, Serialized)"); dsdt_line(" {"); dsdt_line(" Name (SBUF, ResourceTemplate ()"); dsdt_line(" {"); dsdt_line(" GpioIo (Exclusive, PullDefault, 0x0000, " "0x0000, IoRestrictionInputOnly,"); dsdt_line(" \"\\\\_SB.GPO0\", 0x00, " "ResourceConsumer, ,"); dsdt_line(" )"); dsdt_line(" { // Pin list"); dsdt_line(" 0x001E"); dsdt_line(" }"); dsdt_line(" I2cSerialBusV2 (0x0070, " "ControllerInitiated, 0x00061A80,"); dsdt_line(" AddressingMode7Bit, " "\"\\\\_SB.PCI%d.I2C%d\",", dev->bus, i2c_bus); dsdt_line(" 0x00, ResourceConsumer, , Exclusive,"); dsdt_line(" )"); dsdt_line(" })"); dsdt_line(" Return (SBUF)"); dsdt_line(" }"); dsdt_line(" Method (_DSM, 4, NotSerialized)"); dsdt_line(" {"); dsdt_line(" If ((Arg0 == ToUUID (" "\"377ba76a-f390-4aff-ab38-9b1bf33a3015\")))"); dsdt_line(" {"); dsdt_line(" Return (\"ADV7481A\")"); dsdt_line(" }"); dsdt_line(""); dsdt_line(" If ((Arg0 == ToUUID (" "\"ea3b7bd8-e09b-4239-ad6e-ed525f3f26ab\")))"); dsdt_line(" {"); dsdt_line(" Return (0x40)"); dsdt_line(" }"); dsdt_line(""); dsdt_line(" If ((Arg0 == ToUUID (" "\"8dbe2651-70c1-4c6f-ac87-a37cb46e4af6\")))"); dsdt_line(" {"); dsdt_line(" Return (0xFF)"); dsdt_line(" }"); dsdt_line(""); dsdt_line(" If ((Arg0 == ToUUID (" "\"26257549-9271-4ca4-bb43-c4899d5a4881\")))"); dsdt_line(" {"); dsdt_line(" If (Arg2 == One)"); dsdt_line(" {"); dsdt_line(" Return (0x02)"); dsdt_line(" }"); dsdt_line(" If (Arg2 == 0x02)"); dsdt_line(" {"); dsdt_line(" Return (0x02001000)"); dsdt_line(" }"); dsdt_line(" If (Arg2 == 0x03)"); dsdt_line(" {"); dsdt_line(" Return (0x02000E01)"); dsdt_line(" }"); dsdt_line(" }"); dsdt_line(" Return (Zero)"); dsdt_line(" }"); dsdt_line(" }"); dsdt_line("}"); } static void acpi_add_cam2(struct pci_vdev *dev, int i2c_bus) { dsdt_line("Scope(I2C%d)", i2c_bus); dsdt_line("{"); dsdt_line(" Device (CAM2)"); dsdt_line(" {"); dsdt_line(" Name (_ADR, Zero) // _ADR: Address"); dsdt_line(" Name (_HID, \"ADV7481B\") // _HID: Hardware ID"); dsdt_line(" Name (_CID, \"ADV7481B\") // _CID: Compatible ID"); dsdt_line(" Name (_UID, One) // _UID: Unique ID"); dsdt_line(" Method (_CRS, 0, Serialized)"); dsdt_line(" {"); dsdt_line(" Name (SBUF, ResourceTemplate ()"); dsdt_line(" {"); dsdt_line(" GpioIo (Exclusive, PullDefault, 0x000, " "0x0000, IoRestrictionInputOnly,"); dsdt_line(" \"\\\\_SB.GPO0\", 0x00, " "ResourceConsumer, ,"); dsdt_line(" )"); dsdt_line(" { // Pin list"); dsdt_line(" 0x001E"); dsdt_line(" }"); dsdt_line(" I2cSerialBusV2 (0x0071, " "ControllerInitiated, 0x00061A80,"); dsdt_line(" AddressingMode7Bit, " "\"\\\\_SB.PCI%d.I2C%d\",", dev->bus, i2c_bus); dsdt_line(" 0x00, ResourceConsumer, , Exclusive,"); dsdt_line(" )"); dsdt_line(" })"); dsdt_line(" Return (SBUF)"); dsdt_line(" }"); dsdt_line(" Method (_DSM, 4, NotSerialized) "); dsdt_line(" {"); dsdt_line(" If ((Arg0 == ToUUID (" "\"377ba76a-f390-4aff-ab38-9b1bf33a3015\")))"); dsdt_line(" {"); dsdt_line(" Return (\"ADV7481B\")"); dsdt_line(" }"); dsdt_line(""); dsdt_line(" If ((Arg0 == ToUUID (" "\"ea3b7bd8-e09b-4239-ad6e-ed525f3f26ab\")))"); dsdt_line(" {"); dsdt_line(" Return (0x14)"); dsdt_line(" }"); dsdt_line(""); dsdt_line(" If ((Arg0 == ToUUID (" "\"8dbe2651-70c1-4c6f-ac87-a37cb46e4af6\")))"); dsdt_line(" {"); dsdt_line(" Return (0xFF)"); dsdt_line(" }"); dsdt_line(""); dsdt_line(" If ((Arg0 == ToUUID (" "\"26257549-9271-4ca4-bb43-c4899d5a4881\")))"); dsdt_line(" {"); dsdt_line(" If (Arg2 == One)"); dsdt_line(" {"); dsdt_line(" Return (0x02)"); dsdt_line(" }"); dsdt_line(" If (Arg2 == 0x02)"); dsdt_line(" {"); dsdt_line(" Return (0x02001000)"); dsdt_line(" }"); dsdt_line(" If (Arg2 == 0x03)"); dsdt_line(" {"); dsdt_line(" Return (0x02000E01)"); dsdt_line(" }"); dsdt_line(" }"); dsdt_line(" Return (Zero)"); dsdt_line(" }"); dsdt_line(" }"); dsdt_line(""); dsdt_line("}"); } static void acpi_add_hdac(struct pci_vdev *dev, int i2c_bus) { dsdt_line("Scope(I2C%d)", i2c_bus); dsdt_line("{"); dsdt_line(" Device (HDAC)"); dsdt_line(" {"); dsdt_line(" Name (_HID, \"INT34C3\") // _HID: Hardware ID"); dsdt_line(" Name (_CID, \"INT34C3\") // _CID: Compatible ID"); dsdt_line(" Name (_DDN, \"Intel(R) Smart Sound Technology " "Audio Codec\") // _DDN: DOS Device Name"); dsdt_line(" Name (_UID, One) // _UID: Unique ID"); dsdt_line(" Method (_INI, 0, NotSerialized)"); dsdt_line(" {"); dsdt_line(" }"); dsdt_line(""); dsdt_line(" Method (_CRS, 0, NotSerialized)"); dsdt_line(" {"); dsdt_line(" Name (SBFB, ResourceTemplate ()"); dsdt_line(" {"); dsdt_line(" I2cSerialBusV2 (0x006C, " "ControllerInitiated, 0x00061A80,"); dsdt_line(" AddressingMode7Bit, " "\"\\\\_SB.PCI%d.I2C%d\",", dev->bus, i2c_bus); dsdt_line(" 0x00, ResourceConsumer, , Exclusive,"); dsdt_line(" )"); dsdt_line(" })"); dsdt_line(" Name (SBFI, ResourceTemplate ()"); dsdt_line(" {"); dsdt_line(" })"); dsdt_line(" Return (ConcatenateResTemplate (SBFB, SBFI))"); dsdt_line(" }"); dsdt_line(""); dsdt_line(" Method (_STA, 0, NotSerialized) // _STA: Status"); dsdt_line(" {"); dsdt_line(" Return (0x0F)"); dsdt_line(" }"); dsdt_line(" }"); dsdt_line("}"); } static void acpi_add_default(struct pci_vdev *dev, int i2c_bus) { /* Add nothing */ } static bool native_client_access_ok(struct native_i2c_adapter *adapter, uint16_t addr) { if (ioctl(adapter->fd, I2C_SLAVE, addr) < 0) { if (errno == EBUSY) { WPRINTF("i2c_core: client device %x is busy!\n", addr); } else { WPRINTF("i2c_core: client device %d is not exsit!\n", addr); } return false; } return true; } static struct native_i2c_adapter * native_adapter_find(struct virtio_i2c *vi2c, uint16_t addr) { int idx; if (addr < MAX_I2C_VDEV && ((idx = vi2c->adapter_map[addr]) != 0)) { return vi2c->native_adapter[idx - 1]; } return NULL; } static uint8_t native_adapter_proc(struct virtio_i2c *vi2c, struct i2c_msg *msg) { int ret; uint16_t addr; struct i2c_rdwr_ioctl_data work_queue; struct native_i2c_adapter *adapter; uint8_t status; addr = msg->addr; adapter = native_adapter_find(vi2c, addr); if (!adapter) { DPRINTF("%s: could not find device for addr %x\n", __func__, msg->addr); return I2C_MSG_ERR; } work_queue.nmsgs = 1; work_queue.msgs = msg; ret = ioctl(adapter->fd, I2C_RDWR, &work_queue); if (ret < 0) status = I2C_MSG_ERR; else status = I2C_MSG_OK; if (msg->len) DPRINTF("i2c_core: i2c msg: flags=0x%x, addr=0x%x, len=0x%x buf=%x\n", msg->flags, msg->addr, msg->len, msg->buf[0]); else DPRINTF("i2c_core: i2c msg: flags=0x%x, addr=0x%x, len=0x%x\n", msg->flags, msg->addr, msg->len); return status; } static struct native_i2c_adapter * native_adapter_create(int bus, uint16_t client_addr[], int n_client) { int fd; struct native_i2c_adapter *native_adapter; char native_path[20]; int i; unsigned long funcs; if (bus < 0) return NULL; native_adapter = calloc(1, sizeof(struct native_i2c_adapter)); if (native_adapter == NULL) { WPRINTF("i2c_core: failed to calloc struct virtio_i2c_vdev"); return NULL; } snprintf(native_path, sizeof(native_path), "/dev/i2c-%d", bus); native_path[sizeof(native_path) - 1] = '\0'; fd = open(native_path, O_RDWR); if (fd < 0) { WPRINTF("virtio_i2c: failed to open %s\n", native_path); goto fail; } if (ioctl(fd, I2C_FUNCS, &funcs) < 0) { WPRINTF("virtio_i2c: failed to get the funcs \n"); goto fail; } if (!(funcs & I2C_FUNC_I2C)) { WPRINTF("virtio_i2c: this adapter %s doesn't support I2C_FUNC_I2C mode " "(plain i2c-level commands)!\n", native_path); goto fail; } native_adapter->fd = fd; native_adapter->bus = bus; for (i = 0; i < n_client; i++) { if (client_addr[i]) { if (native_client_access_ok(native_adapter, client_addr[i])) { if (native_adapter->i2cdev_enable[client_addr[i]]) { WPRINTF("client addr 0x%x repeat, not allowed.\n", client_addr[i]); goto fail; } native_adapter->i2cdev_enable[client_addr[i]] = true; DPRINTF("virtio_i2c: add client 0x%x\n", client_addr[i]); } else { goto fail; } } } return native_adapter; fail: free(native_adapter); return NULL; } static void native_adapter_remove(struct virtio_i2c *vi2c) { int i; struct native_i2c_adapter *native_adapter; for (i = 0; i < MAX_NATIVE_I2C_ADAPTER; i++) { native_adapter = vi2c->native_adapter[i]; if (native_adapter) { if (native_adapter->fd > 0) close(native_adapter->fd); free(native_adapter); vi2c->native_adapter[i] = NULL; } } } static void virtio_i2c_req_stop(struct virtio_i2c *vi2c) { void *jval; pthread_mutex_lock(&vi2c->req_mtx); vi2c->closing = 1; pthread_cond_broadcast(&vi2c->req_cond); pthread_mutex_unlock(&vi2c->req_mtx); pthread_join(vi2c->req_tid, &jval); } static void * virtio_i2c_proc_thread(void *arg) { struct virtio_i2c *vi2c = arg; struct virtio_vq_info *vq = &vi2c->vq; struct iovec iov[3]; uint16_t idx, flags[3]; struct i2c_msg msg; int n; bool fail_next = false; struct virtio_i2c_out_hdr *out_hdr; struct virtio_i2c_in_hdr *in_hdr; for (;;) { pthread_mutex_lock(&vi2c->req_mtx); vi2c->in_process = 0; while (!vq_has_descs(vq) && !vi2c->closing) pthread_cond_wait(&vi2c->req_cond, &vi2c->req_mtx); if (vi2c->closing) { pthread_mutex_unlock(&vi2c->req_mtx); return NULL; } vi2c->in_process = 1; pthread_mutex_unlock(&vi2c->req_mtx); do { n = vq_getchain(vq, &idx, iov, 3, flags); if (n < 2 || n > 3) { WPRINTF("virtio_i2c_proc: failed to get iov from virtqueue\n"); continue; } out_hdr = iov[0].iov_base; /* From v1.2-cs01 virtio spec, 7-bit address is defined as: * ----------------------------------------------------------- * Bits |15|14|13|12|11|10|9|8|7 |6 |5 |4 |3 |2 |1 |0| * -------------+--+--+--+--+--+--+-+-+--+--+--+--+--+--+--+-+ * 7-bit address|0 |0 |0 |0 |0 |0 |0|0|A6|A5|A4|A3|A2|A1|A0|0| * -------------+--+--+--+--+--+--+-+-+--+--+--+--+--+--+--+-+ */ msg.addr = out_hdr->addr >> 1; if (out_hdr->flags & VIRTIO_I2C_FLAGS_M_RD) msg.flags = I2C_M_RD; else msg.flags = I2C_NO_FLAGS; if (n == 3) { msg.buf = iov[1].iov_base; msg.len = iov[1].iov_len; in_hdr = iov[2].iov_base; } else { // this is a zero-length request msg.buf = NULL; msg.len = 0; in_hdr = iov[1].iov_base; } /* * From v1.2-cs01 virtio spec: * VIRTIO_I2C_FLAGS_FAIL_NEXT(0) is used to group the requests. For a group requests, * a driver clears this bit on the final request and sets it on the other requests. * If this bit is set and a device fails to process the current request, it needs to * fail the next request instead of attempting to execute it. */ if (!fail_next) { in_hdr->status = native_adapter_proc(vi2c, &msg); if ((out_hdr->flags & VIRTIO_I2C_FLAGS_FAIL_NEXT) && (in_hdr->status == I2C_MSG_ERR)) fail_next = true; } else { in_hdr->status = I2C_MSG_ERR; } vq_relchain(vq, idx, 1); if (!(out_hdr->flags & VIRTIO_I2C_FLAGS_FAIL_NEXT)) fail_next = false; } while (vq_has_descs(vq)); vq_endchains(vq, 0); } } static int virtio_i2c_map(struct virtio_i2c *vi2c) { int i, client_addr; struct native_i2c_adapter *native_adapter; /* * Flatten the map for client address and native adapter to the array: * * adapter_map[MAX_I2C_VDEV]: * * Native Adapter | adapter2 | none | adapter1 | adapter3 | none | none| (val) * |----------|-------|----------|----------|------|-----| * Slave Address | addr 1 | none | addr 2 | addr 3 | none | none| (idx) * |<-----------------------MAX_I2C_VDEV---------------->| */ for (i = 0; i < vi2c->native_adapter_num; i++) { native_adapter = vi2c->native_adapter[i]; for (client_addr = 0; client_addr < MAX_I2C_VDEV; client_addr++) { if (native_adapter->i2cdev_enable[client_addr]) { if (vi2c->adapter_map[client_addr]) { WPRINTF("client addr %x repeat, not support!\n", client_addr); return -1; } /* As 0 is the initiate value, + 1 for index */ vi2c->adapter_map[client_addr] = i + 1; DPRINTF("client:%d -> native adapter: %d\n", client_addr, native_adapter->bus); } } } return 0; } static int virtio_i2c_parse(struct virtio_i2c *vi2c, char *optstr) { char *cp, *t, *dsdt_str, *p; uint16_t client_addr[MAX_I2C_VDEV]; int addr, bus, n_adapter, n_client; /* * virtio-i2c,:]>[:]>], * [:]>[:[@]]] * * bus (dec): native adatper bus number. * e.g. 2 for /dev/i2c-2 * client_addr (hex): address for native client device * e.g. 0x1C or 1C * @: node is the acpi node name defined in acpi_node_table[] * e.g. @cam1 means adding 'cam1' node to dsdt table. * * Note: client address can not repeat. */ n_adapter = 0; while (optstr != NULL) { cp = strsep(&optstr, ","); /* * :[:]... */ n_client = 0; bus = -1; while (cp != NULL && *cp !='\0') { if (*cp == ':') cp++; if (bus == -1) { if (dm_strtoi(cp, &t, 10, &bus) || bus < 0) return -1; } else { if (dm_strtoi(cp, &t, 16, &addr) || addr < 0) return -1; if (n_client > MAX_I2C_VDEV) { WPRINTF("too many devices, only support %d \n", MAX_I2C_VDEV); return -1; } client_addr[n_client] = (uint16_t)(addr & (MAX_I2C_VDEV - 1)); p = &vi2c->acpi_nodes[client_addr[n_client]][0]; if (t != NULL && *t == '@') { t++; dsdt_str = strsep(&t, ":,"); snprintf(p, MAX_NODE_NAME_LEN, "%s", dsdt_str); } else { snprintf(p, MAX_NODE_NAME_LEN, "default"); } DPRINTF("native i2c adapter %d:0x%x (%s)\n", bus, client_addr[n_client], p); n_client++; } cp = t; } if (n_adapter >= MAX_NATIVE_I2C_ADAPTER) { WPRINTF("too many adapter, only support %d \n", MAX_NATIVE_I2C_ADAPTER); return -1; } vi2c->native_adapter[n_adapter] = native_adapter_create(bus, client_addr, n_client); if (!vi2c->native_adapter[n_adapter]) return -1; n_adapter++; } vi2c->native_adapter_num = n_adapter; return 0; } static void virtio_i2c_dsdt(struct pci_vdev *dev) { int i, j, node_num; struct acpi_node *anode; int i2c_bus; int found; struct virtio_i2c *vi2c = (struct virtio_i2c *) dev->arg; /* i2c bus number in acpi start from 0 */ i2c_bus = acpi_i2c_adapter_num; /* add i2c adapter */ acpi_add_i2c_adapter(dev, i2c_bus); DPRINTF("add dsdt for i2c adapter %d\n", i2c_bus); /* add client devices */ node_num = sizeof(acpi_node_table) / sizeof(struct acpi_node); for (i = 0; i < MAX_I2C_VDEV; i++) { if (!native_adapter_find(vi2c, i)) continue; found = 0; for (j = 0; j < node_num; j++) { anode = &acpi_node_table[j]; if (!strncmp(anode->node_name, vi2c->acpi_nodes[i], sizeof(anode->node_name))) { found = 1; if (anode->add_node_fn) { anode->add_node_fn(dev, i2c_bus); DPRINTF("add dsdt for %s \n", anode->node_name); } } } if (!found) WPRINTF("cannot find acpi node info for %s \n", vi2c->acpi_nodes[i]); } acpi_i2c_adapter_num++; } static void virtio_i2c_reset(void *vdev) { struct virtio_i2c *vi2c = vdev; DPRINTF("device reset requested !\n"); virtio_reset_dev(&vi2c->base); } static void virtio_i2c_notify(void *vdev, struct virtio_vq_info *vq) { struct virtio_i2c *vi2c = vdev; if (!vq_has_descs(vq)) return; pthread_mutex_lock(&vi2c->req_mtx); if (!vi2c->in_process) pthread_cond_signal(&vi2c->req_cond); pthread_mutex_unlock(&vi2c->req_mtx); } static int virtio_i2c_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) { u_char digest[16]; struct virtio_i2c *vi2c; pthread_mutexattr_t attr; int rc = -1; vi2c = calloc(1, sizeof(struct virtio_i2c)); if (!vi2c) { WPRINTF("calloc returns NULL\n"); return -ENOMEM; } if (virtio_i2c_parse(vi2c, opts)) { WPRINTF("failed to parse parameters %s \n", opts); goto mtx_fail; } if (virtio_i2c_map(vi2c)) goto mtx_fail; /* init mutex attribute properly to avoid deadlock */ rc = pthread_mutexattr_init(&attr); if (rc) { WPRINTF("mutexattr init failed with erro %d!\n", rc); goto mtx_fail; } rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); if (rc) { WPRINTF("mutexattr_settype failed with " "error %d!\n", rc); goto mtx_fail; } rc = pthread_mutex_init(&vi2c->mtx, &attr); if (rc) { WPRINTF("pthread_mutex_init failed with " "error %d!\n", rc); goto mtx_fail; } /* init virtio struct and virtqueues */ virtio_linkup(&vi2c->base, &virtio_i2c_ops, vi2c, dev, &vi2c->vq, BACKEND_VBSU); vi2c->base.mtx = &vi2c->mtx; vi2c->base.device_caps = VIRTIO_I2C_HOSTCAPS; vi2c->vq.qsize = 64; vi2c->native_adapter_num = 0; #if OPENSSL_VERSION_NUMBER >= 0x30000000L EVP_MD_CTX *mdctx = EVP_MD_CTX_new(); EVP_DigestInit_ex(mdctx, EVP_md5(), NULL); EVP_DigestUpdate(mdctx, "vi2c", strlen("vi2c")); EVP_DigestFinal_ex(mdctx, digest, NULL); EVP_MD_CTX_free(mdctx); #else MD5_CTX mdctx; MD5_Init(&mdctx); MD5_Update(&mdctx, "vi2c", strlen("vi2c")); MD5_Final(digest, &mdctx); #endif rc = snprintf(vi2c->ident, sizeof(vi2c->ident), "ACRN--%02X%02X-%02X%02X-%02X%02X", digest[0], digest[1], digest[2], digest[3], digest[4], digest[5]); if (rc < 0) { WPRINTF("create ident failed"); goto fail; } if (rc >= sizeof(vi2c->ident)) { WPRINTF("ident too long\n"); } pci_set_cfgdata16(dev, PCIR_DEVICE, VIRTIO_DEV_I2C); pci_set_cfgdata16(dev, PCIR_VENDOR, VIRTIO_VENDOR); pci_set_cfgdata8(dev, PCIR_CLASS, PCIS_SERIALBUS_SMBUS); pci_set_cfgdata16(dev, PCIR_SUBDEV_0, VIRTIO_TYPE_I2C); pci_set_cfgdata16(dev, PCIR_SUBVEND_0, VIRTIO_VENDOR); if (virtio_interrupt_init(&vi2c->base, virtio_uses_msix())) { WPRINTF("failed to init interrupt"); rc = -1; goto fail; } rc = virtio_set_modern_bar(&vi2c->base, false); vi2c->in_process = 0; vi2c->closing = 0; pthread_mutex_init(&vi2c->req_mtx, NULL); pthread_cond_init(&vi2c->req_cond, NULL); pthread_create(&vi2c->req_tid, NULL, virtio_i2c_proc_thread, vi2c); pthread_setname_np(vi2c->req_tid, "virtio-i2c"); return 0; fail: pthread_mutex_destroy(&vi2c->mtx); mtx_fail: native_adapter_remove(vi2c); free(vi2c); return rc; } static void virtio_i2c_deinit(struct vmctx *ctx, struct pci_vdev *dev, char *opts) { struct virtio_i2c *vi2c; if (dev->arg) { DPRINTF("deinit\n"); vi2c = (struct virtio_i2c *) dev->arg; virtio_i2c_req_stop(vi2c); native_adapter_remove(vi2c); pthread_mutex_destroy(&vi2c->req_mtx); pthread_mutex_destroy(&vi2c->mtx); virtio_i2c_reset(vi2c); acpi_i2c_adapter_num--; assert(acpi_i2c_adapter_num >= 0); free(vi2c); dev->arg = NULL; } } struct pci_vdev_ops pci_ops_virtio_i2c = { .class_name = "virtio-i2c", .vdev_init = virtio_i2c_init, .vdev_deinit = virtio_i2c_deinit, .vdev_barwrite = virtio_pci_write, .vdev_barread = virtio_pci_read, .vdev_write_dsdt = virtio_i2c_dsdt, }; DEFINE_PCI_DEVTYPE(pci_ops_virtio_i2c);