smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
	    const char *path, const char *ea_name, const void *ea_value,
	    const __u16 ea_value_len, const struct nls_table *nls_codepage,
	    struct cifs_sb_info *cifs_sb)
{
	struct cifs_ses *ses = tcon->ses;
	struct TCP_Server_Info *server = cifs_pick_channel(ses);
	__le16 *utf16_path = NULL;
	int ea_name_len = strlen(ea_name);
	int flags = CIFS_CP_CREATE_CLOSE_OP;
	int len;
	struct smb_rqst rqst[3];
	int resp_buftype[3];
	struct kvec rsp_iov[3];
	struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
	struct cifs_open_parms oparms;
	__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
	struct cifs_fid fid;
	struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE];
	unsigned int size[1];
	void *data[1];
	struct smb2_file_full_ea_info *ea = NULL;
	struct kvec close_iov[1];
	struct smb2_query_info_rsp *rsp;
	int rc, used_len = 0;

	if (smb3_encryption_required(tcon))
		flags |= CIFS_TRANSFORM_REQ;

	if (ea_name_len > 255)
		return -EINVAL;

	utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
	if (!utf16_path)
		return -ENOMEM;

	memset(rqst, 0, sizeof(rqst));
	resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
	memset(rsp_iov, 0, sizeof(rsp_iov));

	if (ses->server->ops->query_all_EAs) {
		if (!ea_value) {
			rc = ses->server->ops->query_all_EAs(xid, tcon, path,
							     ea_name, NULL, 0,
							     cifs_sb);
			if (rc == -ENODATA)
				goto sea_exit;
		} else {
			/* If we are adding a attribute we should first check
			 * if there will be enough space available to store
			 * the new EA. If not we should not add it since we
			 * would not be able to even read the EAs back.
			 */
			rc = smb2_query_info_compound(xid, tcon, path,
				      FILE_READ_EA,
				      FILE_FULL_EA_INFORMATION,
				      SMB2_O_INFO_FILE,
				      CIFSMaxBufSize -
				      MAX_SMB2_CREATE_RESPONSE_SIZE -
				      MAX_SMB2_CLOSE_RESPONSE_SIZE,
				      &rsp_iov[1], &resp_buftype[1], cifs_sb);
			if (rc == 0) {
				rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
				used_len = le32_to_cpu(rsp->OutputBufferLength);
			}
			free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
			resp_buftype[1] = CIFS_NO_BUFFER;
			memset(&rsp_iov[1], 0, sizeof(rsp_iov[1]));
			rc = 0;

			/* Use a fudge factor of 256 bytes in case we collide
			 * with a different set_EAs command.
			 */
			if(CIFSMaxBufSize - MAX_SMB2_CREATE_RESPONSE_SIZE -
			   MAX_SMB2_CLOSE_RESPONSE_SIZE - 256 <
			   used_len + ea_name_len + ea_value_len + 1) {
				rc = -ENOSPC;
				goto sea_exit;
			}
		}
	}

	/* Open */
	memset(&open_iov, 0, sizeof(open_iov));
	rqst[0].rq_iov = open_iov;
	rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;

	memset(&oparms, 0, sizeof(oparms));
	oparms.tcon = tcon;
	oparms.desired_access = FILE_WRITE_EA;
	oparms.disposition = FILE_OPEN;
	oparms.create_options = cifs_create_options(cifs_sb, 0);
	oparms.fid = &fid;
	oparms.reconnect = false;

	rc = SMB2_open_init(tcon, server,
			    &rqst[0], &oplock, &oparms, utf16_path);
	if (rc)
		goto sea_exit;
	smb2_set_next_command(tcon, &rqst[0]);

	/* Set Info */
	memset(&si_iov, 0, sizeof(si_iov));
	rqst[1].rq_iov = si_iov;
	rqst[1].rq_nvec = 1;

	len = sizeof(*ea) + ea_name_len + ea_value_len + 1;
	ea = kzalloc(len, GFP_KERNEL);
	if (ea == NULL) {
		rc = -ENOMEM;
		goto sea_exit;
	}

	ea->ea_name_length = ea_name_len;
	ea->ea_value_length = cpu_to_le16(ea_value_len);
	memcpy(ea->ea_data, ea_name, ea_name_len + 1);
	memcpy(ea->ea_data + ea_name_len + 1, ea_value, ea_value_len);

	size[0] = len;
	data[0] = ea;

	rc = SMB2_set_info_init(tcon, server,
				&rqst[1], COMPOUND_FID,
				COMPOUND_FID, current->tgid,
				FILE_FULL_EA_INFORMATION,
				SMB2_O_INFO_FILE, 0, data, size);
	smb2_set_next_command(tcon, &rqst[1]);
	smb2_set_related(&rqst[1]);

	/* Close */
	memset(&close_iov, 0, sizeof(close_iov));
	rqst[2].rq_iov = close_iov;
	rqst[2].rq_nvec = 1;
	rc = SMB2_close_init(tcon, server,
			     &rqst[2], COMPOUND_FID, COMPOUND_FID, false);
	smb2_set_related(&rqst[2]);

	rc = compound_send_recv(xid, ses, server,
				flags, 3, rqst,
				resp_buftype, rsp_iov);
	/* no need to bump num_remote_opens because handle immediately closed */

 sea_exit:
	kfree(ea);
	kfree(utf16_path);
	SMB2_open_free(&rqst[0]);
	SMB2_set_info_free(&rqst[1]);
	SMB2_close_free(&rqst[2]);
	free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
	free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
	free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base);
	return rc;
}


smb2_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset,
	       __u64 length, __u32 type, int lock, int unlock, bool wait)
{
	if (unlock && !lock)
		type = SMB2_LOCKFLAG_UNLOCK;
	return SMB2_lock(xid, tlink_tcon(cfile->tlink),
			 cfile->fid.persistent_fid, cfile->fid.volatile_fid,
			 current->tgid, length, offset, type, wait);
}


receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid,
		       int *num_mids)
{
	char *buf = server->smallbuf;
	struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf;
	unsigned int npages;
	struct page **pages;
	unsigned int len;
	unsigned int buflen = server->pdu_size;
	int rc;
	int i = 0;
	struct smb2_decrypt_work *dw;

	*num_mids = 1;
	len = min_t(unsigned int, buflen, server->vals->read_rsp_size +
		sizeof(struct smb2_transform_hdr)) - HEADER_SIZE(server) + 1;

	rc = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1, len);
	if (rc < 0)
		return rc;
	server->total_read += rc;

	len = le32_to_cpu(tr_hdr->OriginalMessageSize) -
		server->vals->read_rsp_size;
	npages = DIV_ROUND_UP(len, PAGE_SIZE);

	pages = kmalloc_array(npages, sizeof(struct page *), GFP_KERNEL);
	if (!pages) {
		rc = -ENOMEM;
		goto discard_data;
	}

	for (; i < npages; i++) {
		pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM);
		if (!pages[i]) {
			rc = -ENOMEM;
			goto discard_data;
		}
	}

	/* read read data into pages */
	rc = read_data_into_pages(server, pages, npages, len);
	if (rc)
		goto free_pages;

	rc = cifs_discard_remaining_data(server);
	if (rc)
		goto free_pages;

	/*
	 * For large reads, offload to different thread for better performance,
	 * use more cores decrypting which can be expensive
	 */

	if ((server->min_offload) && (server->in_flight > 1) &&
	    (server->pdu_size >= server->min_offload)) {
		dw = kmalloc(sizeof(struct smb2_decrypt_work), GFP_KERNEL);
		if (dw == NULL)
			goto non_offloaded_decrypt;

		dw->buf = server->smallbuf;
		server->smallbuf = (char *)cifs_small_buf_get();

		INIT_WORK(&dw->decrypt, smb2_decrypt_offload);

		dw->npages = npages;
		dw->server = server;
		dw->ppages = pages;
		dw->len = len;
		queue_work(decrypt_wq, &dw->decrypt);
		*num_mids = 0; /* worker thread takes care of finding mid */
		return -1;
	}

non_offloaded_decrypt:
	rc = decrypt_raw_data(server, buf, server->vals->read_rsp_size,
			      pages, npages, len, false);
	if (rc)
		goto free_pages;

	*mid = smb2_find_mid(server, buf);
	if (*mid == NULL)
		cifs_dbg(FYI, "mid not found\n");
	else {
		cifs_dbg(FYI, "mid found\n");
		(*mid)->decrypted = true;
		rc = handle_read_data(server, *mid, buf,
				      server->vals->read_rsp_size,
				      pages, npages, len, false);
		if (rc >= 0) {
			if (server->ops->is_network_name_deleted) {
				server->ops->is_network_name_deleted(buf,
								server);
			}
		}
	}

free_pages:
	for (i = i - 1; i >= 0; i--)
		put_page(pages[i]);
	kfree(pages);
	return rc;
discard_data:
	cifs_discard_remaining_data(server);
	goto free_pages;
}


void close_cached_dir(struct cached_fid *cfid)
{
	mutex_lock(&cfid->fid_mutex);
	kref_put(&cfid->refcount, smb2_close_cached_fid);
	mutex_unlock(&cfid->fid_mutex);
}


static const char *req_handler_field(request_rec *r)
{
    return r->handler;
}


static int lua_ap_some_auth_required(request_rec *r)
{
    return ap_some_auth_required(r);
}


static int lua_apr_touch(lua_State *L)
{
    request_rec     *r;
    const char      *path;
    apr_status_t    status;
    apr_time_t      mtime;

    r = ap_lua_check_request_rec(L, 1);
    luaL_checktype(L, 2, LUA_TSTRING);
    path = lua_tostring(L, 2);
    mtime = (apr_time_t)luaL_optnumber(L, 3, (lua_Number)apr_time_now());
    status = apr_file_mtime_set(path, mtime, r->pool);
    lua_pushboolean(L, (status == 0));
    return 1;
}


static int lua_ap_set_document_root(lua_State *L)
{
    request_rec    *r;
    const char     *root;

    luaL_checktype(L, 1, LUA_TUSERDATA);
    luaL_checktype(L, 2, LUA_TSTRING);
    r = ap_lua_check_request_rec(L, 1);
    root = lua_tostring(L, 2);
    ap_set_document_root(r, root);
    return 0;
}


static void read_cfg_tree(lua_State *L, request_rec *r, ap_directive_t *rcfg) {
    int x = 0;
    const char* value;
    ap_directive_t *cfg;
    lua_newtable(L);

    for (cfg = rcfg; cfg; cfg = cfg->next) {
        x++;
        lua_pushnumber(L, x);
        lua_newtable(L);
        value = apr_psprintf(r->pool, "%s %s", cfg->directive, cfg->args);
        lua_pushstring(L, "directive");
        lua_pushstring(L, value);
        lua_settable(L, -3);
        lua_pushstring(L, "file");
        lua_pushstring(L, cfg->filename);
        lua_settable(L, -3);
        lua_pushstring(L, "line");
        lua_pushnumber(L, cfg->line_num);
        lua_settable(L, -3);
        if (cfg->first_child) {
            lua_pushstring(L, "children");
            read_cfg_tree(L, r, cfg->first_child);
            lua_settable(L, -3);
        }
        lua_settable(L, -3);
    }
}


void ap_lua_push_connection(lua_State *L, conn_rec *c)
{
    req_table_t* t;
    lua_boxpointer(L, c);
    luaL_getmetatable(L, "Apache2.Connection");
    lua_setmetatable(L, -2);
    luaL_getmetatable(L, "Apache2.Connection");

    t = apr_pcalloc(c->pool, sizeof(req_table_t));
    t->t = c->notes;
    t->r = NULL;
    t->n = "notes";
    ap_lua_push_apr_table(L, t);
    lua_setfield(L, -2, "notes");

    lua_pushstring(L, c->client_ip);
    lua_setfield(L, -2, "client_ip");

    lua_pop(L, 1);
}


static int req_emerg(lua_State *L)
{
    return req_log_at(L, APLOG_EMERG);
}


static int req_puts(lua_State *L)
{
    request_rec *r = ap_lua_check_request_rec(L, 1);

    int argc = lua_gettop(L);
    int i;

    for (i = 2; i <= argc; i++) {
        ap_rputs(luaL_checkstring(L, i), r);
    }
    return 0;
}


static int req_remaining_field(request_rec *r)
{
    return r->remaining;
}


static const char* lua_ap_get_server_name(request_rec* r)
{
    const char *name;
    name = ap_get_server_name(r);
    return name ? name : "localhost";
}


static int req_err(lua_State *L)
{
    return req_log_at(L, APLOG_ERR);
}


static const char *req_ap_auth_type_field(request_rec *r)
{
    return r->ap_auth_type;
}


static int req_escape_html(lua_State *L)
{
    request_rec *r = ap_lua_check_request_rec(L, 1);
    const char *s = luaL_checkstring(L, 2);
    lua_pushstring(L, ap_escape_html(r->pool, s));
    return 1;
}


static int lua_apr_sha1(lua_State *L)
{
    unsigned char digest[APR_SHA1_DIGESTSIZE];
    apr_sha1_ctx_t sha1;
    const char     *buffer;
    char           *result;
    size_t len;
    request_rec    *r;

    r = ap_lua_check_request_rec(L, 1);
    luaL_checktype(L, 2, LUA_TSTRING);
    result = apr_pcalloc(r->pool, sizeof(digest) * 2 + 1);
    buffer = lua_tolstring(L, 2, &len);
    apr_sha1_init(&sha1);
    apr_sha1_update(&sha1, buffer, len);
    apr_sha1_final(digest, &sha1);

    ap_bin2hex(digest, sizeof(digest), result);
    lua_pushstring(L, result);
    return 1;
}


static void *cgroup_procs_next(struct seq_file *s, void *v, loff_t *pos)
{
	struct kernfs_open_file *of = s->private;
	struct css_task_iter *it = of->priv;

	if (pos)
		(*pos)++;

	return css_task_iter_next(it);
}


  void initialize() override {
    config_helper_.addConfigModifier(
        [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager&
                hcm) -> void {
          ConfigHelper::setConnectConfig(hcm, false, false,
                                         downstream_protocol_ == Http::CodecType::HTTP3);
        });

    HttpProtocolIntegrationTest::initialize();
  }


void Filter::onGenericPoolFailure(ConnectionPool::PoolFailureReason reason,
                                  Upstream::HostDescriptionConstSharedPtr host) {
  generic_conn_pool_.reset();
  read_callbacks_->upstreamHost(host);
  getStreamInfo().upstreamInfo()->setUpstreamHost(host);

  switch (reason) {
  case ConnectionPool::PoolFailureReason::Overflow:
  case ConnectionPool::PoolFailureReason::LocalConnectionFailure:
    upstream_callbacks_->onEvent(Network::ConnectionEvent::LocalClose);
    break;
  case ConnectionPool::PoolFailureReason::RemoteConnectionFailure:
    upstream_callbacks_->onEvent(Network::ConnectionEvent::RemoteClose);
    break;
  case ConnectionPool::PoolFailureReason::Timeout:
    onConnectTimeout();
    break;
  }
}


void Filter::UpstreamCallbacks::onEvent(Network::ConnectionEvent event) {
  if (event == Network::ConnectionEvent::Connected) {
    return;
  }
  if (drainer_ == nullptr) {
    parent_->onUpstreamEvent(event);
  } else {
    drainer_->onEvent(event);
  }
}


UpstreamDrainManager::~UpstreamDrainManager() {
  // If connections aren't closed before they are destructed an ASSERT fires,
  // so cancel all pending drains, which causes the connections to be closed.
  if (!drainers_.empty()) {
    auto& dispatcher = drainers_.begin()->second->dispatcher();
    while (!drainers_.empty()) {
      auto begin = drainers_.begin();
      Drainer* key = begin->first;
      begin->second->cancelDrain();

      // cancelDrain() should cause that drainer to be removed from drainers_.
      // ASSERT so that we don't end up in an infinite loop.
      ASSERT(drainers_.find(key) == drainers_.end());
    }

    // This destructor is run when shutting down `ThreadLocal`. The destructor of some objects use
    // earlier `ThreadLocal` slots (for accessing the runtime snapshot) so they must run before that
    // slot is destructed. Clear the list to enforce that ordering.
    dispatcher.clearDeferredDeleteList();
  }
}


TEST_P(TcpTunnelingIntegrationTest, TransferEncodingHeaderIgnoredHttp1) {
  if (upstreamProtocol() != Http::CodecType::HTTP1) {
    return;
  }
  initialize();

  // Start a connection, and verify the upgrade headers are received upstream.
  tcp_client_ = makeTcpConnection(lookupPort("tcp_proxy"));
  // Using raw connection to be able to set Transfer-encoding header.
  FakeRawConnectionPtr fake_upstream_connection;
  ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection));
  std::string data;
  ASSERT_TRUE(fake_upstream_connection->waitForData(
      FakeRawConnection::waitForInexactMatch("\r\n\r\n"), &data));
  ASSERT_THAT(data, testing::HasSubstr("CONNECT host.com:80 HTTP/1.1"));

  // Send upgrade headers downstream, fully establishing the connection.
  ASSERT_TRUE(
      fake_upstream_connection->write("HTTP/1.1 200 OK\r\nTransfer-encoding: chunked\r\n\r\n"));

  // Now send some data and close the TCP client.
  ASSERT_TRUE(tcp_client_->write("hello"));
  ASSERT_TRUE(
      fake_upstream_connection->waitForData(FakeRawConnection::waitForInexactMatch("hello")));

  // Close connections.
  ASSERT_TRUE(fake_upstream_connection->close());
  ASSERT_TRUE(fake_upstream_connection->waitForDisconnect());
  tcp_client_->close();
}


TEST_P(TcpTunnelingIntegrationTest, ContentLengthHeaderIgnoredHttp1) {
  if (upstreamProtocol() != Http::CodecType::HTTP1) {
    return;
  }
  initialize();

  // Start a connection, and verify the upgrade headers are received upstream.
  tcp_client_ = makeTcpConnection(lookupPort("tcp_proxy"));
  ASSERT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_));
  ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_));
  ASSERT_TRUE(upstream_request_->waitForHeadersComplete());

  // Send upgrade headers downstream, including content-length that must be
  // ignored.
  default_response_headers_.setStatus(enumToInt(Http::Code::IMUsed));
  default_response_headers_.setContentLength(10);
  upstream_request_->encodeHeaders(default_response_headers_, false);

  // Send data from upstream to downstream.
  upstream_request_->encodeData(12, false);
  ASSERT_TRUE(tcp_client_->waitForData(12));

  // Now send some data and close the TCP client.
  ASSERT_TRUE(tcp_client_->write("hello", false));
  tcp_client_->close();
  ASSERT_TRUE(upstream_request_->waitForData(*dispatcher_, 5));
  ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect());
}


RouteConstSharedPtr Config::getRouteFromEntries(Network::Connection& connection) {
  if (weighted_clusters_.empty()) {
    return getRegularRouteFromEntries(connection);
  }
  return WeightedClusterUtil::pickCluster(weighted_clusters_, total_cluster_weight_,
                                          random_generator_.random(), false);
}


void Filter::onConnectTimeout() {
  ENVOY_CONN_LOG(debug, "connect timeout", read_callbacks_->connection());
  read_callbacks_->upstreamHost()->outlierDetector().putResult(
      Upstream::Outlier::Result::LocalOriginTimeout);
  getStreamInfo().setResponseFlag(StreamInfo::ResponseFlag::UpstreamConnectionFailure);

  // Raise LocalClose, which will trigger a reconnect if needed/configured.
  upstream_callbacks_->onEvent(Network::ConnectionEvent::LocalClose);
}


void Filter::onMaxDownstreamConnectionDuration() {
  ENVOY_CONN_LOG(debug, "max connection duration reached", read_callbacks_->connection());
  getStreamInfo().setResponseFlag(StreamInfo::ResponseFlag::DurationTimeout);
  config_->stats().max_downstream_connection_duration_.inc();
  read_callbacks_->connection().close(Network::ConnectionCloseType::NoFlush);
}


static int virtio_serial_load(QEMUFile *f, void *opaque, int version_id)
{
    if (version_id > 3) {
        return -EINVAL;
    }

    /* The virtio device */
    return virtio_load(VIRTIO_DEVICE(opaque), f, version_id);
}


static void discard_vq_data(VirtQueue *vq, VirtIODevice *vdev)
{
    VirtQueueElement elem;

    if (!virtio_queue_ready(vq)) {
        return;
    }
    while (virtqueue_pop(vq, &elem)) {
        virtqueue_push(vq, &elem, 0);
    }
    virtio_notify(vdev, vq);
}


static void virtio_serial_device_realize(DeviceState *dev, Error **errp)
{
    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
    VirtIOSerial *vser = VIRTIO_SERIAL(dev);
    uint32_t i, max_supported_ports;

    if (!vser->serial.max_virtserial_ports) {
        error_setg(errp, "Maximum number of serial ports not specified");
        return;
    }

    /* Each port takes 2 queues, and one pair is for the control queue */
    max_supported_ports = VIRTIO_QUEUE_MAX / 2 - 1;

    if (vser->serial.max_virtserial_ports > max_supported_ports) {
        error_setg(errp, "maximum ports supported: %u", max_supported_ports);
        return;
    }

    /* We don't support emergency write, skip it for now. */
    /* TODO: cleaner fix, depending on host features. */
    virtio_init(vdev, "virtio-serial", VIRTIO_ID_CONSOLE,
                offsetof(struct virtio_console_config, emerg_wr));

    /* Spawn a new virtio-serial bus on which the ports will ride as devices */
    qbus_create_inplace(&vser->bus, sizeof(vser->bus), TYPE_VIRTIO_SERIAL_BUS,
                        dev, vdev->bus_name);
    qbus_set_hotplug_handler(BUS(&vser->bus), DEVICE(vser), errp);
    vser->bus.vser = vser;
    QTAILQ_INIT(&vser->ports);

    vser->bus.max_nr_ports = vser->serial.max_virtserial_ports;
    vser->ivqs = g_malloc(vser->serial.max_virtserial_ports
                          * sizeof(VirtQueue *));
    vser->ovqs = g_malloc(vser->serial.max_virtserial_ports
                          * sizeof(VirtQueue *));

    /* Add a queue for host to guest transfers for port 0 (backward compat) */
    vser->ivqs[0] = virtio_add_queue(vdev, 128, handle_input);
    /* Add a queue for guest to host transfers for port 0 (backward compat) */
    vser->ovqs[0] = virtio_add_queue(vdev, 128, handle_output);

    /* TODO: host to guest notifications can get dropped
     * if the queue fills up. Implement queueing in host,
     * this might also make it possible to reduce the control
     * queue size: as guest preposts buffers there,
     * this will save 4Kbyte of guest memory per entry. */

    /* control queue: host to guest */
    vser->c_ivq = virtio_add_queue(vdev, 32, control_in);
    /* control queue: guest to host */
    vser->c_ovq = virtio_add_queue(vdev, 32, control_out);

    for (i = 1; i < vser->bus.max_nr_ports; i++) {
        /* Add a per-port queue for host to guest transfers */
        vser->ivqs[i] = virtio_add_queue(vdev, 128, handle_input);
        /* Add a per-per queue for guest to host transfers */
        vser->ovqs[i] = virtio_add_queue(vdev, 128, handle_output);
    }

    vser->ports_map = g_malloc0(((vser->serial.max_virtserial_ports + 31) / 32)
        * sizeof(vser->ports_map[0]));
    /*
     * Reserve location 0 for a console port for backward compat
     * (old kernel, new qemu)
     */
    mark_port_added(vser, 0);

    vser->post_load = NULL;

    /*
     * Register for the savevm section with the virtio-console name
     * to preserve backward compat
     */
    register_savevm(dev, "virtio-console", -1, 3, virtio_serial_save,
                    virtio_serial_load, vser);

    QLIST_INSERT_HEAD(&vserdevices.devices, vser, next);
}


static void virtio_serial_post_load_timer_cb(void *opaque)
{
    uint32_t i;
    VirtIOSerial *s = VIRTIO_SERIAL(opaque);
    VirtIOSerialPort *port;
    uint8_t host_connected;
    VirtIOSerialPortClass *vsc;

    if (!s->post_load) {
        return;
    }
    for (i = 0 ; i < s->post_load->nr_active_ports; ++i) {
        port = s->post_load->connected[i].port;
        host_connected = s->post_load->connected[i].host_connected;
        if (host_connected != port->host_connected) {
            /*
             * We have to let the guest know of the host connection
             * status change
             */
            send_control_event(s, port->id, VIRTIO_CONSOLE_PORT_OPEN,
                               port->host_connected);
        }
        vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port);
        if (vsc->set_guest_connected) {
            vsc->set_guest_connected(port, port->guest_connected);
        }
    }
    g_free(s->post_load->connected);
    timer_free(s->post_load->timer);
    g_free(s->post_load);
    s->post_load = NULL;
}


static size_t write_to_port(VirtIOSerialPort *port,
                            const uint8_t *buf, size_t size)
{
    VirtQueueElement elem;
    VirtQueue *vq;
    size_t offset;

    vq = port->ivq;
    if (!virtio_queue_ready(vq)) {
        return 0;
    }

    offset = 0;
    while (offset < size) {
        size_t len;

        if (!virtqueue_pop(vq, &elem)) {
            break;
        }

        len = iov_from_buf(elem.in_sg, elem.in_num, 0,
                           buf + offset, size - offset);
        offset += len;

        virtqueue_push(vq, &elem, len);
    }

    virtio_notify(VIRTIO_DEVICE(port->vser), vq);
    return offset;
}


static void virtio_serial_device_unrealize(DeviceState *dev, Error **errp)
{
    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
    VirtIOSerial *vser = VIRTIO_SERIAL(dev);

    QLIST_REMOVE(vser, next);

    unregister_savevm(dev, "virtio-console", vser);

    g_free(vser->ivqs);
    g_free(vser->ovqs);
    g_free(vser->ports_map);
    if (vser->post_load) {
        g_free(vser->post_load->connected);
        timer_del(vser->post_load->timer);
        timer_free(vser->post_load->timer);
        g_free(vser->post_load);
    }
    virtio_cleanup(vdev);
}


static void virtser_port_device_realize(DeviceState *dev, Error **errp)
{
    VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev);
    VirtIOSerialPortClass *vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port);
    VirtIOSerialBus *bus = VIRTIO_SERIAL_BUS(qdev_get_parent_bus(dev));
    int max_nr_ports;
    bool plugging_port0;
    Error *err = NULL;

    port->vser = bus->vser;
    port->bh = qemu_bh_new(flush_queued_data_bh, port);

    assert(vsc->have_data);

    /*
     * Is the first console port we're seeing? If so, put it up at
     * location 0. This is done for backward compatibility (old
     * kernel, new qemu).
     */
    plugging_port0 = vsc->is_console && !find_port_by_id(port->vser, 0);

    if (find_port_by_id(port->vser, port->id)) {
        error_setg(errp, "virtio-serial-bus: A port already exists at id %u",
                   port->id);
        return;
    }

    if (port->name != NULL && find_port_by_name(port->name)) {
        error_setg(errp, "virtio-serial-bus: A port already exists by name %s",
                   port->name);
        return;
    }

    if (port->id == VIRTIO_CONSOLE_BAD_ID) {
        if (plugging_port0) {
            port->id = 0;
        } else {
            port->id = find_free_port_id(port->vser);
            if (port->id == VIRTIO_CONSOLE_BAD_ID) {
                error_setg(errp, "virtio-serial-bus: Maximum port limit for "
                                 "this device reached");
                return;
            }
        }
    }

    max_nr_ports = port->vser->serial.max_virtserial_ports;
    if (port->id >= max_nr_ports) {
        error_setg(errp, "virtio-serial-bus: Out-of-range port id specified, "
                         "max. allowed: %u", max_nr_ports - 1);
        return;
    }

    vsc->realize(dev, &err);
    if (err != NULL) {
        error_propagate(errp, err);
        return;
    }

    port->elem.out_num = 0;
}


ssize_t virtio_serial_write(VirtIOSerialPort *port, const uint8_t *buf,
                            size_t size)
{
    if (!port || !port->host_connected || !port->guest_connected) {
        return 0;
    }
    return write_to_port(port, buf, size);
}


static bool is_entry_flag(RzFlagItem *f) {
	return f->space && !strcmp(f->space->name, RZ_FLAGS_FS_SYMBOLS) && rz_str_startswith(f->name, "entry.");
}


RZ_API RZ_BORROW RzPVector *rz_analysis_function_get_vars_used_at(RzAnalysisFunction *fcn, ut64 op_addr) {
	rz_return_val_if_fail(fcn, NULL);
	return ht_up_find(fcn->inst_vars, (st64)op_addr - (st64)fcn->addr, NULL);
}


RZ_IPI void rz_core_analysis_function_until(RzCore *core, ut64 addr_end) {
	rz_return_if_fail(core->offset <= addr_end);
	ut64 addr = core->offset;
	int depth = 1;
	ut64 a, b;
	const char *c;
	a = rz_config_get_i(core->config, "analysis.from");
	b = rz_config_get_i(core->config, "analysis.to");
	c = rz_config_get(core->config, "analysis.limits");
	rz_config_set_i(core->config, "analysis.from", addr);
	rz_config_set_i(core->config, "analysis.to", addr_end);
	rz_config_set(core->config, "analysis.limits", "true");

	RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, addr, 0);
	if (fcn) {
		rz_analysis_function_resize(fcn, addr_end - addr);
	}
	rz_core_analysis_fcn(core, addr, UT64_MAX,
		RZ_ANALYSIS_REF_TYPE_NULL, depth);
	fcn = rz_analysis_get_fcn_in(core->analysis, addr, 0);
	if (fcn) {
		rz_analysis_function_resize(fcn, addr_end - addr);
	}
	rz_config_set_i(core->config, "analysis.from", a);
	rz_config_set_i(core->config, "analysis.to", b);
	rz_config_set(core->config, "analysis.limits", c ? c : "");
}


RZ_API ut64 rz_core_analysis_fcn_list_size(RzCore *core) {
	RzAnalysisFunction *fcn;
	RzListIter *iter;
	ut64 total = 0;

	rz_list_foreach (core->analysis->fcns, iter, fcn) {
		total += rz_analysis_function_realsize(fcn);
	}
	rz_cons_printf("%" PFMT64u "\n", total);
	return total;
}


bool print_arch_hint_cb(ut64 addr, RZ_NULLABLE const char *arch, void *user) {
	HintNode *node = RZ_NEW0(HintNode);
	if (!node) {
		return false;
	}
	node->addr = addr;
	node->type = HINT_NODE_ARCH;
	node->arch = arch;
	rz_rbtree_insert(user, &addr, &node->rb, hint_node_cmp, NULL);
	return true;
}


static char *get_title(ut64 addr) {
	return rz_str_newf("0x%" PFMT64x, addr);
}


static void rz_analysis_set_stringrefs(RzCore *core, RzAnalysisFunction *fcn) {
	RzListIter *iter;
	RzAnalysisXRef *xref;
	RzList *xrefs = rz_analysis_function_get_xrefs_from(fcn);
	rz_list_foreach (xrefs, iter, xref) {
		if (xref->type == RZ_ANALYSIS_REF_TYPE_DATA &&
			rz_bin_is_string(core->bin, xref->to)) {
			rz_analysis_xrefs_set(core->analysis, xref->from, xref->to, RZ_ANALYSIS_REF_TYPE_STRING);
		}
	}
	rz_list_free(xrefs);
}


RZ_API RzGraph *rz_core_analysis_importxrefs(RzCore *core) {
	RzBinObject *obj = rz_bin_cur_object(core->bin);
	bool va = core->io->va || core->bin->is_debugger;

	RzListIter *iter;
	RzBinImport *imp;
	if (!obj) {
		return NULL;
	}
	RzGraph *graph = rz_graph_new();
	if (!graph) {
		return NULL;
	}
	rz_list_foreach (obj->imports, iter, imp) {
		RzBinSymbol *sym = rz_bin_object_get_symbol_of_import(obj, imp);
		ut64 addr = sym ? (va ? rz_bin_object_get_vaddr(obj, sym->paddr, sym->vaddr) : sym->paddr) : UT64_MAX;
		if (addr && addr != UT64_MAX) {
			add_single_addr_xrefs(core, addr, graph);
		} else {
			rz_graph_add_node_info(graph, imp->name, NULL, 0);
		}
	}
	return graph;
}


static bool add_arch_platform_flag_comment_cb(void *user, const ut64 addr, const void *v) {
	if (!v) {
		return false;
	}
	RzArchPlatformItem *item = (RzArchPlatformItem *)v;
	RzCore *core = (RzCore *)user;
	rz_flag_space_push(core->flags, RZ_FLAGS_FS_PLATFORM_PORTS);
	rz_flag_set(core->flags, item->name, addr, 1);
	rz_flag_space_pop(core->flags);
	if (item->comment) {
		rz_core_meta_comment_add(core, item->comment, addr);
	}
	return true;
}


static int fcn_list_default(RzCore *core, RzList *fcns, bool quiet) {
	RzListIter *iter;
	RzAnalysisFunction *fcn;
	rz_list_foreach (fcns, iter, fcn) {
		__fcn_print_default(core, fcn, quiet);
		if (quiet) {
			rz_cons_newline();
		}
	}
	return 0;
}


static void loganalysis(ut64 from, ut64 to, int depth) {
	rz_cons_clear_line(1);
	eprintf("0x%08" PFMT64x " > 0x%08" PFMT64x " %d\r", from, to, depth);
}


bool function_argument_type_derive(RZ_NULLABLE const RzCallable *callable, int arg_num, RzType **type, char **name) {
	rz_return_val_if_fail(type && name, false);
	if (!callable) {
		return false;
	}
	if (arg_num >= rz_pvector_len(callable->args)) {
		return false;
	}
	RzCallableArg *arg = *rz_pvector_index_ptr(callable->args, arg_num);
	if (!arg) {
		return false;
	}
	*type = rz_type_clone(arg->type);
	*name = strdup(arg->name);
	return true;
}


RZ_API int rz_analysis_var_count(RzAnalysis *a, RzAnalysisFunction *fcn, int kind, int type) {
	// type { local: 0, arg: 1 };
	RzList *list = rz_analysis_var_list(a, fcn, kind);
	RzAnalysisVar *var;
	RzListIter *iter;
	int count[2] = {
		0
	};
	rz_list_foreach (list, iter, var) {
		if (kind == RZ_ANALYSIS_VAR_KIND_REG) {
			count[1]++;
			continue;
		}
		count[var->isarg]++;
	}
	rz_list_free(list);
	return count[type];
}


static inline bool var_same_kind(RzAnalysisVar *a, RzAnalysisVar *b) {
	return a->kind == b->kind && a->isarg == b->isarg;
}


static int core_analysis_followptr(RzCore *core, int type, ut64 at, ut64 ptr, ut64 ref, int code, int depth) {
	// SLOW Operation try to reduce as much as possible
	if (!ptr) {
		return false;
	}
	if (ref == UT64_MAX || ptr == ref) {
		const RzAnalysisXRefType t = code ? type ? type : RZ_ANALYSIS_REF_TYPE_CODE : RZ_ANALYSIS_REF_TYPE_DATA;
		rz_analysis_xrefs_set(core->analysis, at, ptr, t);
		return true;
	}
	if (depth < 1) {
		return false;
	}
	int wordsize = (int)(core->analysis->bits / 8);
	ut64 dataptr;
	if (!rz_io_read_i(core->io, ptr, &dataptr, wordsize, false)) {
		// eprintf ("core_analysis_followptr: Cannot read word at destination\n");
		return false;
	}
	return core_analysis_followptr(core, type, at, dataptr, ref, code, depth - 1);
}


RZ_API void rz_core_analysis_hint_print(RzAnalysis *a, ut64 addr, int mode) {
	RBTree tree = NULL;
	ut64 hint_addr = UT64_MAX;
	const char *arch = rz_analysis_hint_arch_at(a, addr, &hint_addr);
	if (hint_addr != UT64_MAX) {
		print_arch_hint_cb(hint_addr, arch, &tree);
	}
	int bits = rz_analysis_hint_bits_at(a, addr, &hint_addr);
	if (hint_addr != UT64_MAX) {
		print_bits_hint_cb(hint_addr, bits, &tree);
	}
	const RzVector *addr_hints = rz_analysis_addr_hints_at(a, addr);
	if (addr_hints) {
		print_addr_hint_cb(addr, addr_hints, &tree);
	}
	print_hint_tree(tree, mode);
	rz_rbtree_free(tree, hint_node_free, NULL);
}


RZ_API RzGraph *rz_core_analysis_codexrefs(RzCore *core, ut64 addr) {
	RzGraph *graph = rz_graph_new();
	if (!graph) {
		return NULL;
	}
	add_single_addr_xrefs(core, addr, graph);
	return graph;
}


RZ_API void rz_core_analysis_callgraph(RzCore *core, ut64 addr, int fmt) {
	const char *font = rz_config_get(core->config, "graph.font");
	int is_html = rz_cons_singleton()->is_html;
	bool refgraph = rz_config_get_i(core->config, "graph.refs");
	RzListIter *iter, *iter2;
	int usenames = rz_config_get_i(core->config, "graph.json.usenames");
	;
	RzAnalysisFunction *fcni;
	RzAnalysisXRef *fcnr;
	PJ *pj = NULL;

	ut64 from = rz_config_get_i(core->config, "graph.from");
	ut64 to = rz_config_get_i(core->config, "graph.to");

	switch (fmt) {
	case RZ_GRAPH_FORMAT_JSON:
		pj = pj_new();
		if (!pj) {
			return;
		}
		pj_a(pj);
		break;
	case RZ_GRAPH_FORMAT_GML:
	case RZ_GRAPH_FORMAT_GMLFCN:
		rz_cons_printf("graph\n[\n"
			       "hierarchic  1\n"
			       "label  \"\"\n"
			       "directed  1\n");
		break;
	case RZ_GRAPH_FORMAT_DOT:
		if (!is_html) {
			const char *gv_edge = rz_config_get(core->config, "graph.gv.edge");
			char *gv_node = strdup(rz_config_get(core->config, "graph.gv.node"));
			const char *gv_grph = rz_config_get(core->config, "graph.gv.graph");
			const char *gv_spline = rz_config_get(core->config, "graph.gv.spline");
			if (!gv_edge || !*gv_edge) {
				gv_edge = "arrowhead=\"normal\" style=bold weight=2";
			}
			if (!gv_node || !*gv_node) {
				free(gv_node);
				gv_node = rz_str_newf("penwidth=4 fillcolor=white style=filled fontname=\"%s Bold\" fontsize=14 shape=box", font);
			}
			if (!gv_grph || !*gv_grph) {
				gv_grph = "bgcolor=azure";
			}
			if (!gv_spline || !*gv_spline) {
				// ortho for bbgraph and curved for callgraph
				gv_spline = "splines=\"curved\"";
			}
			rz_cons_printf("digraph code {\n"
				       "rankdir=LR;\n"
				       "outputorder=edgesfirst;\n"
				       "graph [%s fontname=\"%s\" %s];\n"
				       "node [%s];\n"
				       "edge [%s];\n",
				gv_grph, font, gv_spline,
				gv_node, gv_edge);
			free(gv_node);
		}
		break;
	}
	ut64 base = UT64_MAX;
	int iteration = 0;
repeat:
	rz_list_foreach (core->analysis->fcns, iter, fcni) {
		if (base == UT64_MAX) {
			base = fcni->addr;
		}
		if (from != UT64_MAX && fcni->addr < from) {
			continue;
		}
		if (to != UT64_MAX && fcni->addr > to) {
			continue;
		}
		if (addr != UT64_MAX && addr != fcni->addr) {
			continue;
		}
		RzList *xrefs = rz_analysis_function_get_xrefs_from(fcni);
		RzList *calls = rz_list_new();
		// TODO: maybe fcni->calls instead ?
		rz_list_foreach (xrefs, iter2, fcnr) {
			//  TODO: tail calll jumps are also calls
			if (fcnr->type == 'C' && rz_list_find(calls, fcnr, (RzListComparator)RzAnalysisRef_cmp) == NULL) {
				rz_list_append(calls, fcnr);
			}
		}
		if (rz_list_empty(calls)) {
			rz_list_free(xrefs);
			rz_list_free(calls);
			continue;
		}
		switch (fmt) {
		case RZ_GRAPH_FORMAT_NO:
			rz_cons_printf("0x%08" PFMT64x "\n", fcni->addr);
			break;
		case RZ_GRAPH_FORMAT_GML:
		case RZ_GRAPH_FORMAT_GMLFCN: {
			RzFlagItem *flag = rz_flag_get_i(core->flags, fcni->addr);
			if (iteration == 0) {
				char *msg = flag ? strdup(flag->name) : rz_str_newf("0x%08" PFMT64x, fcni->addr);
				rz_cons_printf("  node [\n"
					       "  id  %" PFMT64d "\n"
					       "    label  \"%s\"\n"
					       "  ]\n",
					fcni->addr - base, msg);
				free(msg);
			}
			break;
		}
		case RZ_GRAPH_FORMAT_JSON:
			pj_o(pj);
			if (usenames) {
				pj_ks(pj, "name", fcni->name);
			} else {
				char fcni_addr[20];
				snprintf(fcni_addr, sizeof(fcni_addr) - 1, "0x%08" PFMT64x, fcni->addr);
				pj_ks(pj, "name", fcni_addr);
			}
			pj_kn(pj, "size", rz_analysis_function_linear_size(fcni));
			pj_ka(pj, "imports");
			break;
		case RZ_GRAPH_FORMAT_DOT:
			rz_cons_printf("  \"0x%08" PFMT64x "\" "
				       "[label=\"%s\""
				       " URL=\"%s/0x%08" PFMT64x "\"];\n",
				fcni->addr, fcni->name,
				fcni->name, fcni->addr);
		}
		rz_list_foreach (calls, iter2, fcnr) {
			// TODO: display only code or data refs?
			RzFlagItem *flag = rz_flag_get_i(core->flags, fcnr->to);
			char *fcnr_name = (flag && flag->name) ? flag->name : rz_str_newf("unk.0x%" PFMT64x, fcnr->to);
			switch (fmt) {
			case RZ_GRAPH_FORMAT_GMLFCN:
				if (iteration == 0) {
					rz_cons_printf("  node [\n"
						       "    id  %" PFMT64d "\n"
						       "    label  \"%s\"\n"
						       "  ]\n",
						fcnr->to - base, fcnr_name);
					rz_cons_printf("  edge [\n"
						       "    source  %" PFMT64d "\n"
						       "    target  %" PFMT64d "\n"
						       "  ]\n",
						fcni->addr - base, fcnr->to - base);
				}
			case RZ_GRAPH_FORMAT_GML:
				if (iteration != 0) {
					rz_cons_printf("  edge [\n"
						       "    source  %" PFMT64d "\n"
						       "    target  %" PFMT64d "\n"
						       "  ]\n",
						fcni->addr - base, fcnr->to - base); //, "#000000"
				}
				break;
			case RZ_GRAPH_FORMAT_DOT:
				rz_cons_printf("  \"0x%08" PFMT64x "\" -> \"0x%08" PFMT64x "\" "
					       "[color=\"%s\" URL=\"%s/0x%08" PFMT64x "\"];\n",
					//"[label=\"%s\" color=\"%s\" URL=\"%s/0x%08"PFMT64x"\"];\n",
					fcni->addr, fcnr->to, //, fcnr_name,
					"#61afef",
					fcnr_name, fcnr->to);
				rz_cons_printf("  \"0x%08" PFMT64x "\" "
					       "[label=\"%s\""
					       " URL=\"%s/0x%08" PFMT64x "\"];\n",
					fcnr->to, fcnr_name,
					fcnr_name, fcnr->to);
				break;
			case RZ_GRAPH_FORMAT_JSON:
				if (usenames) {
					pj_s(pj, fcnr_name);
				} else {
					char fcnr_addr[20];
					snprintf(fcnr_addr, sizeof(fcnr_addr) - 1, "0x%08" PFMT64x, fcnr->to);
					pj_s(pj, fcnr_addr);
				}
				break;
			default:
				if (refgraph || fcnr->type == RZ_ANALYSIS_REF_TYPE_CALL) {
					// TODO: avoid recreating nodes unnecessarily
					rz_cons_printf("agn %s\n", fcni->name);
					rz_cons_printf("agn %s\n", fcnr_name);
					rz_cons_printf("age %s %s\n", fcni->name, fcnr_name);
				} else {
					rz_cons_printf("# - 0x%08" PFMT64x " (%c)\n", fcnr->to, fcnr->type);
				}
			}
			if (!(flag && flag->name)) {
				free(fcnr_name);
			}
		}
		rz_list_free(xrefs);
		rz_list_free(calls);
		if (fmt == RZ_GRAPH_FORMAT_JSON) {
			pj_end(pj);
			pj_end(pj);
		}
	}
	if (iteration == 0 && fmt == RZ_GRAPH_FORMAT_GML) {
		iteration++;
		goto repeat;
	}
	if (iteration == 0 && fmt == RZ_GRAPH_FORMAT_GMLFCN) {
		iteration++;
	}
	switch (fmt) {
	case RZ_GRAPH_FORMAT_GML:
	case RZ_GRAPH_FORMAT_GMLFCN:
		rz_cons_printf("]\n");
		break;
	case RZ_GRAPH_FORMAT_JSON:
		pj_end(pj);
		rz_cons_println(pj_string(pj));
		pj_free(pj);
		break;
	case RZ_GRAPH_FORMAT_DOT:
		rz_cons_printf("}\n");
		break;
	}
}


RZ_API bool rz_core_analysis_function_rename(RzCore *core, ut64 addr, const char *_name) {
	rz_return_val_if_fail(core && _name, false);
	_name = rz_str_trim_head_ro(_name);
	char *name = getFunctionNamePrefix(core, addr, _name);
	// RzAnalysisFunction *fcn = rz_analysis_get_fcn_in (core->analysis, addr, RZ_ANALYSIS_FCN_TYPE_ANY);
	RzAnalysisFunction *fcn = rz_analysis_get_function_at(core->analysis, addr);
	if (fcn) {
		RzFlagItem *flag = rz_flag_get(core->flags, fcn->name);
		if (flag && flag->space && strcmp(flag->space->name, RZ_FLAGS_FS_FUNCTIONS) == 0) {
			// Only flags in the functions fs should be renamed, e.g. we don't want to rename symbol flags.
			rz_flag_rename(core->flags, flag, name);
		} else {
			// No flag or not specific to the function, create a new one.
			rz_flag_space_push(core->flags, RZ_FLAGS_FS_FUNCTIONS);
			rz_flag_set(core->flags, name, fcn->addr, rz_analysis_function_size_from_entry(fcn));
			rz_flag_space_pop(core->flags);
		}
		rz_analysis_function_rename(fcn, name);
		if (core->analysis->cb.on_fcn_rename) {
			core->analysis->cb.on_fcn_rename(core->analysis, core, fcn, name);
		}
		free(name);
		return true;
	}
	free(name);
	return false;
}


RZ_API void rz_core_analysis_coderefs(RzCore *core, ut64 addr) {
	RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, addr, -1);
	if (fcn) {
		const char *me = fcn->name;
		RzListIter *iter;
		RzAnalysisXRef *xref;
		RzList *xrefs = rz_analysis_function_get_xrefs_from(fcn);
		rz_cons_printf("agn %s\n", me);
		rz_list_foreach (xrefs, iter, xref) {
			RzFlagItem *item = rz_flag_get_i(core->flags, xref->to);
			const char *dst = item ? item->name : sdb_fmt("0x%08" PFMT64x, xref->from);
			rz_cons_printf("agn %s\n", dst);
			rz_cons_printf("age %s %s\n", me, dst);
		}
		rz_list_free(xrefs);
	} else {
		eprintf("Not in a function. Use 'df' to define it.\n");
	}
}


RZ_API RZ_DEPRECATE RzAnalysisVar *rz_analysis_get_used_function_var(RzAnalysis *analysis, ut64 addr) {
	RzList *fcns = rz_analysis_get_functions_in(analysis, addr);
	if (!fcns) {
		return NULL;
	}
	RzAnalysisVar *var = NULL;
	RzListIter *it;
	RzAnalysisFunction *fcn;
	rz_list_foreach (fcns, it, fcn) {
		RzPVector *used_vars = rz_analysis_function_get_vars_used_at(fcn, addr);
		if (used_vars && !rz_pvector_empty(used_vars)) {
			var = rz_pvector_at(used_vars, 0);
			break;
		}
	}
	rz_list_free(fcns);
	return var;
}


RZ_API void rz_core_analysis_propagate_noreturn_relocs(RzCore *core, ut64 addr) {
	// Processing every reference calls rz_analysis_op() which sometimes changes the
	// state of `asm.bits` variable, thus we save it to restore after the processing
	// is finished.
	int bits1 = core->analysis->bits;
	int bits2 = core->rasm->bits;
	// find known noreturn functions to propagate
	RzList *noretl = rz_analysis_noreturn_functions(core->analysis);
	// List of the potentially noreturn functions
	SetU *todo = set_u_new();
	struct core_noretl u = { core, noretl, todo };
	ht_up_foreach(core->analysis->ht_xrefs_to, process_refs_cb, &u);
	rz_list_free(noretl);
	core->analysis->bits = bits1;
	core->rasm->bits = bits2;
	// For every function in todo list analyze if it's potentially become noreturn
	ht_up_foreach(todo, reanalyze_fcns_cb, core);
	set_u_free(todo);
}


RZ_API void rz_core_analysis_type_match(RzCore *core, RzAnalysisFunction *fcn, HtUU *loop_table) {
	RzListIter *it;

	rz_return_if_fail(core && core->analysis && fcn);

	if (!core->analysis->esil) {
		eprintf("Please run aeim\n");
		return;
	}

	RzAnalysis *analysis = core->analysis;
	const int mininstrsz = rz_analysis_archinfo(analysis, RZ_ANALYSIS_ARCHINFO_MIN_OP_SIZE);
	const int minopcode = RZ_MAX(1, mininstrsz);
	RzConfigHold *hc = rz_config_hold_new(core->config);
	if (!hc) {
		return;
	}
	RzDebugTrace *dt = NULL;
	RzAnalysisEsilTrace *et = NULL;
	if (!analysis_emul_init(core, hc, &dt, &et) || !fcn) {
		analysis_emul_restore(core, hc, dt, et);
		return;
	}

	// Reserve bigger ht to avoid rehashing
	HtPPOptions opt;
	RzDebugTrace *dtrace = core->dbg->trace;
	opt = dtrace->ht->opt;
	ht_pp_free(dtrace->ht);
	dtrace->ht = ht_pp_new_size(fcn->ninstr, opt.dupvalue, opt.freefn, opt.calcsizeV);
	dtrace->ht->opt = opt;

	// Create a new context to store the return type propagation state
	struct ReturnTypeAnalysisCtx retctx = {
		.resolved = false,
		.ret_type = NULL,
		.ret_reg = NULL,
	};
	struct TypeAnalysisCtx ctx = {
		.retctx = &retctx,
		.cur_idx = 0,
		.prev_dest = NULL,
		.str_flag = false
	};

	HtUP *op_cache = NULL;
	const char *pc = rz_reg_get_name(core->dbg->reg, RZ_REG_NAME_PC);
	if (!pc) {
		goto out_function;
	}
	RzRegItem *r = rz_reg_get(core->dbg->reg, pc, -1);
	if (!r) {
		goto out_function;
	}
	rz_cons_break_push(NULL, NULL);
	rz_list_sort(fcn->bbs, bb_cmpaddr);
	// TODO: The algorithm can be more accurate if blocks are followed by their jmp/fail, not just by address
	RzAnalysisBlock *bb;
	rz_list_foreach (fcn->bbs, it, bb) {
		ut64 addr = bb->addr;
		rz_reg_set_value(core->dbg->reg, r, addr);
		ht_up_free(op_cache);
		op_cache = ht_up_new(NULL, free_op_cache_kv, NULL);
		if (!op_cache) {
			break;
		}
		while (1) {
			if (rz_cons_is_breaked()) {
				goto out_function;
			}
			ut64 pcval = rz_reg_getv(analysis->reg, pc);
			if ((addr >= bb->addr + bb->size) || (addr < bb->addr) || pcval != addr) {
				break;
			}
			RzAnalysisOp *aop = op_cache_get(op_cache, core, addr);
			if (!aop) {
				break;
			}
			if (aop->type == RZ_ANALYSIS_OP_TYPE_ILL) {
				addr += minopcode;
				continue;
			}

			// CHECK_ME : why we hold a loop_count here ?
			//          : can we remove it ?
			// when set null, do not track loop count
			if (loop_table) {
				ut64 loop_count = ht_uu_find(loop_table, addr, NULL);
				if (loop_count > LOOP_MAX || aop->type == RZ_ANALYSIS_OP_TYPE_RET) {
					break;
				}
				loop_count += 1;
				ht_uu_update(loop_table, addr, loop_count);
			}

			if (rz_analysis_op_nonlinear(aop->type)) { // skip the instr
				rz_reg_set_value(core->dbg->reg, r, addr + aop->size);
			} else {
				rz_core_esil_step(core, UT64_MAX, NULL, NULL, false);
			}

			RzPVector *ins_traces = analysis->esil->trace->instructions;
			ctx.cur_idx = rz_pvector_len(ins_traces) - 1;
			RzList *fcns = rz_analysis_get_functions_in(analysis, aop->addr);
			if (!fcns) {
				break;
			}
			RzListIter *it;
			RzAnalysisFunction *fcn;
			rz_list_foreach (fcns, it, fcn) {
				propagate_types_among_used_variables(core, op_cache, fcn, bb, aop, &ctx);
			}
			addr += aop->size;
			rz_list_free(fcns);
		}
	}
	// Type propagation for register based args
	void **vit;
	//RzPVector *cloned_vars = (RzPVector *)rz_vector_clone((RzVector *)&fcn->vars);
	rz_pvector_foreach (&fcn->vars, vit) {
		RzAnalysisVar *rvar = *vit;
		if (rvar->kind == RZ_ANALYSIS_VAR_KIND_REG) {
			RzAnalysisVar *lvar = rz_analysis_var_get_dst_var(rvar);
			RzRegItem *i = rz_reg_index_get(analysis->reg, rvar->delta);
			if (!i) {
				continue;
			}
			// Note that every `var_type_set()` call could remove some variables
			// due to the overlaps resolution
			if (lvar) {
				// Propagate local var type = to => register-based var
				var_type_set(analysis, rvar, lvar->type, false);
				// Propagate local var type <= from = register-based var
				var_type_set(analysis, lvar, rvar->type, false);
			}
		}
	}
out_function:
	free(retctx.ret_reg);
	ht_up_free(op_cache);
	rz_cons_break_pop();
	analysis_emul_restore(core, hc, dt, et);
}


static int is_string(const ut8 *buf, int size, int *len) {
	int i, fakeLen = 0;
	if (size < 1) {
		return 0;
	}
	if (!len) {
		len = &fakeLen;
	}
	if (size > 3 && buf[0] && !buf[1] && buf[2] && !buf[3]) {
		*len = 1; // XXX: TODO: Measure wide string length
		return 2; // is wide
	}
	for (i = 0; i < size; i++) {
		if (!buf[i] && i > MINLEN) {
			*len = i;
			return 1;
		}
		if (buf[i] == 10 || buf[i] == 13 || buf[i] == 9) {
			continue;
		}
		if (buf[i] < 32 || buf[i] > 127) {
			// not ascii text
			return 0;
		}
		if (!IS_PRINTABLE(buf[i])) {
			*len = i;
			return 0;
		}
	}
	*len = i;
	return 1;
}


inline bool operator==(const QuantizationParams& qp1,
                       const QuantizationParams& qp2) {
  return qp1.zero_point == qp2.zero_point && qp1.scale == qp2.scale;
}


bool operator!=(const RuntimeShape& comp) const { return !((*this) == comp); }


inline RuntimeShape DimsToShape(const tflite::Dims<4>& dims) {
  return RuntimeShape(
      {dims.sizes[3], dims.sizes[2], dims.sizes[1], dims.sizes[0]});
}


int MatchingArraySize(const ArrayType1& array1, int index1,
                      const ArrayType2& array2, int index2, Args... args) {
  TFLITE_DCHECK_EQ(ArraySize(array1, index1), ArraySize(array2, index2));
  return MatchingArraySize(array1, index1, args...);
}


  inline int32_t Dims(int i) const {
    TFLITE_DCHECK_GE(i, 0);
    TFLITE_DCHECK_LT(i, size_);
    return size_ > kMaxSmallSize ? dims_pointer_[i] : dims_[i];
  }


inline int MatchingFlatSize(const Dims<N>& dims, const Dims<N>& check_dims_0,
                            const Dims<N>& check_dims_1,
                            const Dims<N>& check_dims_2) {
  for (int i = 0; i < N; ++i) {
    TFLITE_DCHECK_EQ(ArraySize(dims, i), ArraySize(check_dims_0, i));
  }
  return MatchingFlatSize(dims, check_dims_1, check_dims_2);
}


  RuntimeShape(int dimensions_count, const int32_t* dims_data) : size_(0) {
    ReplaceWith(dimensions_count, dims_data);
  }


void ComputeStrides(Dims<N>* dims) {
  dims->strides[0] = 1;
  for (int d = 1; d < N; d++) {
    dims->strides[d] = dims->strides[d - 1] * dims->sizes[d - 1];
  }
}


  ~RuntimeShape() {
    if (size_ > kMaxSmallSize) {
#ifdef TF_LITE_STATIC_MEMORY
      TFLITE_CHECK(false && "No shape resizing supported on this platform");
#else  // TF_LITE_STATIC_MEMORY
      delete[] dims_pointer_;
#endif  // TF_LITE_STATIC_MEMORY
    }
  }


inline tflite::Dims<4> ToRuntimeDims(const tflite::RuntimeShape& array_shape) {
  tflite::Dims<4> result;
  const int dimensions_count = array_shape.DimensionsCount();
  TFLITE_CHECK_LE(dimensions_count, 4);
  int cum_prod = 1;
  for (int i = 0; i < 4; i++) {
    const int new_dim =
        (i < dimensions_count) ? array_shape.Dims(dimensions_count - 1 - i) : 1;
    result.sizes[i] = new_dim;
    result.strides[i] = cum_prod;
    cum_prod *= new_dim;
  }
  return result;
}


static int sqfs_get_regfile_info(struct squashfs_reg_inode *reg,
				 struct squashfs_file_info *finfo,
				 struct squashfs_fragment_block_entry *fentry,
				 __le32 blksz)
{
	int datablk_count = 0, ret;

	finfo->size = get_unaligned_le32(&reg->file_size);
	finfo->offset = get_unaligned_le32(&reg->offset);
	finfo->start = get_unaligned_le32(&reg->start_block);
	finfo->frag = SQFS_IS_FRAGMENTED(get_unaligned_le32(&reg->fragment));

	if (finfo->frag && finfo->offset == 0xFFFFFFFF)
		return -EINVAL;

	if (finfo->size < 1 || finfo->start == 0xFFFFFFFF)
		return -EINVAL;

	if (finfo->frag) {
		datablk_count = finfo->size / le32_to_cpu(blksz);
		ret = sqfs_frag_lookup(get_unaligned_le32(&reg->fragment),
				       fentry);
		if (ret < 0)
			return -EINVAL;
		finfo->comp = ret;
		if (fentry->size < 1 || fentry->start == 0x7FFFFFFF)
			return -EINVAL;
	} else {
		datablk_count = DIV_ROUND_UP(finfo->size, le32_to_cpu(blksz));
	}

	finfo->blk_sizes = malloc(datablk_count * sizeof(u32));
	if (!finfo->blk_sizes)
		return -ENOMEM;

	return datablk_count;
}


static int sqfs_count_metablks(void *table, u32 offset, int table_size)
{
	int count = 0, cur_size = 0, ret;
	u32 data_size;
	bool comp;

	do {
		ret = sqfs_read_metablock(table, offset + cur_size, &comp,
					  &data_size);
		if (ret)
			return -EINVAL;
		cur_size += data_size + SQFS_HEADER_SIZE;
		count++;
	} while (cur_size < table_size);

	return count;
}


static char *sqfs_concat_tokens(char **token_list, int token_count)
{
	char *result;
	int i, length = 0, offset = 0;

	length = sqfs_get_tokens_length(token_list, token_count);

	result = malloc(length + 1);
	if (!result)
		return NULL;

	result[length] = '\0';

	for (i = 0; i < token_count; i++) {
		strcpy(result + offset, token_list[i]);
		offset += strlen(token_list[i]);
		result[offset++] = '/';
	}

	return result;
}


static inline u32 tg3_rx_ret_ring_size(struct tg3 *tp)
{
	if (tg3_flag(tp, LRG_PROD_RING_CAP))
		return TG3_RX_RET_MAX_SIZE_5717;
	else if (tg3_flag(tp, JUMBO_CAPABLE) && !tg3_flag(tp, 5780_CLASS))
		return TG3_RX_RET_MAX_SIZE_5700;
	else
		return TG3_RX_RET_MAX_SIZE_5705;
}


static void tg3_recycle_rx(struct tg3_napi *tnapi,
			   struct tg3_rx_prodring_set *dpr,
			   u32 opaque_key, int src_idx,
			   u32 dest_idx_unmasked)
{
	struct tg3 *tp = tnapi->tp;
	struct tg3_rx_buffer_desc *src_desc, *dest_desc;
	struct ring_info *src_map, *dest_map;
	struct tg3_rx_prodring_set *spr = &tp->napi[0].prodring;
	int dest_idx;

	switch (opaque_key) {
	case RXD_OPAQUE_RING_STD:
		dest_idx = dest_idx_unmasked & tp->rx_std_ring_mask;
		dest_desc = &dpr->rx_std[dest_idx];
		dest_map = &dpr->rx_std_buffers[dest_idx];
		src_desc = &spr->rx_std[src_idx];
		src_map = &spr->rx_std_buffers[src_idx];
		break;

	case RXD_OPAQUE_RING_JUMBO:
		dest_idx = dest_idx_unmasked & tp->rx_jmb_ring_mask;
		dest_desc = &dpr->rx_jmb[dest_idx].std;
		dest_map = &dpr->rx_jmb_buffers[dest_idx];
		src_desc = &spr->rx_jmb[src_idx].std;
		src_map = &spr->rx_jmb_buffers[src_idx];
		break;

	default:
		return;
	}

	dest_map->data = src_map->data;
	dma_unmap_addr_set(dest_map, mapping,
			   dma_unmap_addr(src_map, mapping));
	dest_desc->addr_hi = src_desc->addr_hi;
	dest_desc->addr_lo = src_desc->addr_lo;

	/* Ensure that the update to the skb happens after the physical
	 * addresses have been transferred to the new BD location.
	 */
	smp_wmb();

	src_map->data = NULL;
}


static bool tg3_enable_msix(struct tg3 *tp)
{
	int i, rc;
	struct msix_entry msix_ent[TG3_IRQ_MAX_VECS];

	tp->txq_cnt = tp->txq_req;
	tp->rxq_cnt = tp->rxq_req;
	if (!tp->rxq_cnt)
		tp->rxq_cnt = netif_get_num_default_rss_queues();
	if (tp->rxq_cnt > tp->rxq_max)
		tp->rxq_cnt = tp->rxq_max;

	/* Disable multiple TX rings by default.  Simple round-robin hardware
	 * scheduling of the TX rings can cause starvation of rings with
	 * small packets when other rings have TSO or jumbo packets.
	 */
	if (!tp->txq_req)
		tp->txq_cnt = 1;

	tp->irq_cnt = tg3_irq_count(tp);

	for (i = 0; i < tp->irq_max; i++) {
		msix_ent[i].entry  = i;
		msix_ent[i].vector = 0;
	}

	rc = pci_enable_msix(tp->pdev, msix_ent, tp->irq_cnt);
	if (rc < 0) {
		return false;
	} else if (rc != 0) {
		if (pci_enable_msix(tp->pdev, msix_ent, rc))
			return false;
		netdev_notice(tp->dev, "Requested %d MSI-X vectors, received %d\n",
			      tp->irq_cnt, rc);
		tp->irq_cnt = rc;
		tp->rxq_cnt = max(rc - 1, 1);
		if (tp->txq_cnt)
			tp->txq_cnt = min(tp->rxq_cnt, tp->txq_max);
	}

	for (i = 0; i < tp->irq_max; i++)
		tp->napi[i].irq_vec = msix_ent[i].vector;

	if (netif_set_real_num_rx_queues(tp->dev, tp->rxq_cnt)) {
		pci_disable_msix(tp->pdev);
		return false;
	}

	if (tp->irq_cnt == 1)
		return true;

	tg3_flag_set(tp, ENABLE_RSS);

	if (tp->txq_cnt > 1)
		tg3_flag_set(tp, ENABLE_TSS);

	netif_set_real_num_tx_queues(tp->dev, tp->txq_cnt);

	return true;
}


static void tg3_phy_apply_otp(struct tg3 *tp)
{
	u32 otp, phy;

	if (!tp->phy_otp)
		return;

	otp = tp->phy_otp;

	if (tg3_phy_toggle_auxctl_smdsp(tp, true))
		return;

	phy = ((otp & TG3_OTP_AGCTGT_MASK) >> TG3_OTP_AGCTGT_SHIFT);
	phy |= MII_TG3_DSP_TAP1_AGCTGT_DFLT;
	tg3_phydsp_write(tp, MII_TG3_DSP_TAP1, phy);

	phy = ((otp & TG3_OTP_HPFFLTR_MASK) >> TG3_OTP_HPFFLTR_SHIFT) |
	      ((otp & TG3_OTP_HPFOVER_MASK) >> TG3_OTP_HPFOVER_SHIFT);
	tg3_phydsp_write(tp, MII_TG3_DSP_AADJ1CH0, phy);

	phy = ((otp & TG3_OTP_LPFDIS_MASK) >> TG3_OTP_LPFDIS_SHIFT);
	phy |= MII_TG3_DSP_AADJ1CH3_ADCCKADJ;
	tg3_phydsp_write(tp, MII_TG3_DSP_AADJ1CH3, phy);

	phy = ((otp & TG3_OTP_VDAC_MASK) >> TG3_OTP_VDAC_SHIFT);
	tg3_phydsp_write(tp, MII_TG3_DSP_EXP75, phy);

	phy = ((otp & TG3_OTP_10BTAMP_MASK) >> TG3_OTP_10BTAMP_SHIFT);
	tg3_phydsp_write(tp, MII_TG3_DSP_EXP96, phy);

	phy = ((otp & TG3_OTP_ROFF_MASK) >> TG3_OTP_ROFF_SHIFT) |
	      ((otp & TG3_OTP_RCOFF_MASK) >> TG3_OTP_RCOFF_SHIFT);
	tg3_phydsp_write(tp, MII_TG3_DSP_EXP97, phy);

	tg3_phy_toggle_auxctl_smdsp(tp, false);
}


static int tg3_wait_macro_done(struct tg3 *tp)
{
	int limit = 100;

	while (limit--) {
		u32 tmp32;

		if (!tg3_readphy(tp, MII_TG3_DSP_CONTROL, &tmp32)) {
			if ((tmp32 & 0x1000) == 0)
				break;
		}
	}
	if (limit < 0)
		return -EBUSY;

	return 0;
}


static void tg3_enable_ints(struct tg3 *tp)
{
	int i;

	tp->irq_sync = 0;
	wmb();

	tw32(TG3PCI_MISC_HOST_CTRL,
	     (tp->misc_host_ctrl & ~MISC_HOST_CTRL_MASK_PCI_INT));

	tp->coal_now = tp->coalesce_mode | HOSTCC_MODE_ENABLE;
	for (i = 0; i < tp->irq_cnt; i++) {
		struct tg3_napi *tnapi = &tp->napi[i];

		tw32_mailbox_f(tnapi->int_mbox, tnapi->last_tag << 24);
		if (tg3_flag(tp, 1SHOT_MSI))
			tw32_mailbox_f(tnapi->int_mbox, tnapi->last_tag << 24);

		tp->coal_now |= tnapi->coal_now;
	}

	/* Force an initial interrupt */
	if (!tg3_flag(tp, TAGGED_STATUS) &&
	    (tp->napi[0].hw_status->status & SD_STATUS_UPDATED))
		tw32(GRC_LOCAL_CTRL, tp->grc_local_ctrl | GRC_LCLCTRL_SETINT);
	else
		tw32(HOSTCC_MODE, tp->coal_now);

	tp->coal_now &= ~(tp->napi[0].coal_now | tp->napi[1].coal_now);
}


static u32 tg3_read32_mbox_5906(struct tg3 *tp, u32 off)
{
	return readl(tp->regs + off + GRCMBOX_BASE);
}


static void tg3_rss_check_indir_tbl(struct tg3 *tp)
{
	int i;

	if (!tg3_flag(tp, SUPPORT_MSIX))
		return;

	if (tp->rxq_cnt == 1) {
		memset(&tp->rss_ind_tbl[0], 0, sizeof(tp->rss_ind_tbl));
		return;
	}

	/* Validate table against current IRQ count */
	for (i = 0; i < TG3_RSS_INDIR_TBL_SIZE; i++) {
		if (tp->rss_ind_tbl[i] >= tp->rxq_cnt)
			break;
	}

	if (i != TG3_RSS_INDIR_TBL_SIZE)
		tg3_rss_init_dflt_indir_tbl(tp, tp->rxq_cnt);
}


static void tg3_mac_loopback(struct tg3 *tp, bool enable)
{
	if (enable) {
		tp->mac_mode &= ~(MAC_MODE_HALF_DUPLEX |
				  MAC_MODE_PORT_MODE_MASK);

		tp->mac_mode |= MAC_MODE_PORT_INT_LPBACK;

		if (!tg3_flag(tp, 5705_PLUS))
			tp->mac_mode |= MAC_MODE_LINK_POLARITY;

		if (tp->phy_flags & TG3_PHYFLG_10_100_ONLY)
			tp->mac_mode |= MAC_MODE_PORT_MODE_MII;
		else
			tp->mac_mode |= MAC_MODE_PORT_MODE_GMII;
	} else {
		tp->mac_mode &= ~MAC_MODE_PORT_INT_LPBACK;

		if (tg3_flag(tp, 5705_PLUS) ||
		    (tp->phy_flags & TG3_PHYFLG_PHY_SERDES) ||
		    tg3_asic_rev(tp) == ASIC_REV_5700)
			tp->mac_mode &= ~MAC_MODE_LINK_POLARITY;
	}

	tw32(MAC_MODE, tp->mac_mode);
	udelay(40);
}


static int tg3_fw_img_is_valid(struct tg3 *tp, u32 offset)
{
	u32 val;

	if (tg3_nvram_read(tp, offset, &val) ||
	    (val & 0xfc000000) != 0x0c000000 ||
	    tg3_nvram_read(tp, offset + 4, &val) ||
	    val != 0)
		return 0;

	return 1;
}


static struct pci_dev *tg3_find_peer(struct tg3 *tp)
{
	struct pci_dev *peer;
	unsigned int func, devnr = tp->pdev->devfn & ~7;

	for (func = 0; func < 8; func++) {
		peer = pci_get_slot(tp->pdev->bus, devnr | func);
		if (peer && peer != tp->pdev)
			break;
		pci_dev_put(peer);
	}
	/* 5704 can be configured in single-port mode, set peer to
	 * tp->pdev in that case.
	 */
	if (!peer) {
		peer = tp->pdev;
		return peer;
	}

	/*
	 * We don't need to keep the refcount elevated; there's no way
	 * to remove one half of this device without removing the other
	 */
	pci_dev_put(peer);

	return peer;
}


static void tg3_napi_enable(struct tg3 *tp)
{
	int i;

	for (i = 0; i < tp->irq_cnt; i++)
		napi_enable(&tp->napi[i].napi);
}


static void tg3_read_fw_ver(struct tg3 *tp)
{
	u32 val;
	bool vpd_vers = false;

	if (tp->fw_ver[0] != 0)
		vpd_vers = true;

	if (tg3_flag(tp, NO_NVRAM)) {
		strcat(tp->fw_ver, "sb");
		tg3_read_otp_ver(tp);
		return;
	}

	if (tg3_nvram_read(tp, 0, &val))
		return;

	if (val == TG3_EEPROM_MAGIC)
		tg3_read_bc_ver(tp);
	else if ((val & TG3_EEPROM_MAGIC_FW_MSK) == TG3_EEPROM_MAGIC_FW)
		tg3_read_sb_ver(tp, val);
	else if ((val & TG3_EEPROM_MAGIC_HW_MSK) == TG3_EEPROM_MAGIC_HW)
		tg3_read_hwsb_ver(tp);

	if (tg3_flag(tp, ENABLE_ASF)) {
		if (tg3_flag(tp, ENABLE_APE)) {
			tg3_probe_ncsi(tp);
			if (!vpd_vers)
				tg3_read_dash_ver(tp);
		} else if (!vpd_vers) {
			tg3_read_mgmtfw_ver(tp);
		}
	}

	tp->fw_ver[TG3_VER_SIZE - 1] = 0;
}


static int tg3_get_sset_count(struct net_device *dev, int sset)
{
	switch (sset) {
	case ETH_SS_TEST:
		return TG3_NUM_TEST;
	case ETH_SS_STATS:
		return TG3_NUM_STATS;
	default:
		return -EOPNOTSUPP;
	}
}


static void tg3_wait_for_event_ack(struct tg3 *tp)
{
	int i;
	unsigned int delay_cnt;
	long time_remain;

	/* If enough time has passed, no wait is necessary. */
	time_remain = (long)(tp->last_event_jiffies + 1 +
		      usecs_to_jiffies(TG3_FW_EVENT_TIMEOUT_USEC)) -
		      (long)jiffies;
	if (time_remain < 0)
		return;

	/* Check if we can shorten the wait time. */
	delay_cnt = jiffies_to_usecs(time_remain);
	if (delay_cnt > TG3_FW_EVENT_TIMEOUT_USEC)
		delay_cnt = TG3_FW_EVENT_TIMEOUT_USEC;
	delay_cnt = (delay_cnt >> 3) + 1;

	for (i = 0; i < delay_cnt; i++) {
		if (!(tr32(GRC_RX_CPU_EVENT) & GRC_RX_CPU_DRIVER_EVENT))
			break;
		udelay(8);
	}
}


static void tg3_mem_tx_release(struct tg3 *tp)
{
	int i;

	for (i = 0; i < tp->irq_max; i++) {
		struct tg3_napi *tnapi = &tp->napi[i];

		if (tnapi->tx_ring) {
			dma_free_coherent(&tp->pdev->dev, TG3_TX_RING_BYTES,
				tnapi->tx_ring, tnapi->tx_desc_mapping);
			tnapi->tx_ring = NULL;
		}

		kfree(tnapi->tx_buffers);
		tnapi->tx_buffers = NULL;
	}
}


static int tg3_get_default_macaddr_sparc(struct tg3 *tp)
{
	struct net_device *dev = tp->dev;

	memcpy(dev->dev_addr, idprom->id_ethaddr, 6);
	return 0;
}


static void tg3_coal_tx_init(struct tg3 *tp, struct ethtool_coalesce *ec)
{
	int i = 0;

	if (!tg3_flag(tp, ENABLE_TSS)) {
		tw32(HOSTCC_TXCOL_TICKS, ec->tx_coalesce_usecs);
		tw32(HOSTCC_TXMAX_FRAMES, ec->tx_max_coalesced_frames);
		tw32(HOSTCC_TXCOAL_MAXF_INT, ec->tx_max_coalesced_frames_irq);
	} else {
		tw32(HOSTCC_TXCOL_TICKS, 0);
		tw32(HOSTCC_TXMAX_FRAMES, 0);
		tw32(HOSTCC_TXCOAL_MAXF_INT, 0);

		for (; i < tp->txq_cnt; i++) {
			u32 reg;

			reg = HOSTCC_TXCOL_TICKS_VEC1 + i * 0x18;
			tw32(reg, ec->tx_coalesce_usecs);
			reg = HOSTCC_TXMAX_FRAMES_VEC1 + i * 0x18;
			tw32(reg, ec->tx_max_coalesced_frames);
			reg = HOSTCC_TXCOAL_MAXF_INT_VEC1 + i * 0x18;
			tw32(reg, ec->tx_max_coalesced_frames_irq);
		}
	}

	for (; i < tp->irq_max - 1; i++) {
		tw32(HOSTCC_TXCOL_TICKS_VEC1 + i * 0x18, 0);
		tw32(HOSTCC_TXMAX_FRAMES_VEC1 + i * 0x18, 0);
		tw32(HOSTCC_TXCOAL_MAXF_INT_VEC1 + i * 0x18, 0);
	}
}


static void tg3_get_5752_nvram_info(struct tg3 *tp)
{
	u32 nvcfg1;

	nvcfg1 = tr32(NVRAM_CFG1);

	/* NVRAM protection for TPM */
	if (nvcfg1 & (1 << 27))
		tg3_flag_set(tp, PROTECTED_NVRAM);

	switch (nvcfg1 & NVRAM_CFG1_5752VENDOR_MASK) {
	case FLASH_5752VENDOR_ATMEL_EEPROM_64KHZ:
	case FLASH_5752VENDOR_ATMEL_EEPROM_376KHZ:
		tp->nvram_jedecnum = JEDEC_ATMEL;
		tg3_flag_set(tp, NVRAM_BUFFERED);
		break;
	case FLASH_5752VENDOR_ATMEL_FLASH_BUFFERED:
		tp->nvram_jedecnum = JEDEC_ATMEL;
		tg3_flag_set(tp, NVRAM_BUFFERED);
		tg3_flag_set(tp, FLASH);
		break;
	case FLASH_5752VENDOR_ST_M45PE10:
	case FLASH_5752VENDOR_ST_M45PE20:
	case FLASH_5752VENDOR_ST_M45PE40:
		tp->nvram_jedecnum = JEDEC_ST;
		tg3_flag_set(tp, NVRAM_BUFFERED);
		tg3_flag_set(tp, FLASH);
		break;
	}

	if (tg3_flag(tp, FLASH)) {
		tg3_nvram_get_pagesize(tp, nvcfg1);
	} else {
		/* For eeprom, set pagesize to maximum eeprom size */
		tp->nvram_pagesize = ATMEL_AT24C512_CHIP_SIZE;

		nvcfg1 &= ~NVRAM_CFG1_COMPAT_BYPASS;
		tw32(NVRAM_CFG1, nvcfg1);
	}
}


static int tg3_phy_init(struct tg3 *tp)
{
	struct phy_device *phydev;

	if (tp->phy_flags & TG3_PHYFLG_IS_CONNECTED)
		return 0;

	/* Bring the PHY back to a known state. */
	tg3_bmcr_reset(tp);

	phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR];

	/* Attach the MAC to the PHY. */
	phydev = phy_connect(tp->dev, dev_name(&phydev->dev),
			     tg3_adjust_link, phydev->interface);
	if (IS_ERR(phydev)) {
		dev_err(&tp->pdev->dev, "Could not attach to PHY\n");
		return PTR_ERR(phydev);
	}

	/* Mask with MAC supported features. */
	switch (phydev->interface) {
	case PHY_INTERFACE_MODE_GMII:
	case PHY_INTERFACE_MODE_RGMII:
		if (!(tp->phy_flags & TG3_PHYFLG_10_100_ONLY)) {
			phydev->supported &= (PHY_GBIT_FEATURES |
					      SUPPORTED_Pause |
					      SUPPORTED_Asym_Pause);
			break;
		}
		/* fallthru */
	case PHY_INTERFACE_MODE_MII:
		phydev->supported &= (PHY_BASIC_FEATURES |
				      SUPPORTED_Pause |
				      SUPPORTED_Asym_Pause);
		break;
	default:
		phy_disconnect(tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]);
		return -EINVAL;
	}

	tp->phy_flags |= TG3_PHYFLG_IS_CONNECTED;

	phydev->advertising = phydev->supported;

	return 0;
}


static void tg3_get_nvram_size(struct tg3 *tp)
{
	u32 val;

	if (tg3_flag(tp, NO_NVRAM) || tg3_nvram_read(tp, 0, &val) != 0)
		return;

	/* Selfboot format */
	if (val != TG3_EEPROM_MAGIC) {
		tg3_get_eeprom_size(tp);
		return;
	}

	if (tg3_nvram_read(tp, 0xf0, &val) == 0) {
		if (val != 0) {
			/* This is confusing.  We want to operate on the
			 * 16-bit value at offset 0xf2.  The tg3_nvram_read()
			 * call will read from NVRAM and byteswap the data
			 * according to the byteswapping settings for all
			 * other register accesses.  This ensures the data we
			 * want will always reside in the lower 16-bits.
			 * However, the data in NVRAM is in LE format, which
			 * means the data from the NVRAM read will always be
			 * opposite the endianness of the CPU.  The 16-bit
			 * byteswap then brings the data to CPU endianness.
			 */
			tp->nvram_size = swab16((u16)(val & 0x0000ffff)) * 1024;
			return;
		}
	}
	tp->nvram_size = TG3_NVRAM_SIZE_512KB;
}


static void tg3_nvram_init(struct tg3 *tp)
{
	if (tg3_flag(tp, IS_SSB_CORE)) {
		/* No NVRAM and EEPROM on the SSB Broadcom GigE core. */
		tg3_flag_clear(tp, NVRAM);
		tg3_flag_clear(tp, NVRAM_BUFFERED);
		tg3_flag_set(tp, NO_NVRAM);
		return;
	}

	tw32_f(GRC_EEPROM_ADDR,
	     (EEPROM_ADDR_FSM_RESET |
	      (EEPROM_DEFAULT_CLOCK_PERIOD <<
	       EEPROM_ADDR_CLKPERD_SHIFT)));

	msleep(1);

	/* Enable seeprom accesses. */
	tw32_f(GRC_LOCAL_CTRL,
	     tr32(GRC_LOCAL_CTRL) | GRC_LCLCTRL_AUTO_SEEPROM);
	udelay(100);

	if (tg3_asic_rev(tp) != ASIC_REV_5700 &&
	    tg3_asic_rev(tp) != ASIC_REV_5701) {
		tg3_flag_set(tp, NVRAM);

		if (tg3_nvram_lock(tp)) {
			netdev_warn(tp->dev,
				    "Cannot get nvram lock, %s failed\n",
				    __func__);
			return;
		}
		tg3_enable_nvram_access(tp);

		tp->nvram_size = 0;

		if (tg3_asic_rev(tp) == ASIC_REV_5752)
			tg3_get_5752_nvram_info(tp);
		else if (tg3_asic_rev(tp) == ASIC_REV_5755)
			tg3_get_5755_nvram_info(tp);
		else if (tg3_asic_rev(tp) == ASIC_REV_5787 ||
			 tg3_asic_rev(tp) == ASIC_REV_5784 ||
			 tg3_asic_rev(tp) == ASIC_REV_5785)
			tg3_get_5787_nvram_info(tp);
		else if (tg3_asic_rev(tp) == ASIC_REV_5761)
			tg3_get_5761_nvram_info(tp);
		else if (tg3_asic_rev(tp) == ASIC_REV_5906)
			tg3_get_5906_nvram_info(tp);
		else if (tg3_asic_rev(tp) == ASIC_REV_57780 ||
			 tg3_flag(tp, 57765_CLASS))
			tg3_get_57780_nvram_info(tp);
		else if (tg3_asic_rev(tp) == ASIC_REV_5717 ||
			 tg3_asic_rev(tp) == ASIC_REV_5719)
			tg3_get_5717_nvram_info(tp);
		else if (tg3_asic_rev(tp) == ASIC_REV_5720 ||
			 tg3_asic_rev(tp) == ASIC_REV_5762)
			tg3_get_5720_nvram_info(tp);
		else
			tg3_get_nvram_info(tp);

		if (tp->nvram_size == 0)
			tg3_get_nvram_size(tp);

		tg3_disable_nvram_access(tp);
		tg3_nvram_unlock(tp);

	} else {
		tg3_flag_clear(tp, NVRAM);
		tg3_flag_clear(tp, NVRAM_BUFFERED);

		tg3_get_eeprom_size(tp);
	}
}


static inline int tg3_40bit_overflow_test(struct tg3 *tp, dma_addr_t mapping,
					  int len)
{
#if defined(CONFIG_HIGHMEM) && (BITS_PER_LONG == 64)
	if (tg3_flag(tp, 40BIT_DMA_BUG))
		return ((u64) mapping + len) > DMA_BIT_MASK(40);
	return 0;
#else
	return 0;
#endif
}


static void tg3_mdio_config_5785(struct tg3 *tp)
{
	u32 val;
	struct phy_device *phydev;

	phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR];
	switch (phydev->drv->phy_id & phydev->drv->phy_id_mask) {
	case PHY_ID_BCM50610:
	case PHY_ID_BCM50610M:
		val = MAC_PHYCFG2_50610_LED_MODES;
		break;
	case PHY_ID_BCMAC131:
		val = MAC_PHYCFG2_AC131_LED_MODES;
		break;
	case PHY_ID_RTL8211C:
		val = MAC_PHYCFG2_RTL8211C_LED_MODES;
		break;
	case PHY_ID_RTL8201E:
		val = MAC_PHYCFG2_RTL8201E_LED_MODES;
		break;
	default:
		return;
	}

	if (phydev->interface != PHY_INTERFACE_MODE_RGMII) {
		tw32(MAC_PHYCFG2, val);

		val = tr32(MAC_PHYCFG1);
		val &= ~(MAC_PHYCFG1_RGMII_INT |
			 MAC_PHYCFG1_RXCLK_TO_MASK | MAC_PHYCFG1_TXCLK_TO_MASK);
		val |= MAC_PHYCFG1_RXCLK_TIMEOUT | MAC_PHYCFG1_TXCLK_TIMEOUT;
		tw32(MAC_PHYCFG1, val);

		return;
	}

	if (!tg3_flag(tp, RGMII_INBAND_DISABLE))
		val |= MAC_PHYCFG2_EMODE_MASK_MASK |
		       MAC_PHYCFG2_FMODE_MASK_MASK |
		       MAC_PHYCFG2_GMODE_MASK_MASK |
		       MAC_PHYCFG2_ACT_MASK_MASK   |
		       MAC_PHYCFG2_QUAL_MASK_MASK |
		       MAC_PHYCFG2_INBAND_ENABLE;

	tw32(MAC_PHYCFG2, val);

	val = tr32(MAC_PHYCFG1);
	val &= ~(MAC_PHYCFG1_RXCLK_TO_MASK | MAC_PHYCFG1_TXCLK_TO_MASK |
		 MAC_PHYCFG1_RGMII_EXT_RX_DEC | MAC_PHYCFG1_RGMII_SND_STAT_EN);
	if (!tg3_flag(tp, RGMII_INBAND_DISABLE)) {
		if (tg3_flag(tp, RGMII_EXT_IBND_RX_EN))
			val |= MAC_PHYCFG1_RGMII_EXT_RX_DEC;
		if (tg3_flag(tp, RGMII_EXT_IBND_TX_EN))
			val |= MAC_PHYCFG1_RGMII_SND_STAT_EN;
	}
	val |= MAC_PHYCFG1_RXCLK_TIMEOUT | MAC_PHYCFG1_TXCLK_TIMEOUT |
	       MAC_PHYCFG1_RGMII_INT | MAC_PHYCFG1_TXC_DRV;
	tw32(MAC_PHYCFG1, val);

	val = tr32(MAC_EXT_RGMII_MODE);
	val &= ~(MAC_RGMII_MODE_RX_INT_B |
		 MAC_RGMII_MODE_RX_QUALITY |
		 MAC_RGMII_MODE_RX_ACTIVITY |
		 MAC_RGMII_MODE_RX_ENG_DET |
		 MAC_RGMII_MODE_TX_ENABLE |
		 MAC_RGMII_MODE_TX_LOWPWR |
		 MAC_RGMII_MODE_TX_RESET);
	if (!tg3_flag(tp, RGMII_INBAND_DISABLE)) {
		if (tg3_flag(tp, RGMII_EXT_IBND_RX_EN))
			val |= MAC_RGMII_MODE_RX_INT_B |
			       MAC_RGMII_MODE_RX_QUALITY |
			       MAC_RGMII_MODE_RX_ACTIVITY |
			       MAC_RGMII_MODE_RX_ENG_DET;
		if (tg3_flag(tp, RGMII_EXT_IBND_TX_EN))
			val |= MAC_RGMII_MODE_TX_ENABLE |
			       MAC_RGMII_MODE_TX_LOWPWR |
			       MAC_RGMII_MODE_TX_RESET;
	}
	tw32(MAC_EXT_RGMII_MODE, val);
}


static void tg3_tx_recover(struct tg3 *tp)
{
	BUG_ON(tg3_flag(tp, MBOX_WRITE_REORDER) ||
	       tp->write32_tx_mbox == tg3_write_indirect_mbox);

	netdev_warn(tp->dev,
		    "The system may be re-ordering memory-mapped I/O "
		    "cycles to the network device, attempting to recover. "
		    "Please report the problem to the driver maintainer "
		    "and include system chipset information.\n");

	spin_lock(&tp->lock);
	tg3_flag_set(tp, TX_RECOVERY_PENDING);
	spin_unlock(&tp->lock);
}


static void tg3_frob_aux_power(struct tg3 *tp, bool include_wol)
{
	bool need_vaux = false;

	/* The GPIOs do something completely different on 57765. */
	if (!tg3_flag(tp, IS_NIC) || tg3_flag(tp, 57765_CLASS))
		return;

	if (tg3_asic_rev(tp) == ASIC_REV_5717 ||
	    tg3_asic_rev(tp) == ASIC_REV_5719 ||
	    tg3_asic_rev(tp) == ASIC_REV_5720) {
		tg3_frob_aux_power_5717(tp, include_wol ?
					tg3_flag(tp, WOL_ENABLE) != 0 : 0);
		return;
	}

	if (tp->pdev_peer && tp->pdev_peer != tp->pdev) {
		struct net_device *dev_peer;

		dev_peer = pci_get_drvdata(tp->pdev_peer);

		/* remove_one() may have been run on the peer. */
		if (dev_peer) {
			struct tg3 *tp_peer = netdev_priv(dev_peer);

			if (tg3_flag(tp_peer, INIT_COMPLETE))
				return;

			if ((include_wol && tg3_flag(tp_peer, WOL_ENABLE)) ||
			    tg3_flag(tp_peer, ENABLE_ASF))
				need_vaux = true;
		}
	}

	if ((include_wol && tg3_flag(tp, WOL_ENABLE)) ||
	    tg3_flag(tp, ENABLE_ASF))
		need_vaux = true;

	if (need_vaux)
		tg3_pwrsrc_switch_to_vaux(tp);
	else
		tg3_pwrsrc_die_with_vmain(tp);
}


static void tg3_hwmon_open(struct tg3 *tp)
{
	int i, err;
	u32 size = 0;
	struct pci_dev *pdev = tp->pdev;
	struct tg3_ocir ocirs[TG3_SD_NUM_RECS];

	tg3_sd_scan_scratchpad(tp, ocirs);

	for (i = 0; i < TG3_SD_NUM_RECS; i++) {
		if (!ocirs[i].src_data_length)
			continue;

		size += ocirs[i].src_hdr_length;
		size += ocirs[i].src_data_length;
	}

	if (!size)
		return;

	/* Register hwmon sysfs hooks */
	err = sysfs_create_group(&pdev->dev.kobj, &tg3_group);
	if (err) {
		dev_err(&pdev->dev, "Cannot create sysfs group, aborting\n");
		return;
	}

	tp->hwmon_dev = hwmon_device_register(&pdev->dev);
	if (IS_ERR(tp->hwmon_dev)) {
		tp->hwmon_dev = NULL;
		dev_err(&pdev->dev, "Cannot register hwmon device, aborting\n");
		sysfs_remove_group(&pdev->dev.kobj, &tg3_group);
	}
}


static int tg3_rx_prodring_init(struct tg3 *tp,
				struct tg3_rx_prodring_set *tpr)
{
	tpr->rx_std_buffers = kzalloc(TG3_RX_STD_BUFF_RING_SIZE(tp),
				      GFP_KERNEL);
	if (!tpr->rx_std_buffers)
		return -ENOMEM;

	tpr->rx_std = dma_alloc_coherent(&tp->pdev->dev,
					 TG3_RX_STD_RING_BYTES(tp),
					 &tpr->rx_std_mapping,
					 GFP_KERNEL);
	if (!tpr->rx_std)
		goto err_out;

	if (tg3_flag(tp, JUMBO_CAPABLE) && !tg3_flag(tp, 5780_CLASS)) {
		tpr->rx_jmb_buffers = kzalloc(TG3_RX_JMB_BUFF_RING_SIZE(tp),
					      GFP_KERNEL);
		if (!tpr->rx_jmb_buffers)
			goto err_out;

		tpr->rx_jmb = dma_alloc_coherent(&tp->pdev->dev,
						 TG3_RX_JMB_RING_BYTES(tp),
						 &tpr->rx_jmb_mapping,
						 GFP_KERNEL);
		if (!tpr->rx_jmb)
			goto err_out;
	}

	return 0;

err_out:
	tg3_rx_prodring_fini(tp, tpr);
	return -ENOMEM;
}


static int tg3_set_phys_id(struct net_device *dev,
			    enum ethtool_phys_id_state state)
{
	struct tg3 *tp = netdev_priv(dev);

	if (!netif_running(tp->dev))
		return -EAGAIN;

	switch (state) {
	case ETHTOOL_ID_ACTIVE:
		return 1;	/* cycle on/off once per second */

	case ETHTOOL_ID_ON:
		tw32(MAC_LED_CTRL, LED_CTRL_LNKLED_OVERRIDE |
		     LED_CTRL_1000MBPS_ON |
		     LED_CTRL_100MBPS_ON |
		     LED_CTRL_10MBPS_ON |
		     LED_CTRL_TRAFFIC_OVERRIDE |
		     LED_CTRL_TRAFFIC_BLINK |
		     LED_CTRL_TRAFFIC_LED);
		break;

	case ETHTOOL_ID_OFF:
		tw32(MAC_LED_CTRL, LED_CTRL_LNKLED_OVERRIDE |
		     LED_CTRL_TRAFFIC_OVERRIDE);
		break;

	case ETHTOOL_ID_INACTIVE:
		tw32(MAC_LED_CTRL, tp->led_ctrl);
		break;
	}

	return 0;
}


static bool tg3_phy_copper_an_config_ok(struct tg3 *tp, u32 *lcladv)
{
	u32 advmsk, tgtadv, advertising;

	advertising = tp->link_config.advertising;
	tgtadv = ethtool_adv_to_mii_adv_t(advertising) & ADVERTISE_ALL;

	advmsk = ADVERTISE_ALL;
	if (tp->link_config.active_duplex == DUPLEX_FULL) {
		tgtadv |= mii_advertise_flowctrl(tp->link_config.flowctrl);
		advmsk |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
	}

	if (tg3_readphy(tp, MII_ADVERTISE, lcladv))
		return false;

	if ((*lcladv & advmsk) != tgtadv)
		return false;

	if (!(tp->phy_flags & TG3_PHYFLG_10_100_ONLY)) {
		u32 tg3_ctrl;

		tgtadv = ethtool_adv_to_mii_ctrl1000_t(advertising);

		if (tg3_readphy(tp, MII_CTRL1000, &tg3_ctrl))
			return false;

		if (tgtadv &&
		    (tg3_chip_rev_id(tp) == CHIPREV_ID_5701_A0 ||
		     tg3_chip_rev_id(tp) == CHIPREV_ID_5701_B0)) {
			tgtadv |= CTL1000_AS_MASTER | CTL1000_ENABLE_MASTER;
			tg3_ctrl &= (ADVERTISE_1000HALF | ADVERTISE_1000FULL |
				     CTL1000_AS_MASTER | CTL1000_ENABLE_MASTER);
		} else {
			tg3_ctrl &= (ADVERTISE_1000HALF | ADVERTISE_1000FULL);
		}

		if (tg3_ctrl != tgtadv)
			return false;
	}

	return true;
}


static int tg3_mdio_write(struct mii_bus *bp, int mii_id, int reg, u16 val)
{
	struct tg3 *tp = bp->priv;
	u32 ret = 0;

	spin_lock_bh(&tp->lock);

	if (tg3_writephy(tp, reg, val))
		ret = -EIO;

	spin_unlock_bh(&tp->lock);

	return ret;
}


static void tg3_read_bc_ver(struct tg3 *tp)
{
	u32 val, offset, start, ver_offset;
	int i, dst_off;
	bool newver = false;

	if (tg3_nvram_read(tp, 0xc, &offset) ||
	    tg3_nvram_read(tp, 0x4, &start))
		return;

	offset = tg3_nvram_logical_addr(tp, offset);

	if (tg3_nvram_read(tp, offset, &val))
		return;

	if ((val & 0xfc000000) == 0x0c000000) {
		if (tg3_nvram_read(tp, offset + 4, &val))
			return;

		if (val == 0)
			newver = true;
	}

	dst_off = strlen(tp->fw_ver);

	if (newver) {
		if (TG3_VER_SIZE - dst_off < 16 ||
		    tg3_nvram_read(tp, offset + 8, &ver_offset))
			return;

		offset = offset + ver_offset - start;
		for (i = 0; i < 16; i += 4) {
			__be32 v;
			if (tg3_nvram_read_be32(tp, offset + i, &v))
				return;

			memcpy(tp->fw_ver + dst_off + i, &v, sizeof(v));
		}
	} else {
		u32 major, minor;

		if (tg3_nvram_read(tp, TG3_NVM_PTREV_BCVER, &ver_offset))
			return;

		major = (ver_offset & TG3_NVM_BCVER_MAJMSK) >>
			TG3_NVM_BCVER_MAJSFT;
		minor = ver_offset & TG3_NVM_BCVER_MINMSK;
		snprintf(&tp->fw_ver[dst_off], TG3_VER_SIZE - dst_off,
			 "v%d.%02d", major, minor);
	}
}


static void tg3_get_eeprom_size(struct tg3 *tp)
{
	u32 cursize, val, magic;

	tp->nvram_size = EEPROM_CHIP_SIZE;

	if (tg3_nvram_read(tp, 0, &magic) != 0)
		return;

	if ((magic != TG3_EEPROM_MAGIC) &&
	    ((magic & TG3_EEPROM_MAGIC_FW_MSK) != TG3_EEPROM_MAGIC_FW) &&
	    ((magic & TG3_EEPROM_MAGIC_HW_MSK) != TG3_EEPROM_MAGIC_HW))
		return;

	/*
	 * Size the chip by reading offsets at increasing powers of two.
	 * When we encounter our validation signature, we know the addressing
	 * has wrapped around, and thus have our chip size.
	 */
	cursize = 0x10;

	while (cursize < tp->nvram_size) {
		if (tg3_nvram_read(tp, cursize, &val) != 0)
			return;

		if (val == magic)
			break;

		cursize <<= 1;
	}

	tp->nvram_size = cursize;
}


static int tg3_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
{
	struct tg3 *tp = container_of(ptp, struct tg3, ptp_info);

	tg3_full_lock(tp, 0);
	tp->ptp_adjust += delta;
	tg3_full_unlock(tp);

	return 0;
}


static void tg3_set_bdinfo(struct tg3 *tp, u32 bdinfo_addr,
			   dma_addr_t mapping, u32 maxlen_flags,
			   u32 nic_addr)
{
	tg3_write_mem(tp,
		      (bdinfo_addr + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_HIGH),
		      ((u64) mapping >> 32));
	tg3_write_mem(tp,
		      (bdinfo_addr + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_LOW),
		      ((u64) mapping & 0xffffffff));
	tg3_write_mem(tp,
		      (bdinfo_addr + TG3_BDINFO_MAXLEN_FLAGS),
		       maxlen_flags);

	if (!tg3_flag(tp, 5705_PLUS))
		tg3_write_mem(tp,
			      (bdinfo_addr + TG3_BDINFO_NIC_ADDR),
			      nic_addr);
}


static int tg3_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
	struct mii_ioctl_data *data = if_mii(ifr);
	struct tg3 *tp = netdev_priv(dev);
	int err;

	if (tg3_flag(tp, USE_PHYLIB)) {
		struct phy_device *phydev;
		if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED))
			return -EAGAIN;
		phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR];
		return phy_mii_ioctl(phydev, ifr, cmd);
	}

	switch (cmd) {
	case SIOCGMIIPHY:
		data->phy_id = tp->phy_addr;

		/* fallthru */
	case SIOCGMIIREG: {
		u32 mii_regval;

		if (tp->phy_flags & TG3_PHYFLG_PHY_SERDES)
			break;			/* We have no PHY */

		if (!netif_running(dev))
			return -EAGAIN;

		spin_lock_bh(&tp->lock);
		err = __tg3_readphy(tp, data->phy_id & 0x1f,
				    data->reg_num & 0x1f, &mii_regval);
		spin_unlock_bh(&tp->lock);

		data->val_out = mii_regval;

		return err;
	}

	case SIOCSMIIREG:
		if (tp->phy_flags & TG3_PHYFLG_PHY_SERDES)
			break;			/* We have no PHY */

		if (!netif_running(dev))
			return -EAGAIN;

		spin_lock_bh(&tp->lock);
		err = __tg3_writephy(tp, data->phy_id & 0x1f,
				     data->reg_num & 0x1f, data->val_in);
		spin_unlock_bh(&tp->lock);

		return err;

	case SIOCSHWTSTAMP:
		return tg3_hwtstamp_ioctl(dev, ifr, cmd);

	default:
		/* do nothing */
		break;
	}
	return -EOPNOTSUPP;
}


R_API RBinJavaAttrInfo *r_bin_java_inner_classes_attr_new(ut8 *buffer, ut64 sz, ut64 buf_offset) {
	RBinJavaClassesAttribute *icattr;
	RBinJavaAttrInfo *attr = NULL;
	RBinJavaCPTypeObj *obj;
	ut32 i = 0;
	ut64 offset = 0, curpos;
	attr = r_bin_java_default_attr_new (buffer, sz, buf_offset);
	offset += 6;
	if (attr == NULL) {
		// TODO eprintf
		return attr;
	}
	attr->type = R_BIN_JAVA_ATTR_TYPE_INNER_CLASSES_ATTR;
	attr->info.inner_classes_attr.number_of_classes = R_BIN_JAVA_USHORT (buffer, offset);
	offset += 2;
	attr->info.inner_classes_attr.classes = r_list_newf (r_bin_java_inner_classes_attr_entry_free);
	for (i = 0; i < attr->info.inner_classes_attr.number_of_classes; i++) {
		curpos = buf_offset + offset;
		if (offset + 8 > sz) {
			eprintf ("Invalid amount of inner classes\n");
			break;
		}
		icattr = R_NEW0 (RBinJavaClassesAttribute);
		if (!icattr) {
			break;
		}
		icattr->inner_class_info_idx = R_BIN_JAVA_USHORT (buffer, offset);
		offset += 2;
		icattr->outer_class_info_idx = R_BIN_JAVA_USHORT (buffer, offset);
		offset += 2;
		icattr->inner_name_idx = R_BIN_JAVA_USHORT (buffer, offset);
		offset += 2;
		icattr->inner_class_access_flags = R_BIN_JAVA_USHORT (buffer, offset);
		offset += 2;
		icattr->flags_str = retrieve_class_method_access_string (icattr->inner_class_access_flags);
		icattr->file_offset = curpos;
		icattr->size = 8;

		obj = r_bin_java_get_item_from_bin_cp_list (R_BIN_JAVA_GLOBAL_BIN, icattr->inner_name_idx);
		if (obj == NULL) {
			eprintf ("BINCPLIS IS HULL %d\n", icattr->inner_name_idx);
		}
		icattr->name = r_bin_java_get_item_name_from_bin_cp_list (R_BIN_JAVA_GLOBAL_BIN, obj);
		if (!icattr->name) {
			obj = r_bin_java_get_item_from_bin_cp_list (R_BIN_JAVA_GLOBAL_BIN, icattr->inner_class_info_idx);
			if (!obj) {
				eprintf ("BINCPLIST IS NULL %d\n", icattr->inner_class_info_idx);
			}
			icattr->name = r_bin_java_get_item_name_from_bin_cp_list (R_BIN_JAVA_GLOBAL_BIN, obj);
			if (!icattr->name) {
				icattr->name = r_str_dup (NULL, "NULL");
				eprintf ("r_bin_java_inner_classes_attr: Unable to find the name for %d index.\n", icattr->inner_name_idx);
				break;
			}
		}

		IFDBG eprintf("r_bin_java_inner_classes_attr: Inner class name %d is %s.\n", icattr->inner_name_idx, icattr->name);
		r_list_append (attr->info.inner_classes_attr.classes, (void *) icattr);
	}
	attr->size = offset;
	// IFDBG r_bin_java_print_inner_classes_attr_summary(attr);
	return attr;
}


R_API char *r_bin_java_print_float_cp_stringify(RBinJavaCPTypeObj *obj) {
	ut32 size = 255, consumed = 0;
	char *value = malloc (size);
	if (value) {
		memset (value, 0, size);
		consumed = snprintf (value, size, "%d.0x%04"PFMT64x ".%s.%f",
			obj->metas->ord, obj->file_offset + obj->loadaddr, ((RBinJavaCPTypeMetas *) obj->metas->type_info)->name,
			R_BIN_JAVA_FLOAT (obj->info.cp_float.bytes.raw, 0));
		if (consumed >= size - 1) {
			free (value);
			size += size >> 1;
			value = malloc (size);
			if (value) {
				memset (value, 0, size);
				consumed = snprintf (value, size, "%d.0x%04"PFMT64x ".%s.%f",
					obj->metas->ord, obj->file_offset + obj->loadaddr, ((RBinJavaCPTypeMetas *) obj->metas->type_info)->name,
					R_BIN_JAVA_FLOAT (obj->info.cp_float.bytes.raw, 0));
			}
		}
	}
	return value;
}


R_API RBinJavaAttrInfo *r_bin_java_rtv_annotations_attr_new(ut8 *buffer, ut64 sz, ut64 buf_offset) {
	ut32 i = 0;
	RBinJavaAttrInfo *attr = NULL;
	ut64 offset = 0;
	attr = r_bin_java_default_attr_new (buffer, sz, buf_offset);
	offset += 6;
	if (attr) {
		attr->type = R_BIN_JAVA_ATTR_TYPE_RUNTIME_VISIBLE_ANNOTATION_ATTR;
		attr->info.annotation_array.num_annotations = R_BIN_JAVA_USHORT (buffer, offset);
		offset += 2;
		attr->info.annotation_array.annotations = r_list_newf (r_bin_java_annotation_free);
		for (i = 0; i < attr->info.annotation_array.num_annotations; i++) {
			int len = sz - offset;
			if (len < 1) {
				break;
			}
			RBinJavaAnnotation *annotation = r_bin_java_annotation_new (buffer + offset, len, buf_offset + offset);
			if (annotation) {
				offset += annotation->size;
				r_list_append (attr->info.annotation_array.annotations, (void *) annotation);
			}
		}
		attr->size = offset;
	}
	return attr;
}


R_API RBinJavaStackMapFrameMetas *r_bin_java_determine_stack_frame_type(ut8 tag) {
	ut8 type_value = 0;
	if (tag < 64) {
		type_value = R_BIN_JAVA_STACK_FRAME_SAME;
	} else if (63 < tag && tag < 128) {
		type_value = R_BIN_JAVA_STACK_FRAME_SAME_LOCALS_1;
	} else if (247 < tag && tag < 251) {
		type_value = R_BIN_JAVA_STACK_FRAME_CHOP;
	} else if (tag == 251) {
		type_value = R_BIN_JAVA_STACK_FRAME_SAME_FRAME_EXTENDED;
	} else if (251 < tag && tag < 255) {
		type_value = R_BIN_JAVA_STACK_FRAME_APPEND;
	} else if (tag == 255) {
		type_value = R_BIN_JAVA_STACK_FRAME_FULL_FRAME;
	} else {
		type_value = R_BIN_JAVA_STACK_FRAME_RESERVED;
	}
	return &R_BIN_JAVA_STACK_MAP_FRAME_METAS[type_value];
}


R_API int r_bin_java_new_bin(RBinJavaObj *bin, ut64 loadaddr, Sdb *kv, const ut8 *buf, ut64 len) {
	R_BIN_JAVA_GLOBAL_BIN = bin;
	bin->lines.count = 0;
	bin->loadaddr = loadaddr;
	r_bin_java_get_java_null_cp ();
	bin->id = r_num_rand (UT32_MAX);
	bin->kv = kv ? kv : sdb_new (NULL, NULL, 0);
	bin->AllJavaBinObjs = NULL;
	return r_bin_java_load_bin (bin, buf, len);
}


R_API char *r_bin_java_print_integer_cp_stringify(RBinJavaCPTypeObj *obj) {
	ut32 size = 255, consumed = 0;
	char *value = malloc (size);
	if (value) {
		memset (value, 0, size);
		consumed = snprintf (value, size, "%d.0x%04"PFMT64x ".%s.0x%08x",
			obj->metas->ord, obj->file_offset + obj->loadaddr, ((RBinJavaCPTypeMetas *) obj->metas->type_info)->name,
			R_BIN_JAVA_UINT (obj->info.cp_integer.bytes.raw, 0));
		if (consumed >= size - 1) {
			free (value);
			size += size >> 1;
			value = malloc (size);
			if (value) {
				memset (value, 0, size);
				consumed = snprintf (value, size, "%d.0x%04"PFMT64x ".%s.0x%08x",
					obj->metas->ord, obj->file_offset + obj->loadaddr, ((RBinJavaCPTypeMetas *) obj->metas->type_info)->name,
					R_BIN_JAVA_UINT (obj->info.cp_integer.bytes.raw, 0));
			}
		}
	}
	return value;
}


R_API RBinJavaField *r_bin_java_read_next_field(RBinJavaObj *bin, const ut64 offset, const ut8 *buffer, const ut64 len) {
	RBinJavaAttrInfo *attr;
	ut32 i, idx;
	ut8 buf[8];
	RBinJavaCPTypeObj *item = NULL;
	const ut8 *f_buf = buffer + offset;
	ut64 adv = 0;
	RBinJavaField *field;
	if (bin == NULL) {
		return NULL;
	}
	if (offset + 8 >= len) {
		return NULL;
	}
	field = (RBinJavaField *) R_NEW0 (RBinJavaField);
	if (field == NULL) {
		eprintf ("Unable to allocate memory for field information\n");
		return NULL;
	}
	field->metas = (RBinJavaMetaInfo *) R_NEW0 (RBinJavaMetaInfo);
	if (field->metas == NULL) {
		eprintf ("Unable to allocate memory for meta information\n");
		free (field);
		return NULL;
	}
	memcpy (buf, f_buf, 8);
	field->file_offset = offset;
	field->flags = R_BIN_JAVA_USHORT (buf, 0);
	field->flags_str = retrieve_field_access_string (field->flags);
	field->name_idx = R_BIN_JAVA_USHORT (buf, 2);
	field->descriptor_idx = R_BIN_JAVA_USHORT (buf, 4);
	field->attr_count = R_BIN_JAVA_USHORT (buf, 6);
	field->attributes = r_list_newf (r_bin_java_attribute_free);
	field->type = R_BIN_JAVA_FIELD_TYPE_FIELD;
	adv += 8;
	field->metas->ord = bin->field_idx;

	idx = field->name_idx;
	item = r_bin_java_get_item_from_bin_cp_list (bin, idx);
	field->name = r_bin_java_get_utf8_from_bin_cp_list (bin, (ut32) (field->name_idx));
	IFDBG eprintf("Field name_idx: %d, which is: ord: %d, name: %s, value: %s\n", idx, item->metas->ord, ((RBinJavaCPTypeMetas *)item->metas->type_info)->name, field->name);
	if (field->name == NULL) {
		field->name = (char *) malloc (21);
		snprintf ((char *) field->name, 20, "sym.field_%08x", field->metas->ord);
		IFDBG eprintf("r_bin_java_read_next_field: Unable to find the name for 0x%02x index.\n", field->name_idx);
	}
	idx = field->descriptor_idx;
	item = r_bin_java_get_item_from_bin_cp_list (bin, idx);
	field->descriptor = r_bin_java_get_utf8_from_bin_cp_list (bin, (ut32) field->descriptor_idx);
	IFDBG eprintf("Field descriptor_idx: %d, which is: ord: %d, name: %s, value: %s\n", idx, item->metas->ord, ((RBinJavaCPTypeMetas *)item->metas->type_info)->name, field->descriptor);
	if (field->descriptor == NULL) {
		field->descriptor = r_str_dup (NULL, "NULL");
		IFDBG eprintf("r_bin_java_read_next_field: Unable to find the descriptor for 0x%02x index.\n", field->descriptor_idx);
	}
	IFDBG eprintf("Looking for a NameAndType CP with name_idx: %d descriptor_idx: %d\n", field->name_idx, field->descriptor_idx);
	field->field_ref_cp_obj = r_bin_java_find_cp_ref_info_from_name_and_type (bin, field->name_idx, field->descriptor_idx);
	if (field->field_ref_cp_obj) {
		IFDBG eprintf("Found the obj.\n");
		item = r_bin_java_get_item_from_bin_cp_list (bin, field->field_ref_cp_obj->info.cp_field.class_idx);
		IFDBG eprintf("Field class reference value: %d, which is: ord: %d, name: %s\n", field->field_ref_cp_obj->info.cp_field.class_idx, item->metas->ord, ((RBinJavaCPTypeMetas *)item->metas->type_info)->name);
		field->class_name = r_bin_java_get_item_name_from_bin_cp_list (bin, item);
		IFDBG eprintf("Field requesting ref_cp_obj the following which is: ord: %d, name: %s\n", field->field_ref_cp_obj->metas->ord, ((RBinJavaCPTypeMetas *)field->field_ref_cp_obj->metas->type_info)->name);
		IFDBG eprintf("FieldRef class name resolves to: %s\n", field->class_name);
		if (field->class_name == NULL) {
			field->class_name = r_str_dup (NULL, "NULL");
		}
	} else {
		// XXX - default to this class?
		field->field_ref_cp_obj = r_bin_java_get_item_from_bin_cp_list (bin, bin->cf2.this_class);
		field->class_name = r_bin_java_get_item_name_from_bin_cp_list (bin, field->field_ref_cp_obj);
	}
	IFDBG eprintf("Parsing %s(%s)", field->name, field->descriptor);
	if (field->attr_count > 0) {
		field->attr_offset = adv + offset;
		for (i = 0; i < field->attr_count && offset + adv < len; i++) {
			attr = r_bin_java_read_next_attr (bin, offset + adv, buffer, len);
			if (!attr) {
				eprintf ("[X] r_bin_java: Error unable to parse remainder of classfile after Field Attribute: %d.\n", i);
				return NULL;
			}
			if ((r_bin_java_get_attr_type_by_name (attr->name))->type == R_BIN_JAVA_ATTR_TYPE_CODE_ATTR) {
				// This is necessary for determing the appropriate number of bytes when readin
				// uoffset, ustack, ulocalvar values
				bin->cur_method_code_length = attr->info.code_attr.code_length;
				bin->offset_sz = 2;// (attr->info.code_attr.code_length > 65535) ? 4 : 2;
				bin->ustack_sz = 2;// (attr->info.code_attr.max_stack > 65535) ? 4 : 2;
				bin->ulocalvar_sz = 2;// (attr->info.code_attr.max_locals > 65535) ? 4 : 2;
			}
			r_list_append (field->attributes, attr);
			adv += attr->size;
			if (adv + offset >= len) {
				eprintf ("[X] r_bin_java: Error unable to parse remainder of classfile after Field Attribute: %d.\n", i);
				return NULL;
			}
		}
	}
	field->size = adv;
	return field;
}


R_API char *r_bin_java_print_string_cp_stringify(RBinJavaCPTypeObj *obj) {
	ut32 size = 255, consumed = 0;
	char *value = malloc (size);
	if (value) {
		memset (value, 0, size);
		consumed = snprintf (value, size, "%d.0x%04"PFMT64x ".%s.%d",
			obj->metas->ord, obj->file_offset + obj->loadaddr, ((RBinJavaCPTypeMetas *) obj->metas->type_info)->name,
			obj->info.cp_string.string_idx);
		if (consumed >= size - 1) {
			free (value);
			size += size >> 1;
			value = malloc (size);
			if (value) {
				memset (value, 0, size);
				consumed = snprintf (value, size, "%d.0x%04"PFMT64x ".%s.%d",
					obj->metas->ord, obj->file_offset,
					((RBinJavaCPTypeMetas *) obj->metas->type_info)->name,
					obj->info.cp_string.string_idx);
			}
		}
	}
	return value;
}


R_API RBinJavaAttrInfo *r_bin_java_unknown_attr_new(ut8 *buffer, ut64 sz, ut64 buf_offset) {
	return r_bin_java_default_attr_new (buffer, sz, buf_offset);
}


R_API RList *r_bin_java_get_sections(RBinJavaObj *bin) {
	RBinSection *section = NULL;
	RList *sections = r_list_newf (free);
	ut64 baddr = bin->loadaddr;
	RBinJavaField *fm_type;
	RListIter *iter = NULL;
	if (bin->cp_count > 0) {
		section = R_NEW0 (RBinSection);
		if (section) {
			strcpy (section->name, "constant_pool");
			section->size = bin->cp_size;
			section->paddr = bin->cp_offset + baddr;
			section->srwx = R_BIN_SCN_READABLE;
			section->add = true;
			r_list_append (sections, section);
		}
		section = NULL;
	}
	if (bin->fields_count > 0) {
		section = R_NEW0 (RBinSection);
		if (section) {
			strcpy (section->name, "fields");
			section->size = bin->fields_size;
			section->paddr = bin->fields_offset + baddr;
			section->srwx = R_BIN_SCN_READABLE;
			section->add = true;
			r_list_append (sections, section);
			section = NULL;
			r_list_foreach (bin->fields_list, iter, fm_type) {
				if (fm_type->attr_offset == 0) {
					continue;
				}
				section = R_NEW0 (RBinSection);
				if (section) {
					snprintf (section->name, R_BIN_SIZEOF_STRINGS, "attrs.%s", fm_type->name);
					section->size = fm_type->size - (fm_type->file_offset - fm_type->attr_offset);
					section->paddr = fm_type->attr_offset + baddr;
					section->srwx = R_BIN_SCN_READABLE;
					section->add = true;
					r_list_append (sections, section);
				}
			}
		}
	}
	if (bin->methods_count > 0) {
		section = R_NEW0 (RBinSection);
		if (section) {
			strcpy (section->name, "methods");
			section->size = bin->methods_size;
			section->paddr = bin->methods_offset + baddr;
			section->srwx = R_BIN_SCN_READABLE | R_BIN_SCN_EXECUTABLE;
			section->add = true;
			r_list_append (sections, section);
			section = NULL;
			r_list_foreach (bin->methods_list, iter, fm_type) {
				if (fm_type->attr_offset == 0) {
					continue;
				}
				section = R_NEW0 (RBinSection);
				if (section) {
					snprintf (section->name, R_BIN_SIZEOF_STRINGS, "attrs.%s", fm_type->name);
					section->size = fm_type->size - (fm_type->file_offset - fm_type->attr_offset);
					section->paddr = fm_type->attr_offset + baddr;
					section->srwx = R_BIN_SCN_READABLE;
					section->add = true;
					r_list_append (sections, section);
				}
			}
		}
	}
	if (bin->interfaces_count > 0) {
		section = R_NEW0 (RBinSection);
		if (section) {
			strcpy (section->name, "interfaces");
			section->size = bin->interfaces_size;
			section->paddr = bin->interfaces_offset + baddr;
			section->srwx = R_BIN_SCN_READABLE;
			section->add = true;
			r_list_append (sections, section);
		}
		section = NULL;
	}
	if (bin->attrs_count > 0) {
		section = R_NEW0 (RBinSection);
		if (section) {
			strcpy (section->name, "attributes");
			section->size = bin->attrs_size;
			section->paddr = bin->attrs_offset + baddr;
			section->srwx = R_BIN_SCN_READABLE;
			section->add = true;
			r_list_append (sections, section);
		}
		section = NULL;
	}
	return sections;
}


static void ape_apply_filters(APEContext *ctx, int32_t *decoded0,
                              int32_t *decoded1, int count)
{
    int i;

    for (i = 0; i < APE_FILTER_LEVELS; i++) {
        if (!ape_filter_orders[ctx->fset][i])
            break;
        apply_filter(ctx, ctx->filters[i], decoded0, decoded1, count,
                     ape_filter_orders[ctx->fset][i],
                     ape_filter_fracbits[ctx->fset][i]);
    }
}


static void do_apply_filter(APEContext *ctx, int version, APEFilter *f,
                            int32_t *data, int count, int order, int fracbits)
{
    int res;
    int absres;

    while (count--) {
        /* round fixedpoint scalar product */
        res = ctx->adsp.scalarproduct_and_madd_int16(f->coeffs,
                                                     f->delay - order,
                                                     f->adaptcoeffs - order,
                                                     order, APESIGN(*data));
        res = (res + (1 << (fracbits - 1))) >> fracbits;
        res += *data;
        *data++ = res;

        /* Update the output history */
        *f->delay++ = av_clip_int16(res);

        if (version < 3980) {
            /* Version ??? to < 3.98 files (untested) */
            f->adaptcoeffs[0]  = (res == 0) ? 0 : ((res >> 28) & 8) - 4;
            f->adaptcoeffs[-4] >>= 1;
            f->adaptcoeffs[-8] >>= 1;
        } else {
            /* Version 3.98 and later files */

            /* Update the adaption coefficients */
            absres = FFABS(res);
            if (absres)
                *f->adaptcoeffs = APESIGN(res) *
                                  (8 << ((absres > f->avg * 3) + (absres > f->avg * 4 / 3)));
                /* equivalent to the following code
                    if (absres <= f->avg * 4 / 3)
                        *f->adaptcoeffs = APESIGN(res) * 8;
                    else if (absres <= f->avg * 3)
                        *f->adaptcoeffs = APESIGN(res) * 16;
                    else
                        *f->adaptcoeffs = APESIGN(res) * 32;
                */
            else
                *f->adaptcoeffs = 0;

            f->avg += (absres - f->avg) / 16;

            f->adaptcoeffs[-1] >>= 1;
            f->adaptcoeffs[-2] >>= 1;
            f->adaptcoeffs[-8] >>= 1;
        }

        f->adaptcoeffs++;

        /* Have we filled the history buffer? */
        if (f->delay == f->historybuffer + HISTORY_SIZE + (order * 2)) {
            memmove(f->historybuffer, f->delay - (order * 2),
                    (order * 2) * sizeof(*f->historybuffer));
            f->delay = f->historybuffer + order * 2;
            f->adaptcoeffs = f->historybuffer + order;
        }
    }
}


static void init_predictor_decoder(APEContext *ctx)
{
    APEPredictor *p = &ctx->predictor;

    /* Zero the history buffers */
    memset(p->historybuffer, 0, PREDICTOR_SIZE * sizeof(*p->historybuffer));
    p->buf = p->historybuffer;

    /* Initialize and zero the coefficients */
    if (ctx->fileversion < 3930) {
        if (ctx->compression_level == COMPRESSION_LEVEL_FAST) {
            memcpy(p->coeffsA[0], initial_coeffs_fast_3320,
                   sizeof(initial_coeffs_fast_3320));
            memcpy(p->coeffsA[1], initial_coeffs_fast_3320,
                   sizeof(initial_coeffs_fast_3320));
        } else {
            memcpy(p->coeffsA[0], initial_coeffs_a_3800,
                   sizeof(initial_coeffs_a_3800));
            memcpy(p->coeffsA[1], initial_coeffs_a_3800,
                   sizeof(initial_coeffs_a_3800));
        }
    } else {
        memcpy(p->coeffsA[0], initial_coeffs_3930, sizeof(initial_coeffs_3930));
        memcpy(p->coeffsA[1], initial_coeffs_3930, sizeof(initial_coeffs_3930));
    }
    memset(p->coeffsB, 0, sizeof(p->coeffsB));
    if (ctx->fileversion < 3930) {
        memcpy(p->coeffsB[0], initial_coeffs_b_3800,
               sizeof(initial_coeffs_b_3800));
        memcpy(p->coeffsB[1], initial_coeffs_b_3800,
               sizeof(initial_coeffs_b_3800));
    }

    p->filterA[0] = p->filterA[1] = 0;
    p->filterB[0] = p->filterB[1] = 0;
    p->lastA[0]   = p->lastA[1]   = 0;

    p->sample_pos = 0;
}