int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
			 enum iscsi_param param, char *buf)
{
	struct iscsi_conn *conn = cls_conn->dd_data;
	int len;

	switch(param) {
	case ISCSI_PARAM_PING_TMO:
		len = sysfs_emit(buf, "%u\n", conn->ping_timeout);
		break;
	case ISCSI_PARAM_RECV_TMO:
		len = sysfs_emit(buf, "%u\n", conn->recv_timeout);
		break;
	case ISCSI_PARAM_MAX_RECV_DLENGTH:
		len = sysfs_emit(buf, "%u\n", conn->max_recv_dlength);
		break;
	case ISCSI_PARAM_MAX_XMIT_DLENGTH:
		len = sysfs_emit(buf, "%u\n", conn->max_xmit_dlength);
		break;
	case ISCSI_PARAM_HDRDGST_EN:
		len = sysfs_emit(buf, "%d\n", conn->hdrdgst_en);
		break;
	case ISCSI_PARAM_DATADGST_EN:
		len = sysfs_emit(buf, "%d\n", conn->datadgst_en);
		break;
	case ISCSI_PARAM_IFMARKER_EN:
		len = sysfs_emit(buf, "%d\n", conn->ifmarker_en);
		break;
	case ISCSI_PARAM_OFMARKER_EN:
		len = sysfs_emit(buf, "%d\n", conn->ofmarker_en);
		break;
	case ISCSI_PARAM_EXP_STATSN:
		len = sysfs_emit(buf, "%u\n", conn->exp_statsn);
		break;
	case ISCSI_PARAM_PERSISTENT_PORT:
		len = sysfs_emit(buf, "%d\n", conn->persistent_port);
		break;
	case ISCSI_PARAM_PERSISTENT_ADDRESS:
		len = sysfs_emit(buf, "%s\n", conn->persistent_address);
		break;
	case ISCSI_PARAM_STATSN:
		len = sysfs_emit(buf, "%u\n", conn->statsn);
		break;
	case ISCSI_PARAM_MAX_SEGMENT_SIZE:
		len = sysfs_emit(buf, "%u\n", conn->max_segment_size);
		break;
	case ISCSI_PARAM_KEEPALIVE_TMO:
		len = sysfs_emit(buf, "%u\n", conn->keepalive_tmo);
		break;
	case ISCSI_PARAM_LOCAL_PORT:
		len = sysfs_emit(buf, "%u\n", conn->local_port);
		break;
	case ISCSI_PARAM_TCP_TIMESTAMP_STAT:
		len = sysfs_emit(buf, "%u\n", conn->tcp_timestamp_stat);
		break;
	case ISCSI_PARAM_TCP_NAGLE_DISABLE:
		len = sysfs_emit(buf, "%u\n", conn->tcp_nagle_disable);
		break;
	case ISCSI_PARAM_TCP_WSF_DISABLE:
		len = sysfs_emit(buf, "%u\n", conn->tcp_wsf_disable);
		break;
	case ISCSI_PARAM_TCP_TIMER_SCALE:
		len = sysfs_emit(buf, "%u\n", conn->tcp_timer_scale);
		break;
	case ISCSI_PARAM_TCP_TIMESTAMP_EN:
		len = sysfs_emit(buf, "%u\n", conn->tcp_timestamp_en);
		break;
	case ISCSI_PARAM_IP_FRAGMENT_DISABLE:
		len = sysfs_emit(buf, "%u\n", conn->fragment_disable);
		break;
	case ISCSI_PARAM_IPV4_TOS:
		len = sysfs_emit(buf, "%u\n", conn->ipv4_tos);
		break;
	case ISCSI_PARAM_IPV6_TC:
		len = sysfs_emit(buf, "%u\n", conn->ipv6_traffic_class);
		break;
	case ISCSI_PARAM_IPV6_FLOW_LABEL:
		len = sysfs_emit(buf, "%u\n", conn->ipv6_flow_label);
		break;
	case ISCSI_PARAM_IS_FW_ASSIGNED_IPV6:
		len = sysfs_emit(buf, "%u\n", conn->is_fw_assigned_ipv6);
		break;
	case ISCSI_PARAM_TCP_XMIT_WSF:
		len = sysfs_emit(buf, "%u\n", conn->tcp_xmit_wsf);
		break;
	case ISCSI_PARAM_TCP_RECV_WSF:
		len = sysfs_emit(buf, "%u\n", conn->tcp_recv_wsf);
		break;
	case ISCSI_PARAM_LOCAL_IPADDR:
		len = sysfs_emit(buf, "%s\n", conn->local_ipaddr);
		break;
	default:
		return -ENOSYS;
	}

	return len;
}


};

static umode_t iscsi_flashnode_sess_attr_is_visible(struct kobject *kobj,
						    struct attribute *attr,
						    int i)
{
	struct device *dev = container_of(kobj, struct device, kobj);
	struct iscsi_bus_flash_session *fnode_sess =
						iscsi_dev_to_flash_session(dev);
	struct iscsi_transport *t = fnode_sess->transport;
	int param;

	if (attr == &dev_attr_fnode_auto_snd_tgt_disable.attr) {
		param = ISCSI_FLASHNODE_AUTO_SND_TGT_DISABLE;
	} else if (attr == &dev_attr_fnode_discovery_session.attr) {
		param = ISCSI_FLASHNODE_DISCOVERY_SESS;
	} else if (attr == &dev_attr_fnode_portal_type.attr) {
		param = ISCSI_FLASHNODE_PORTAL_TYPE;
	} else if (attr == &dev_attr_fnode_entry_enable.attr) {
		param = ISCSI_FLASHNODE_ENTRY_EN;
	} else if (attr == &dev_attr_fnode_immediate_data.attr) {
		param = ISCSI_FLASHNODE_IMM_DATA_EN;
	} else if (attr == &dev_attr_fnode_initial_r2t.attr) {
		param = ISCSI_FLASHNODE_INITIAL_R2T_EN;
	} else if (attr == &dev_attr_fnode_data_seq_in_order.attr) {
		param = ISCSI_FLASHNODE_DATASEQ_INORDER;
	} else if (attr == &dev_attr_fnode_data_pdu_in_order.attr) {
		param = ISCSI_FLASHNODE_PDU_INORDER;
	} else if (attr == &dev_attr_fnode_chap_auth.attr) {
		param = ISCSI_FLASHNODE_CHAP_AUTH_EN;
	} else if (attr == &dev_attr_fnode_discovery_logout.attr) {
		param = ISCSI_FLASHNODE_DISCOVERY_LOGOUT_EN;
	} else if (attr == &dev_attr_fnode_bidi_chap.attr) {
		param = ISCSI_FLASHNODE_BIDI_CHAP_EN;
	} else if (attr == &dev_attr_fnode_discovery_auth_optional.attr) {
		param = ISCSI_FLASHNODE_DISCOVERY_AUTH_OPTIONAL;
	} else if (attr == &dev_attr_fnode_erl.attr) {
		param = ISCSI_FLASHNODE_ERL;
	} else if (attr == &dev_attr_fnode_first_burst_len.attr) {
		param = ISCSI_FLASHNODE_FIRST_BURST;
	} else if (attr == &dev_attr_fnode_def_time2wait.attr) {
		param = ISCSI_FLASHNODE_DEF_TIME2WAIT;
	} else if (attr == &dev_attr_fnode_def_time2retain.attr) {
		param = ISCSI_FLASHNODE_DEF_TIME2RETAIN;
	} else if (attr == &dev_attr_fnode_max_outstanding_r2t.attr) {
		param = ISCSI_FLASHNODE_MAX_R2T;
	} else if (attr == &dev_attr_fnode_isid.attr) {
		param = ISCSI_FLASHNODE_ISID;
	} else if (attr == &dev_attr_fnode_tsid.attr) {
		param = ISCSI_FLASHNODE_TSID;
	} else if (attr == &dev_attr_fnode_max_burst_len.attr) {
		param = ISCSI_FLASHNODE_MAX_BURST;
	} else if (attr == &dev_attr_fnode_def_taskmgmt_tmo.attr) {
		param = ISCSI_FLASHNODE_DEF_TASKMGMT_TMO;
	} else if (attr == &dev_attr_fnode_targetalias.attr) {
		param = ISCSI_FLASHNODE_ALIAS;
	} else if (attr == &dev_attr_fnode_targetname.attr) {
		param = ISCSI_FLASHNODE_NAME;
	} else if (attr == &dev_attr_fnode_tpgt.attr) {
		param = ISCSI_FLASHNODE_TPGT;
	} else if (attr == &dev_attr_fnode_discovery_parent_idx.attr) {
		param = ISCSI_FLASHNODE_DISCOVERY_PARENT_IDX;
	} else if (attr == &dev_attr_fnode_discovery_parent_type.attr) {
		param = ISCSI_FLASHNODE_DISCOVERY_PARENT_TYPE;
	} else if (attr == &dev_attr_fnode_chap_in_idx.attr) {
		param = ISCSI_FLASHNODE_CHAP_IN_IDX;
	} else if (attr == &dev_attr_fnode_chap_out_idx.attr) {
		param = ISCSI_FLASHNODE_CHAP_OUT_IDX;
	} else if (attr == &dev_attr_fnode_username.attr) {
		param = ISCSI_FLASHNODE_USERNAME;
	} else if (attr == &dev_attr_fnode_username_in.attr) {
		param = ISCSI_FLASHNODE_USERNAME_IN;
	} else if (attr == &dev_attr_fnode_password.attr) {
		param = ISCSI_FLASHNODE_PASSWORD;
	} else if (attr == &dev_attr_fnode_password_in.attr) {
		param = ISCSI_FLASHNODE_PASSWORD_IN;
	} else if (attr == &dev_attr_fnode_is_boot_target.attr) {
		param = ISCSI_FLASHNODE_IS_BOOT_TGT;
	} else {
		WARN_ONCE(1, "Invalid flashnode session attr");
		return 0;
	}


void iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr)
{
	__iscsi_update_cmdsn(session, be32_to_cpu(hdr->exp_cmdsn),
			     be32_to_cpu(hdr->max_cmdsn));
}


int iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr,
			char *data, uint32_t data_size)
{
	struct iscsi_conn *conn = cls_conn->dd_data;
	struct iscsi_session *session = conn->session;
	int err = 0;

	spin_lock_bh(&session->frwd_lock);
	if (!__iscsi_conn_send_pdu(conn, hdr, data, data_size))
		err = -EPERM;
	spin_unlock_bh(&session->frwd_lock);
	return err;
}


static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
{
	struct iscsi_conn *conn = task->conn;
	struct iscsi_session *session = conn->session;
	struct scsi_cmnd *sc = task->sc;
	struct iscsi_scsi_req *hdr;
	unsigned hdrlength, cmd_len, transfer_length;
	itt_t itt;
	int rc;

	rc = iscsi_check_tmf_restrictions(task, ISCSI_OP_SCSI_CMD);
	if (rc)
		return rc;

	if (conn->session->tt->alloc_pdu) {
		rc = conn->session->tt->alloc_pdu(task, ISCSI_OP_SCSI_CMD);
		if (rc)
			return rc;
	}
	hdr = (struct iscsi_scsi_req *)task->hdr;
	itt = hdr->itt;
	memset(hdr, 0, sizeof(*hdr));

	if (session->tt->parse_pdu_itt)
		hdr->itt = task->hdr_itt = itt;
	else
		hdr->itt = task->hdr_itt = build_itt(task->itt,
						     task->conn->session->age);
	task->hdr_len = 0;
	rc = iscsi_add_hdr(task, sizeof(*hdr));
	if (rc)
		return rc;
	hdr->opcode = ISCSI_OP_SCSI_CMD;
	hdr->flags = ISCSI_ATTR_SIMPLE;
	int_to_scsilun(sc->device->lun, &hdr->lun);
	task->lun = hdr->lun;
	hdr->exp_statsn = cpu_to_be32(conn->exp_statsn);
	cmd_len = sc->cmd_len;
	if (cmd_len < ISCSI_CDB_SIZE)
		memset(&hdr->cdb[cmd_len], 0, ISCSI_CDB_SIZE - cmd_len);
	else if (cmd_len > ISCSI_CDB_SIZE) {
		rc = iscsi_prep_ecdb_ahs(task);
		if (rc)
			return rc;
		cmd_len = ISCSI_CDB_SIZE;
	}
	memcpy(hdr->cdb, sc->cmnd, cmd_len);

	task->imm_count = 0;
	if (scsi_get_prot_op(sc) != SCSI_PROT_NORMAL)
		task->protected = true;

	transfer_length = scsi_transfer_length(sc);
	hdr->data_length = cpu_to_be32(transfer_length);
	if (sc->sc_data_direction == DMA_TO_DEVICE) {
		struct iscsi_r2t_info *r2t = &task->unsol_r2t;

		hdr->flags |= ISCSI_FLAG_CMD_WRITE;
		/*
		 * Write counters:
		 *
		 *	imm_count	bytes to be sent right after
		 *			SCSI PDU Header
		 *
		 *	unsol_count	bytes(as Data-Out) to be sent
		 *			without	R2T ack right after
		 *			immediate data
		 *
		 *	r2t data_length bytes to be sent via R2T ack's
		 *
		 *      pad_count       bytes to be sent as zero-padding
		 */
		memset(r2t, 0, sizeof(*r2t));

		if (session->imm_data_en) {
			if (transfer_length >= session->first_burst)
				task->imm_count = min(session->first_burst,
							conn->max_xmit_dlength);
			else
				task->imm_count = min(transfer_length,
						      conn->max_xmit_dlength);
			hton24(hdr->dlength, task->imm_count);
		} else
			zero_data(hdr->dlength);

		if (!session->initial_r2t_en) {
			r2t->data_length = min(session->first_burst,
					       transfer_length) -
					       task->imm_count;
			r2t->data_offset = task->imm_count;
			r2t->ttt = cpu_to_be32(ISCSI_RESERVED_TAG);
			r2t->exp_statsn = cpu_to_be32(conn->exp_statsn);
		}

		if (!task->unsol_r2t.data_length)
			/* No unsolicit Data-Out's */
			hdr->flags |= ISCSI_FLAG_CMD_FINAL;
	} else {
		hdr->flags |= ISCSI_FLAG_CMD_FINAL;
		zero_data(hdr->dlength);

		if (sc->sc_data_direction == DMA_FROM_DEVICE)
			hdr->flags |= ISCSI_FLAG_CMD_READ;
	}

	/* calculate size of additional header segments (AHSs) */
	hdrlength = task->hdr_len - sizeof(*hdr);

	WARN_ON(hdrlength & (ISCSI_PAD_LEN-1));
	hdrlength /= ISCSI_PAD_LEN;

	WARN_ON(hdrlength >= 256);
	hdr->hlength = hdrlength & 0xFF;
	hdr->cmdsn = task->cmdsn = cpu_to_be32(session->cmdsn);

	if (session->tt->init_task && session->tt->init_task(task))
		return -EIO;

	task->state = ISCSI_TASK_RUNNING;
	session->cmdsn++;

	conn->scsicmd_pdus_cnt++;
	ISCSI_DBG_SESSION(session, "iscsi prep [%s cid %d sc %p cdb 0x%x "
			  "itt 0x%x len %d cmdsn %d win %d]\n",
			  sc->sc_data_direction == DMA_TO_DEVICE ?
			  "write" : "read", conn->id, sc, sc->cmnd[0],
			  task->itt, transfer_length,
			  session->cmdsn,
			  session->max_cmdsn - session->exp_cmdsn + 1);
	return 0;
}


 *  %NULL on failure
 */
struct iscsi_bus_flash_conn *
iscsi_create_flashnode_conn(struct Scsi_Host *shost,
			    struct iscsi_bus_flash_session *fnode_sess,
			    struct iscsi_transport *transport,
			    int dd_size)
{
	struct iscsi_bus_flash_conn *fnode_conn;
	int err;

	fnode_conn = kzalloc(sizeof(*fnode_conn) + dd_size, GFP_KERNEL);
	if (!fnode_conn)
		return NULL;

	fnode_conn->transport = transport;
	fnode_conn->dev.type = &iscsi_flashnode_conn_dev_type;
	fnode_conn->dev.bus = &iscsi_flashnode_bus;
	fnode_conn->dev.parent = &fnode_sess->dev;
	dev_set_name(&fnode_conn->dev, "flashnode_conn-%u:%u:0",
		     shost->host_no, fnode_sess->target_id);

	err = device_register(&fnode_conn->dev);
	if (err)
		goto free_fnode_conn;

	if (dd_size)
		fnode_conn->dd_data = &fnode_conn[1];

	return fnode_conn;

free_fnode_conn:


 *
 * Mark a session as ready to accept IO.
 */
void iscsi_unblock_session(struct iscsi_cls_session *session)
{
	queue_work(iscsi_eh_timer_workq, &session->unblock_work);
	/*
	 * Blocking the session can be done from any context so we only
	 * queue the block work. Make sure the unblock work has completed
	 * because it flushes/cancels the other works and updates the state.


int intel_guc_reset_engine(struct intel_guc *guc,
			   struct intel_engine_cs *engine)
{
	/* XXX: to be implemented with submission interface rework */

	return -ENODEV;
}


static int engine_init_common(struct intel_engine_cs *engine)
{
	struct intel_context *ce;
	int ret;

	engine->set_default_submission(engine);

	/*
	 * We may need to do things with the shrinker which
	 * require us to immediately switch back to the default
	 * context. This can cause a problem as pinning the
	 * default context also requires GTT space which may not
	 * be available. To avoid this we always pin the default
	 * context.
	 */
	ce = create_kernel_context(engine);
	if (IS_ERR(ce))
		return PTR_ERR(ce);

	ret = measure_breadcrumb_dw(ce);
	if (ret < 0)
		goto err_context;

	engine->emit_fini_breadcrumb_dw = ret;
	engine->kernel_context = ce;

	return 0;

err_context:
	intel_context_put(ce);
	return ret;
}


static u32 guc_ads_private_data_size(struct intel_guc *guc)
{
	return PAGE_ALIGN(guc->fw.private_data_size);
}


void intel_uc_fw_cleanup_fetch(struct intel_uc_fw *uc_fw)
{
	if (!intel_uc_fw_is_available(uc_fw))
		return;

	i915_gem_object_put(fetch_and_zero(&uc_fw->obj));

	intel_uc_fw_change_status(uc_fw, INTEL_UC_FIRMWARE_SELECTED);
}


static void intel_engine_print_registers(struct intel_engine_cs *engine,
					 struct drm_printer *m)
{
	struct drm_i915_private *dev_priv = engine->i915;
	struct intel_engine_execlists * const execlists = &engine->execlists;
	u64 addr;

	if (engine->id == RENDER_CLASS && IS_GEN_RANGE(dev_priv, 4, 7))
		drm_printf(m, "\tCCID: 0x%08x\n", ENGINE_READ(engine, CCID));
	if (HAS_EXECLISTS(dev_priv)) {
		drm_printf(m, "\tEL_STAT_HI: 0x%08x\n",
			   ENGINE_READ(engine, RING_EXECLIST_STATUS_HI));
		drm_printf(m, "\tEL_STAT_LO: 0x%08x\n",
			   ENGINE_READ(engine, RING_EXECLIST_STATUS_LO));
	}
	drm_printf(m, "\tRING_START: 0x%08x\n",
		   ENGINE_READ(engine, RING_START));
	drm_printf(m, "\tRING_HEAD:  0x%08x\n",
		   ENGINE_READ(engine, RING_HEAD) & HEAD_ADDR);
	drm_printf(m, "\tRING_TAIL:  0x%08x\n",
		   ENGINE_READ(engine, RING_TAIL) & TAIL_ADDR);
	drm_printf(m, "\tRING_CTL:   0x%08x%s\n",
		   ENGINE_READ(engine, RING_CTL),
		   ENGINE_READ(engine, RING_CTL) & (RING_WAIT | RING_WAIT_SEMAPHORE) ? " [waiting]" : "");
	if (INTEL_GEN(engine->i915) > 2) {
		drm_printf(m, "\tRING_MODE:  0x%08x%s\n",
			   ENGINE_READ(engine, RING_MI_MODE),
			   ENGINE_READ(engine, RING_MI_MODE) & (MODE_IDLE) ? " [idle]" : "");
	}

	if (INTEL_GEN(dev_priv) >= 6) {
		drm_printf(m, "\tRING_IMR:   0x%08x\n",
			   ENGINE_READ(engine, RING_IMR));
		drm_printf(m, "\tRING_ESR:   0x%08x\n",
			   ENGINE_READ(engine, RING_ESR));
		drm_printf(m, "\tRING_EMR:   0x%08x\n",
			   ENGINE_READ(engine, RING_EMR));
		drm_printf(m, "\tRING_EIR:   0x%08x\n",
			   ENGINE_READ(engine, RING_EIR));
	}

	addr = intel_engine_get_active_head(engine);
	drm_printf(m, "\tACTHD:  0x%08x_%08x\n",
		   upper_32_bits(addr), lower_32_bits(addr));
	addr = intel_engine_get_last_batch_head(engine);
	drm_printf(m, "\tBBADDR: 0x%08x_%08x\n",
		   upper_32_bits(addr), lower_32_bits(addr));
	if (INTEL_GEN(dev_priv) >= 8)
		addr = ENGINE_READ64(engine, RING_DMA_FADD, RING_DMA_FADD_UDW);
	else if (INTEL_GEN(dev_priv) >= 4)
		addr = ENGINE_READ(engine, RING_DMA_FADD);
	else
		addr = ENGINE_READ(engine, DMA_FADD_I8XX);
	drm_printf(m, "\tDMA_FADDR: 0x%08x_%08x\n",
		   upper_32_bits(addr), lower_32_bits(addr));
	if (INTEL_GEN(dev_priv) >= 4) {
		drm_printf(m, "\tIPEIR: 0x%08x\n",
			   ENGINE_READ(engine, RING_IPEIR));
		drm_printf(m, "\tIPEHR: 0x%08x\n",
			   ENGINE_READ(engine, RING_IPEHR));
	} else {
		drm_printf(m, "\tIPEIR: 0x%08x\n", ENGINE_READ(engine, IPEIR));
		drm_printf(m, "\tIPEHR: 0x%08x\n", ENGINE_READ(engine, IPEHR));
	}

	if (HAS_EXECLISTS(dev_priv)) {
		struct i915_request * const *port, *rq;
		const u32 *hws =
			&engine->status_page.addr[I915_HWS_CSB_BUF0_INDEX];
		const u8 num_entries = execlists->csb_size;
		unsigned int idx;
		u8 read, write;

		drm_printf(m, "\tExeclist tasklet queued? %s (%s), preempt? %s, timeslice? %s\n",
			   yesno(test_bit(TASKLET_STATE_SCHED,
					  &engine->execlists.tasklet.state)),
			   enableddisabled(!atomic_read(&engine->execlists.tasklet.count)),
			   repr_timer(&engine->execlists.preempt),
			   repr_timer(&engine->execlists.timer));

		read = execlists->csb_head;
		write = READ_ONCE(*execlists->csb_write);

		drm_printf(m, "\tExeclist status: 0x%08x %08x; CSB read:%d, write:%d, entries:%d\n",
			   ENGINE_READ(engine, RING_EXECLIST_STATUS_LO),
			   ENGINE_READ(engine, RING_EXECLIST_STATUS_HI),
			   read, write, num_entries);

		if (read >= num_entries)
			read = 0;
		if (write >= num_entries)
			write = 0;
		if (read > write)
			write += num_entries;
		while (read < write) {
			idx = ++read % num_entries;
			drm_printf(m, "\tExeclist CSB[%d]: 0x%08x, context: %d\n",
				   idx, hws[idx * 2], hws[idx * 2 + 1]);
		}

		execlists_active_lock_bh(execlists);
		rcu_read_lock();
		for (port = execlists->active; (rq = *port); port++) {
			char hdr[160];
			int len;

			len = scnprintf(hdr, sizeof(hdr),
					"\t\tActive[%d]:  ccid:%08x%s%s, ",
					(int)(port - execlists->active),
					rq->context->lrc.ccid,
					intel_context_is_closed(rq->context) ? "!" : "",
					intel_context_is_banned(rq->context) ? "*" : "");
			len += print_ring(hdr + len, sizeof(hdr) - len, rq);
			scnprintf(hdr + len, sizeof(hdr) - len, "rq: ");
			print_request(m, rq, hdr);
		}
		for (port = execlists->pending; (rq = *port); port++) {
			char hdr[160];
			int len;

			len = scnprintf(hdr, sizeof(hdr),
					"\t\tPending[%d]: ccid:%08x%s%s, ",
					(int)(port - execlists->pending),
					rq->context->lrc.ccid,
					intel_context_is_closed(rq->context) ? "!" : "",
					intel_context_is_banned(rq->context) ? "*" : "");
			len += print_ring(hdr + len, sizeof(hdr) - len, rq);
			scnprintf(hdr + len, sizeof(hdr) - len, "rq: ");
			print_request(m, rq, hdr);
		}
		rcu_read_unlock();
		execlists_active_unlock_bh(execlists);
	} else if (INTEL_GEN(dev_priv) > 6) {
		drm_printf(m, "\tPP_DIR_BASE: 0x%08x\n",
			   ENGINE_READ(engine, RING_PP_DIR_BASE));
		drm_printf(m, "\tPP_DIR_BASE_READ: 0x%08x\n",
			   ENGINE_READ(engine, RING_PP_DIR_BASE_READ));
		drm_printf(m, "\tPP_DIR_DCLV: 0x%08x\n",
			   ENGINE_READ(engine, RING_PP_DIR_DCLV));
	}
}


int intel_guc_allocate_and_map_vma(struct intel_guc *guc, u32 size,
				   struct i915_vma **out_vma, void **out_vaddr)
{
	struct i915_vma *vma;
	void *vaddr;

	vma = intel_guc_allocate_vma(guc, size);
	if (IS_ERR(vma))
		return PTR_ERR(vma);

	vaddr = i915_gem_object_pin_map(vma->obj, I915_MAP_WB);
	if (IS_ERR(vaddr)) {
		i915_vma_unpin_and_release(&vma, 0);
		return PTR_ERR(vaddr);
	}

	*out_vma = vma;
	*out_vaddr = vaddr;

	return 0;
}


static int init_status_page(struct intel_engine_cs *engine)
{
	struct drm_i915_gem_object *obj;
	struct i915_vma *vma;
	void *vaddr;
	int ret;

	/*
	 * Though the HWS register does support 36bit addresses, historically
	 * we have had hangs and corruption reported due to wild writes if
	 * the HWS is placed above 4G. We only allow objects to be allocated
	 * in GFP_DMA32 for i965, and no earlier physical address users had
	 * access to more than 4G.
	 */
	obj = i915_gem_object_create_internal(engine->i915, PAGE_SIZE);
	if (IS_ERR(obj)) {
		drm_err(&engine->i915->drm,
			"Failed to allocate status page\n");
		return PTR_ERR(obj);
	}

	i915_gem_object_set_cache_coherency(obj, I915_CACHE_LLC);

	vma = i915_vma_instance(obj, &engine->gt->ggtt->vm, NULL);
	if (IS_ERR(vma)) {
		ret = PTR_ERR(vma);
		goto err;
	}

	vaddr = i915_gem_object_pin_map(obj, I915_MAP_WB);
	if (IS_ERR(vaddr)) {
		ret = PTR_ERR(vaddr);
		goto err;
	}

	engine->status_page.addr = memset(vaddr, 0, PAGE_SIZE);
	engine->status_page.vma = vma;

	if (!HWS_NEEDS_PHYSICAL(engine->i915)) {
		ret = pin_ggtt_status_page(engine, vma);
		if (ret)
			goto err_unpin;
	}

	return 0;

err_unpin:
	i915_gem_object_unpin_map(obj);
err:
	i915_gem_object_put(obj);
	return ret;
}


static void guc_init_params(struct intel_guc *guc)
{
	u32 *params = guc->params;
	int i;

	BUILD_BUG_ON(sizeof(guc->params) != GUC_CTL_MAX_DWORDS * sizeof(u32));

	params[GUC_CTL_LOG_PARAMS] = guc_ctl_log_params_flags(guc);
	params[GUC_CTL_FEATURE] = guc_ctl_feature_flags(guc);
	params[GUC_CTL_DEBUG] = guc_ctl_debug_flags(guc);
	params[GUC_CTL_ADS] = guc_ctl_ads_flags(guc);

	for (i = 0; i < GUC_CTL_MAX_DWORDS; i++)
		DRM_DEBUG_DRIVER("param[%2d] = %#x\n", i, params[i]);
}


static void print_properties(struct intel_engine_cs *engine,
			     struct drm_printer *m)
{
	static const struct pmap {
		size_t offset;
		const char *name;
	} props[] = {
#define P(x) { \
	.offset = offsetof(typeof(engine->props), x), \
	.name = #x \
}
		P(heartbeat_interval_ms),
		P(max_busywait_duration_ns),
		P(preempt_timeout_ms),
		P(stop_timeout_ms),
		P(timeslice_duration_ms),

		{},
#undef P
	};
	const struct pmap *p;

	drm_printf(m, "\tProperties:\n");
	for (p = props; p->name; p++)
		drm_printf(m, "\t\t%s: %lu [default %lu]\n",
			   p->name,
			   read_ul(&engine->props, p->offset),
			   read_ul(&engine->defaults, p->offset));
}


static void gen9_enable_guc_interrupts(struct intel_guc *guc)
{
	struct intel_gt *gt = guc_to_gt(guc);

	assert_rpm_wakelock_held(&gt->i915->runtime_pm);

	spin_lock_irq(&gt->irq_lock);
	if (!guc->interrupts.enabled) {
		WARN_ON_ONCE(intel_uncore_read(gt->uncore, GEN8_GT_IIR(2)) &
			     gt->pm_guc_events);
		guc->interrupts.enabled = true;
		gen6_gt_pm_enable_irq(gt, gt->pm_guc_events);
	}
	spin_unlock_irq(&gt->irq_lock);
}


static void gen11_enable_guc_interrupts(struct intel_guc *guc)
{
	struct intel_gt *gt = guc_to_gt(guc);

	spin_lock_irq(&gt->irq_lock);
	if (!guc->interrupts.enabled) {
		u32 events = REG_FIELD_PREP(ENGINE1_MASK, GUC_INTR_GUC2HOST);

		WARN_ON_ONCE(gen11_gt_reset_one_iir(gt, 0, GEN11_GUC));
		intel_uncore_write(gt->uncore,
				   GEN11_GUC_SG_INTR_ENABLE, events);
		intel_uncore_write(gt->uncore,
				   GEN11_GUC_SG_INTR_MASK, ~events);
		guc->interrupts.enabled = true;
	}
	spin_unlock_irq(&gt->irq_lock);
}


void t2p_validate(T2P* t2p){

#ifdef JPEG_SUPPORT
	if(t2p->pdf_defaultcompression==T2P_COMPRESS_JPEG){
		if(t2p->pdf_defaultcompressionquality>100 ||
			t2p->pdf_defaultcompressionquality<1){
			t2p->pdf_defaultcompressionquality=0;
		}
	}
#endif
#ifdef ZIP_SUPPORT
	if(t2p->pdf_defaultcompression==T2P_COMPRESS_ZIP){
		uint16 m=t2p->pdf_defaultcompressionquality%100;
		if(t2p->pdf_defaultcompressionquality/100 > 9 ||
			(m>1 && m<10) || m>15){
			t2p->pdf_defaultcompressionquality=0;
		}
		if(t2p->pdf_defaultcompressionquality%100 !=0){
			t2p->pdf_defaultcompressionquality/=100;
			t2p->pdf_defaultcompressionquality*=100;
			TIFFError(
				TIFF2PDF_MODULE,
				"PNG Group predictor differencing not implemented, assuming compression quality %u",
				t2p->pdf_defaultcompressionquality);
		}
		t2p->pdf_defaultcompressionquality%=100;
		if(t2p->pdf_minorversion<2){t2p->pdf_minorversion=2;}
	}
#endif
	(void)0;

	return;
}


tsize_t t2p_write_pdf_info(T2P* t2p, TIFF* input, TIFF* output)
{
	tsize_t written = 0;
	const char* info;
	char buffer[512];

	if(t2p->pdf_datetime[0] == '\0')
		t2p_pdf_tifftime(t2p, input);
	if (strlen(t2p->pdf_datetime) > 0) {
		written += t2pWriteFile(output, (tdata_t) "<< \n/CreationDate ", 18);
		written += t2p_write_pdf_string(t2p->pdf_datetime, output);
		written += t2pWriteFile(output, (tdata_t) "\n/ModDate ", 10);
		written += t2p_write_pdf_string(t2p->pdf_datetime, output);
	}
	written += t2pWriteFile(output, (tdata_t) "\n/Producer ", 11);
	snprintf(buffer, sizeof(buffer), "libtiff / tiff2pdf - %d", TIFFLIB_VERSION);
	written += t2p_write_pdf_string(buffer, output);
	written += t2pWriteFile(output, (tdata_t) "\n", 1);
	if (!t2p->pdf_creator_set) {
		if (TIFFGetField(input, TIFFTAG_SOFTWARE, &info) != 0 && info) {
			strncpy(t2p->pdf_creator, info, sizeof(t2p->pdf_creator) - 1);
			t2p->pdf_creator[sizeof(t2p->pdf_creator) - 1] = '\0';
		}
	}
	if (t2p->pdf_creator[0] != '\0') {
		written += t2pWriteFile(output, (tdata_t) "/Creator ", 9);
		written += t2p_write_pdf_string(t2p->pdf_creator, output);
		written += t2pWriteFile(output, (tdata_t) "\n", 1);
	}
	if (!t2p->pdf_author_set) {
		if ((TIFFGetField(input, TIFFTAG_ARTIST, &info) != 0
		     || TIFFGetField(input, TIFFTAG_COPYRIGHT, &info) != 0)
		    && info) {
			strncpy(t2p->pdf_author, info, sizeof(t2p->pdf_author) - 1);
			t2p->pdf_author[sizeof(t2p->pdf_author) - 1] = '\0';
		}
	}
	if (t2p->pdf_author[0] != '\0') {
		written += t2pWriteFile(output, (tdata_t) "/Author ", 8);
		written += t2p_write_pdf_string(t2p->pdf_author, output);
		written += t2pWriteFile(output, (tdata_t) "\n", 1);
	}
	if (!t2p->pdf_title_set) {
		if (TIFFGetField(input, TIFFTAG_DOCUMENTNAME, &info) != 0 && info) {
			strncpy(t2p->pdf_title, info, sizeof(t2p->pdf_title) - 1);
			t2p->pdf_title[sizeof(t2p->pdf_title) - 1] = '\0';
		}
	}
	if (t2p->pdf_title[0] != '\0') {
		written += t2pWriteFile(output, (tdata_t) "/Title ", 7);
		written += t2p_write_pdf_string(t2p->pdf_title, output);
		written += t2pWriteFile(output, (tdata_t) "\n", 1);
	}
	if (!t2p->pdf_subject_set) {
		if (TIFFGetField(input, TIFFTAG_IMAGEDESCRIPTION, &info) != 0 && info) {
			strncpy(t2p->pdf_subject, info, sizeof(t2p->pdf_subject) - 1);
			t2p->pdf_subject[sizeof(t2p->pdf_subject) - 1] = '\0';
		}
	}
	if (t2p->pdf_subject[0] != '\0') {
		written += t2pWriteFile(output, (tdata_t) "/Subject ", 9);
		written += t2p_write_pdf_string(t2p->pdf_subject, output);
		written += t2pWriteFile(output, (tdata_t) "\n", 1);
	}
	if (t2p->pdf_keywords[0] != '\0') {
		written += t2pWriteFile(output, (tdata_t) "/Keywords ", 10);
		written += t2p_write_pdf_string(t2p->pdf_keywords, output);
		written += t2pWriteFile(output, (tdata_t) "\n", 1);
	}
	written += t2pWriteFile(output, (tdata_t) ">> \n", 4);

	return(written);
}


tsize_t t2p_write_pdf_xobject_calcs(T2P* t2p, TIFF* output){

	tsize_t written=0;
	char buffer[256];
	int buflen=0;

	float X_W=0.0;
	float Y_W=0.0;
	float Z_W=0.0;
	float X_R=0.0;
	float Y_R=0.0;
	float Z_R=0.0;
	float X_G=0.0;
	float Y_G=0.0;
	float Z_G=0.0;
	float X_B=0.0;
	float Y_B=0.0;
	float Z_B=0.0;
	float x_w=0.0;
	float y_w=0.0;
	float z_w=0.0;
	float x_r=0.0;
	float y_r=0.0;
	float x_g=0.0;
	float y_g=0.0;
	float x_b=0.0;
	float y_b=0.0;
	float R=1.0;
	float G=1.0;
	float B=1.0;

	written += t2pWriteFile(output, (tdata_t) "[", 1);
	if(t2p->pdf_colorspace & T2P_CS_CALGRAY){
		written += t2pWriteFile(output, (tdata_t) "/CalGray ", 9);
		X_W = t2p->tiff_whitechromaticities[0];
		Y_W = t2p->tiff_whitechromaticities[1];
		Z_W = 1.0F - (X_W + Y_W);
		normalizePoint(X_W, Y_W, Z_W);
	}
	if(t2p->pdf_colorspace & T2P_CS_CALRGB){
		written += t2pWriteFile(output, (tdata_t) "/CalRGB ", 8);
		x_w = t2p->tiff_whitechromaticities[0];
		y_w = t2p->tiff_whitechromaticities[1];
		x_r = t2p->tiff_primarychromaticities[0];
		y_r = t2p->tiff_primarychromaticities[1];
		x_g = t2p->tiff_primarychromaticities[2];
		y_g = t2p->tiff_primarychromaticities[3];
		x_b = t2p->tiff_primarychromaticities[4];
		y_b = t2p->tiff_primarychromaticities[5];
		z_w = y_w * ((x_g - x_b)*y_r - (x_r-x_b)*y_g + (x_r-x_g)*y_b);
		Y_R = (y_r/R) * ((x_g-x_b)*y_w - (x_w-x_b)*y_g + (x_w-x_g)*y_b) / z_w;
		X_R = Y_R * x_r / y_r;
		Z_R = Y_R * (((1-x_r)/y_r)-1);
		Y_G = ((0.0F-(y_g))/G) * ((x_r-x_b)*y_w - (x_w-x_b)*y_r + (x_w-x_r)*y_b) / z_w;
		X_G = Y_G * x_g / y_g;
		Z_G = Y_G * (((1-x_g)/y_g)-1);
		Y_B = (y_b/B) * ((x_r-x_g)*y_w - (x_w-x_g)*y_r + (x_w-x_r)*y_g) / z_w;
		X_B = Y_B * x_b / y_b;
		Z_B = Y_B * (((1-x_b)/y_b)-1);
		X_W = (X_R * R) + (X_G * G) + (X_B * B);
		Y_W = (Y_R * R) + (Y_G * G) + (Y_B * B);
		Z_W = (Z_R * R) + (Z_G * G) + (Z_B * B);
		normalizePoint(X_W, Y_W, Z_W);
	}
	written += t2pWriteFile(output, (tdata_t) "<< \n", 4);
	if(t2p->pdf_colorspace & T2P_CS_CALGRAY){
		written += t2pWriteFile(output, (tdata_t) "/WhitePoint ", 12);
		buflen=snprintf(buffer, sizeof(buffer), "[%.4f %.4f %.4f] \n", X_W, Y_W, Z_W);
		check_snprintf_ret(t2p, buflen, buffer);
		written += t2pWriteFile(output, (tdata_t) buffer, buflen);
		written += t2pWriteFile(output, (tdata_t) "/Gamma 2.2 \n", 12);
	}
	if(t2p->pdf_colorspace & T2P_CS_CALRGB){
		written += t2pWriteFile(output, (tdata_t) "/WhitePoint ", 12);
		buflen=snprintf(buffer, sizeof(buffer), "[%.4f %.4f %.4f] \n", X_W, Y_W, Z_W);
		check_snprintf_ret(t2p, buflen, buffer);
		written += t2pWriteFile(output, (tdata_t) buffer, buflen);
		written += t2pWriteFile(output, (tdata_t) "/Matrix ", 8);
		buflen=snprintf(buffer, sizeof(buffer), "[%.4f %.4f %.4f %.4f %.4f %.4f %.4f %.4f %.4f] \n",
			X_R, Y_R, Z_R,
			X_G, Y_G, Z_G,
			X_B, Y_B, Z_B);
		check_snprintf_ret(t2p, buflen, buffer);
		written += t2pWriteFile(output, (tdata_t) buffer, buflen);
		written += t2pWriteFile(output, (tdata_t) "/Gamma [2.2 2.2 2.2] \n", 22);
	}
	written += t2pWriteFile(output, (tdata_t) ">>] \n", 5);

	return(written);
}


void *zmalloc_usable(size_t size, size_t *usable) {
    void *ptr = ztrymalloc_usable(size, usable);
    if (!ptr) zmalloc_oom_handler(size);
    return ptr;
}


static ssize_t qemu_net_queue_deliver_iov(NetQueue *queue,
                                          NetClientState *sender,
                                          unsigned flags,
                                          const struct iovec *iov,
                                          int iovcnt)
{
    ssize_t ret = -1;

    queue->delivering = 1;
    ret = queue->deliver(sender, flags, iov, iovcnt, queue->opaque);
    queue->delivering = 0;

    return ret;
}


static void qemu_net_queue_append(NetQueue *queue,
                                  NetClientState *sender,
                                  unsigned flags,
                                  const uint8_t *buf,
                                  size_t size,
                                  NetPacketSent *sent_cb)
{
    NetPacket *packet;

    if (queue->nq_count >= queue->nq_maxlen && !sent_cb) {
        return; /* drop if queue full and no callback */
    }
    packet = g_malloc(sizeof(NetPacket) + size);
    packet->sender = sender;
    packet->flags = flags;
    packet->size = size;
    packet->sent_cb = sent_cb;
    memcpy(packet->data, buf, size);

    queue->nq_count++;
    QTAILQ_INSERT_TAIL(&queue->packets, packet, entry);
}


bool qemu_net_queue_flush(NetQueue *queue)
{
    if (queue->delivering)
        return false;

    while (!QTAILQ_EMPTY(&queue->packets)) {
        NetPacket *packet;
        int ret;

        packet = QTAILQ_FIRST(&queue->packets);
        QTAILQ_REMOVE(&queue->packets, packet, entry);
        queue->nq_count--;

        ret = qemu_net_queue_deliver(queue,
                                     packet->sender,
                                     packet->flags,
                                     packet->data,
                                     packet->size);
        if (ret == 0) {
            queue->nq_count++;
            QTAILQ_INSERT_HEAD(&queue->packets, packet, entry);
            return false;
        }

        if (packet->sent_cb) {
            packet->sent_cb(packet->sender, ret);
        }

        g_free(packet);
    }
    return true;
}


static uint16_t read_from_phy(MSF2EmacState *s)
{
    uint8_t reg_addr = s->regs[R_MII_ADDR] & R_MII_ADDR_REGADDR_MASK;
    uint8_t phy_addr = (s->regs[R_MII_ADDR] >> R_MII_ADDR_PHYADDR_SHIFT) &
                       R_MII_ADDR_REGADDR_MASK;

    if (phy_addr == PHYADDR) {
        return s->phy_regs[reg_addr];
    } else {
        return 0xFFFF;
    }
}


static void msf2_dma_tx(MSF2EmacState *s)
{
    NetClientState *nc = qemu_get_queue(s->nic);
    hwaddr desc = s->regs[R_DMA_TX_DESC];
    uint8_t buf[MAX_PKT_SIZE];
    EmacDesc d;
    int size;
    uint8_t pktcnt;
    uint32_t status;

    if (!(s->regs[R_CFG1] & R_CFG1_TX_EN_MASK)) {
        return;
    }

    while (1) {
        emac_load_desc(s, &d, desc);
        if (d.pktsize & EMPTY_MASK) {
            break;
        }
        size = d.pktsize & PKT_SIZE;
        address_space_read(&s->dma_as, d.pktaddr, MEMTXATTRS_UNSPECIFIED,
                           buf, size);
        /*
         * This is very basic way to send packets. Ideally there should be
         * a FIFO and packets should be sent out from FIFO only when
         * R_CFG1 bit 0 is set.
         */
        if (s->regs[R_CFG1] & R_CFG1_LB_EN_MASK) {
            qemu_receive_packet(nc, buf, size);
        } else {
            qemu_send_packet(nc, buf, size);
        }
        d.pktsize |= EMPTY_MASK;
        emac_store_desc(s, &d, desc);
        /* update sent packets count */
        status = s->regs[R_DMA_TX_STATUS];
        pktcnt = FIELD_EX32(status, DMA_TX_STATUS, PKTCNT);
        pktcnt++;
        s->regs[R_DMA_TX_STATUS] = FIELD_DP32(status, DMA_TX_STATUS,
                                              PKTCNT, pktcnt);
        s->regs[R_DMA_TX_STATUS] |= R_DMA_TX_STATUS_PKT_SENT_MASK;
        desc = d.next;
    }
    s->regs[R_DMA_TX_STATUS] |= R_DMA_TX_STATUS_UNDERRUN_MASK;
    s->regs[R_DMA_TX_CTL] &= ~R_DMA_TX_CTL_EN_MASK;
}


static void sungem_mmio_txdma_write(void *opaque, hwaddr addr, uint64_t val,
                                    unsigned size)
{
    SunGEMState *s = opaque;

    if (!(addr < 0x38) && !(addr >= 0x100 && addr <= 0x118)) {
        qemu_log_mask(LOG_GUEST_ERROR,
                      "Write to unknown TXDMA register 0x%"HWADDR_PRIx"\n",
                      addr);
        return;
    }

    trace_sungem_mmio_txdma_write(addr, val);

    /* Pre-write filter */
    switch (addr) {
    /* Read only registers */
    case TXDMA_TXDONE:
    case TXDMA_PCNT:
    case TXDMA_SMACHINE:
    case TXDMA_DPLOW:
    case TXDMA_DPHI:
    case TXDMA_FSZ:
    case TXDMA_FTAG:
        return; /* No actual write */
    }

    s->txdmaregs[addr >> 2] = val;

    /* Post write action */
    switch (addr) {
    case TXDMA_KICK:
        sungem_tx_kick(s);
        break;
    case TXDMA_CFG:
        sungem_update_masks(s);
        break;
    }
}


static void sungem_reset_tx(SunGEMState *s)
{
    trace_sungem_tx_reset();

    /* XXX Do TXCFG */
    /* XXX Check value */
    s->txdmaregs[TXDMA_FSZ >> 2] = 0x90;
    s->txdmaregs[TXDMA_TXDONE >> 2] = 0;
    s->txdmaregs[TXDMA_KICK >> 2] = 0;
    s->txdmaregs[TXDMA_CFG >> 2] = 0x118010;

    sungem_update_masks(s);

    s->tx_size = 0;
    s->tx_first_ctl = 0;
}


static uint64_t sungem_mmio_mac_read(void *opaque, hwaddr addr, unsigned size)
{
    SunGEMState *s = opaque;
    uint32_t val;

    if (!(addr <= 0x134)) {
        qemu_log_mask(LOG_GUEST_ERROR,
                      "Read from unknown MAC register 0x%"HWADDR_PRIx"\n",
                      addr);
        return 0;
    }

    val = s->macregs[addr >> 2];

    trace_sungem_mmio_mac_read(addr, val);

    switch (addr) {
    case MAC_TXSTAT:
        /* Side effect, clear all */
        s->macregs[addr >> 2] = 0;
        sungem_update_status(s, GREG_STAT_TXMAC, false);
        break;
    case MAC_RXSTAT:
        /* Side effect, clear all */
        s->macregs[addr >> 2] = 0;
        sungem_update_status(s, GREG_STAT_RXMAC, false);
        break;
    case MAC_CSTAT:
        /* Side effect, interrupt bits */
        s->macregs[addr >> 2] &= MAC_CSTAT_PTR;
        sungem_update_status(s, GREG_STAT_MAC, false);
        break;
    }

    return val;
}


static void sungem_eval_irq(SunGEMState *s)
{
    uint32_t stat, mask;

    mask = s->gregs[GREG_IMASK >> 2];
    stat = s->gregs[GREG_STAT >> 2] & ~GREG_STAT_TXNR;
    if (stat & ~mask) {
        pci_set_irq(PCI_DEVICE(s), 1);
    } else {
        pci_set_irq(PCI_DEVICE(s), 0);
    }
}


static void sungem_reset(DeviceState *dev)
{
    SunGEMState *s = SUNGEM(dev);

    sungem_reset_all(s, true);
}


static void rtl8139_IntrStatus_write(RTL8139State *s, uint32_t val)
{
    DPRINTF("IntrStatus write(w) val=0x%04x\n", val);

#if 0

    /* writing to ISR has no effect */

    return;

#else
    uint16_t newStatus = s->IntrStatus & ~val;

    /* mask unwritable bits */
    newStatus = SET_MASKED(newStatus, 0x1e00, s->IntrStatus);

    /* writing 1 to interrupt status register bit clears it */
    s->IntrStatus = 0;
    rtl8139_update_irq(s);

    s->IntrStatus = newStatus;
    rtl8139_set_next_tctr_time(s);
    rtl8139_update_irq(s);

#endif
}


static void rtl8139_BasicModeCtrl_write(RTL8139State *s, uint32_t val)
{
    val &= 0xffff;

    DPRINTF("BasicModeCtrl register write(w) val=0x%04x\n", val);

    /* mask unwritable bits */
    uint32_t mask = 0xccff;

    if (1 || !rtl8139_config_writable(s))
    {
        /* Speed setting and autonegotiation enable bits are read-only */
        mask |= 0x3000;
        /* Duplex mode setting is read-only */
        mask |= 0x0100;
    }

    if (val & 0x8000) {
        /* Reset PHY */
        rtl8139_reset_phy(s);
    }

    val = SET_MASKED(val, mask, s->BasicModeCtrl);

    s->BasicModeCtrl = val;
}


static uint16_t ones_complement_sum(uint8_t *data, size_t len)
{
    uint32_t result = 0;

    for (; len > 1; data+=2, len-=2)
    {
        result += *(uint16_t*)data;
    }

    /* add the remainder byte */
    if (len)
    {
        uint8_t odd[2] = {*data, 0};
        result += *(uint16_t*)odd;
    }

    while (result>>16)
        result = (result & 0xffff) + (result >> 16);

    return result;
}


static void rtl8139_transfer_frame(RTL8139State *s, uint8_t *buf, int size,
    int do_interrupt, const uint8_t *dot1q_buf)
{
    struct iovec *iov = NULL;
    struct iovec vlan_iov[3];

    if (!size)
    {
        DPRINTF("+++ empty ethernet frame\n");
        return;
    }

    if (dot1q_buf && size >= ETH_ALEN * 2) {
        iov = (struct iovec[3]) {
            { .iov_base = buf, .iov_len = ETH_ALEN * 2 },
            { .iov_base = (void *) dot1q_buf, .iov_len = VLAN_HLEN },
            { .iov_base = buf + ETH_ALEN * 2,
                .iov_len = size - ETH_ALEN * 2 },
        };

        memcpy(vlan_iov, iov, sizeof(vlan_iov));
        iov = vlan_iov;
    }

    if (TxLoopBack == (s->TxConfig & TxLoopBack))
    {
        size_t buf2_size;
        uint8_t *buf2;

        if (iov) {
            buf2_size = iov_size(iov, 3);
            buf2 = g_malloc(buf2_size);
            iov_to_buf(iov, 3, 0, buf2, buf2_size);
            buf = buf2;
        }

        DPRINTF("+++ transmit loopback mode\n");
        qemu_receive_packet(qemu_get_queue(s->nic), buf, size);

        if (iov) {
            g_free(buf2);
        }
    }
    else
    {
        if (iov) {
            qemu_sendv_packet(qemu_get_queue(s->nic), iov, 3);
        } else {
            qemu_send_packet(qemu_get_queue(s->nic), buf, size);
        }
    }
}


static void rtl8139_RxBuf_write(RTL8139State *s, uint32_t val)
{
    DPRINTF("RxBuf write val=0x%08x\n", val);

    s->RxBuf = val;

    /* may need to reset rxring here */
}


static uint32_t rtl8139_BasicModeCtrl_read(RTL8139State *s)
{
    uint32_t ret = s->BasicModeCtrl;

    DPRINTF("BasicModeCtrl register read(w) val=0x%04x\n", ret);

    return ret;
}


static void rtl8139_reset(DeviceState *d)
{
    RTL8139State *s = RTL8139(d);
    int i;

    /* restore MAC address */
    memcpy(s->phys, s->conf.macaddr.a, 6);
    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->phys);

    /* reset interrupt mask */
    s->IntrStatus = 0;
    s->IntrMask = 0;

    rtl8139_update_irq(s);

    /* mark all status registers as owned by host */
    for (i = 0; i < 4; ++i)
    {
        s->TxStatus[i] = TxHostOwns;
    }

    s->currTxDesc = 0;
    s->currCPlusRxDesc = 0;
    s->currCPlusTxDesc = 0;

    s->RxRingAddrLO = 0;
    s->RxRingAddrHI = 0;

    s->RxBuf = 0;

    rtl8139_reset_rxring(s, 8192);

    /* ACK the reset */
    s->TxConfig = 0;

#if 0
//    s->TxConfig |= HW_REVID(1, 0, 0, 0, 0, 0, 0); // RTL-8139  HasHltClk
    s->clock_enabled = 0;
#else
    s->TxConfig |= HW_REVID(1, 1, 1, 0, 1, 1, 0); // RTL-8139C+ HasLWake
    s->clock_enabled = 1;
#endif

    s->bChipCmdState = CmdReset; /* RxBufEmpty bit is calculated on read from ChipCmd */;

    /* set initial state data */
    s->Config0 = 0x0; /* No boot ROM */
    s->Config1 = 0xC; /* IO mapped and MEM mapped registers available */
    s->Config3 = 0x1; /* fast back-to-back compatible */
    s->Config5 = 0x0;

    s->CpCmd   = 0x0; /* reset C+ mode */
    s->cplus_enabled = 0;

//    s->BasicModeCtrl = 0x3100; // 100Mbps, full duplex, autonegotiation
//    s->BasicModeCtrl = 0x2100; // 100Mbps, full duplex
    s->BasicModeCtrl = 0x1000; // autonegotiation

    rtl8139_reset_phy(s);

    /* also reset timer and disable timer interrupt */
    s->TCTR = 0;
    s->TimerInt = 0;
    s->TCTR_base = 0;
    rtl8139_set_next_tctr_time(s);

    /* reset tally counters */
    RTL8139TallyCounters_clear(&s->tally_counters);
}


static uint32_t rtl8139_TxAddr_read(RTL8139State *s, uint32_t txAddrOffset)
{
    uint32_t ret = s->TxAddr[txAddrOffset/4];

    DPRINTF("TxAddr read offset=0x%x val=0x%08x\n", txAddrOffset, ret);

    return ret;
}


static ssize_t rtl8139_receive(NetClientState *nc, const uint8_t *buf, size_t size)
{
    return rtl8139_do_receive(nc, buf, size, 1);
}


static uint32_t rtl8139_IntrMask_read(RTL8139State *s)
{
    uint32_t ret = s->IntrMask;

    DPRINTF("IntrMask read(w) val=0x%04x\n", ret);

    return ret;
}


static void pcnet_transmit(PCNetState *s)
{
    hwaddr xmit_cxda = 0;
    int count = CSR_XMTRL(s)-1;
    int add_crc = 0;
    int bcnt;
    s->xmit_pos = -1;

    if (!CSR_TXON(s)) {
        s->csr[0] &= ~0x0008;
        return;
    }

    s->tx_busy = 1;

txagain:
    if (pcnet_tdte_poll(s)) {
        struct pcnet_TMD tmd;

        TMDLOAD(&tmd, PHYSADDR(s,CSR_CXDA(s)));

#ifdef PCNET_DEBUG_TMD
        printf("  TMDLOAD 0x%08x\n", PHYSADDR(s,CSR_CXDA(s)));
        PRINT_TMD(&tmd);
#endif
        if (GET_FIELD(tmd.status, TMDS, STP)) {
            s->xmit_pos = 0;
            xmit_cxda = PHYSADDR(s,CSR_CXDA(s));
            if (BCR_SWSTYLE(s) != 1)
                add_crc = GET_FIELD(tmd.status, TMDS, ADDFCS);
        }
        if (s->lnkst == 0 &&
            (!CSR_LOOP(s) || (!CSR_INTL(s) && !BCR_TMAULOOP(s)))) {
            SET_FIELD(&tmd.misc, TMDM, LCAR, 1);
            SET_FIELD(&tmd.status, TMDS, ERR, 1);
            SET_FIELD(&tmd.status, TMDS, OWN, 0);
            s->csr[0] |= 0xa000; /* ERR | CERR */
            s->xmit_pos = -1;
            goto txdone;
        }

        if (s->xmit_pos < 0) {
            goto txdone;
        }

        bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT);

        /* if multi-tmd packet outsizes s->buffer then skip it silently.
         * Note: this is not what real hw does.
         * Last four bytes of s->buffer are used to store CRC FCS code.
         */
        if (s->xmit_pos + bcnt > sizeof(s->buffer) - 4) {
            s->xmit_pos = -1;
            goto txdone;
        }

        s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr),
                         s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s));
        s->xmit_pos += bcnt;

        if (!GET_FIELD(tmd.status, TMDS, ENP)) {
            goto txdone;
        }

#ifdef PCNET_DEBUG
        printf("pcnet_transmit size=%d\n", s->xmit_pos);
#endif
        if (CSR_LOOP(s)) {
            if (BCR_SWSTYLE(s) == 1)
                add_crc = !GET_FIELD(tmd.status, TMDS, NOFCS);
            s->looptest = add_crc ? PCNET_LOOPTEST_CRC : PCNET_LOOPTEST_NOCRC;
            qemu_receive_packet(qemu_get_queue(s->nic), s->buffer, s->xmit_pos);
            s->looptest = 0;
        } else {
            if (s->nic) {
                qemu_send_packet(qemu_get_queue(s->nic), s->buffer,
                                 s->xmit_pos);
            }
        }

        s->csr[0] &= ~0x0008;   /* clear TDMD */
        s->csr[4] |= 0x0004;    /* set TXSTRT */
        s->xmit_pos = -1;

txdone:
        SET_FIELD(&tmd.status, TMDS, OWN, 0);
        TMDSTORE(&tmd, PHYSADDR(s,CSR_CXDA(s)));
        if (!CSR_TOKINTD(s)
            || (CSR_LTINTEN(s) && GET_FIELD(tmd.status, TMDS, LTINT))) {
            s->csr[0] |= 0x0200;    /* set TINT */
        }
        if (CSR_XMTRC(s) <= 1) {
            CSR_XMTRC(s) = CSR_XMTRL(s);
        } else {
            CSR_XMTRC(s)--;
        }
        if (count--) {
            goto txagain;
        }
    } else if (s->xmit_pos >= 0) {
        struct pcnet_TMD tmd;
        TMDLOAD(&tmd, xmit_cxda);
        SET_FIELD(&tmd.misc, TMDM, BUFF, 1);
        SET_FIELD(&tmd.misc, TMDM, UFLO, 1);
        SET_FIELD(&tmd.status, TMDS, ERR, 1);
        SET_FIELD(&tmd.status, TMDS, OWN, 0);
        TMDSTORE(&tmd, xmit_cxda);
        s->csr[0] |= 0x0200;    /* set TINT */
        if (!CSR_DXSUFLO(s)) {
            s->csr[0] &= ~0x0010;
        } else if (count--) {
            goto txagain;
        }
    }

    s->tx_busy = 0;
}


ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_)
{
    PCNetState *s = qemu_get_nic_opaque(nc);
    int is_padr = 0, is_bcast = 0, is_ladr = 0;
    uint8_t buf1[60];
    int remaining;
    int crc_err = 0;
    size_t size = size_;

    if (CSR_DRX(s) || CSR_STOP(s) || CSR_SPND(s) || !size ||
        (CSR_LOOP(s) && !s->looptest)) {
        return -1;
    }
#ifdef PCNET_DEBUG
    printf("pcnet_receive size=%zu\n", size);
#endif

    /* if too small buffer, then expand it */
    if (size < MIN_BUF_SIZE) {
        memcpy(buf1, buf, size);
        memset(buf1 + size, 0, MIN_BUF_SIZE - size);
        buf = buf1;
        size = MIN_BUF_SIZE;
    }

    if (CSR_PROM(s)
        || (is_padr=padr_match(s, buf, size))
        || (is_bcast=padr_bcast(s, buf, size))
        || (is_ladr=ladr_match(s, buf, size))) {

        pcnet_rdte_poll(s);

        if (!(CSR_CRST(s) & 0x8000) && s->rdra) {
            struct pcnet_RMD rmd;
            int rcvrc = CSR_RCVRC(s)-1,i;
            hwaddr nrda;
            for (i = CSR_RCVRL(s)-1; i > 0; i--, rcvrc--) {
                if (rcvrc <= 1)
                    rcvrc = CSR_RCVRL(s);
                nrda = s->rdra +
                    (CSR_RCVRL(s) - rcvrc) *
                    (BCR_SWSTYLE(s) ? 16 : 8 );
                RMDLOAD(&rmd, nrda);
                if (GET_FIELD(rmd.status, RMDS, OWN)) {
#ifdef PCNET_DEBUG_RMD
                    printf("pcnet - scan buffer: RCVRC=%d PREV_RCVRC=%d\n",
                                rcvrc, CSR_RCVRC(s));
#endif
                    CSR_RCVRC(s) = rcvrc;
                    pcnet_rdte_poll(s);
                    break;
                }
            }
        }

        if (!(CSR_CRST(s) & 0x8000)) {
#ifdef PCNET_DEBUG_RMD
            printf("pcnet - no buffer: RCVRC=%d\n", CSR_RCVRC(s));
#endif
            s->csr[0] |= 0x1000; /* Set MISS flag */
            CSR_MISSC(s)++;
        } else {
            uint8_t *src = s->buffer;
            hwaddr crda = CSR_CRDA(s);
            struct pcnet_RMD rmd;
            int pktcount = 0;

            if (!s->looptest) {
                if (size > 4092) {
#ifdef PCNET_DEBUG_RMD
                    fprintf(stderr, "pcnet: truncates rx packet.\n");
#endif
                    size = 4092;
                }
                memcpy(src, buf, size);
                /* no need to compute the CRC */
                src[size] = 0;
                src[size + 1] = 0;
                src[size + 2] = 0;
                src[size + 3] = 0;
                size += 4;
            } else if (s->looptest == PCNET_LOOPTEST_CRC ||
                       !CSR_DXMTFCS(s) || size < MIN_BUF_SIZE+4) {
                uint32_t fcs = ~0;
                uint8_t *p = src;

                while (p != &src[size])
                    CRC(fcs, *p++);
                *(uint32_t *)p = htonl(fcs);
                size += 4;
            } else {
                uint32_t fcs = ~0;
                uint8_t *p = src;

                while (p != &src[size])
                    CRC(fcs, *p++);
                crc_err = (*(uint32_t *)p != htonl(fcs));
            }

#ifdef PCNET_DEBUG_MATCH
            PRINT_PKTHDR(buf);
#endif

            RMDLOAD(&rmd, PHYSADDR(s,crda));
            /*if (!CSR_LAPPEN(s))*/
                SET_FIELD(&rmd.status, RMDS, STP, 1);

#define PCNET_RECV_STORE() do {                                 \
    int count = MIN(4096 - GET_FIELD(rmd.buf_length, RMDL, BCNT),remaining); \
    hwaddr rbadr = PHYSADDR(s, rmd.rbadr);          \
    s->phys_mem_write(s->dma_opaque, rbadr, src, count, CSR_BSWP(s)); \
    src += count; remaining -= count;                           \
    SET_FIELD(&rmd.status, RMDS, OWN, 0);                       \
    RMDSTORE(&rmd, PHYSADDR(s,crda));                           \
    pktcount++;                                                 \
} while (0)

            remaining = size;
            PCNET_RECV_STORE();
            if ((remaining > 0) && CSR_NRDA(s)) {
                hwaddr nrda = CSR_NRDA(s);
#ifdef PCNET_DEBUG_RMD
                PRINT_RMD(&rmd);
#endif
                RMDLOAD(&rmd, PHYSADDR(s,nrda));
                if (GET_FIELD(rmd.status, RMDS, OWN)) {
                    crda = nrda;
                    PCNET_RECV_STORE();
#ifdef PCNET_DEBUG_RMD
                    PRINT_RMD(&rmd);
#endif
                    if ((remaining > 0) && (nrda=CSR_NNRD(s))) {
                        RMDLOAD(&rmd, PHYSADDR(s,nrda));
                        if (GET_FIELD(rmd.status, RMDS, OWN)) {
                            crda = nrda;
                            PCNET_RECV_STORE();
                        }
                    }
                }
            }

#undef PCNET_RECV_STORE

            RMDLOAD(&rmd, PHYSADDR(s,crda));
            if (remaining == 0) {
                SET_FIELD(&rmd.msg_length, RMDM, MCNT, size);
                SET_FIELD(&rmd.status, RMDS, ENP, 1);
                SET_FIELD(&rmd.status, RMDS, PAM, !CSR_PROM(s) && is_padr);
                SET_FIELD(&rmd.status, RMDS, LFAM, !CSR_PROM(s) && is_ladr);
                SET_FIELD(&rmd.status, RMDS, BAM, !CSR_PROM(s) && is_bcast);
                if (crc_err) {
                    SET_FIELD(&rmd.status, RMDS, CRC, 1);
                    SET_FIELD(&rmd.status, RMDS, ERR, 1);
                }
            } else {
                SET_FIELD(&rmd.status, RMDS, OFLO, 1);
                SET_FIELD(&rmd.status, RMDS, BUFF, 1);
                SET_FIELD(&rmd.status, RMDS, ERR, 1);
            }
            RMDSTORE(&rmd, PHYSADDR(s,crda));
            s->csr[0] |= 0x0400;

#ifdef PCNET_DEBUG
            printf("RCVRC=%d CRDA=0x%08x BLKS=%d\n",
                CSR_RCVRC(s), PHYSADDR(s,CSR_CRDA(s)), pktcount);
#endif
#ifdef PCNET_DEBUG_RMD
            PRINT_RMD(&rmd);
#endif

            while (pktcount--) {
                if (CSR_RCVRC(s) <= 1) {
                    CSR_RCVRC(s) = CSR_RCVRL(s);
                } else {
                    CSR_RCVRC(s)--;
                }
            }

            pcnet_rdte_poll(s);

        }
    }

    pcnet_poll(s);
    pcnet_update_irq(s);

    return size_;
}


static void phy_update_link(CadenceGEMState *s)
{
    DB_PRINT("down %d\n", qemu_get_queue(s->nic)->link_down);

    /* Autonegotiation status mirrors link status.  */
    if (qemu_get_queue(s->nic)->link_down) {
        s->phy_regs[PHY_REG_STATUS] &= ~(PHY_REG_STATUS_ANEGCMPL |
                                         PHY_REG_STATUS_LINK);
        s->phy_regs[PHY_REG_INT_ST] |= PHY_REG_INT_ST_LINKC;
    } else {
        s->phy_regs[PHY_REG_STATUS] |= (PHY_REG_STATUS_ANEGCMPL |
                                         PHY_REG_STATUS_LINK);
        s->phy_regs[PHY_REG_INT_ST] |= (PHY_REG_INT_ST_LINKC |
                                        PHY_REG_INT_ST_ANEGCMPL |
                                        PHY_REG_INT_ST_ENERGY);
    }
}


static uint32_t gem_get_max_buf_len(CadenceGEMState *s, bool tx)
{
    uint32_t size;
    if (s->regs[GEM_NWCFG] & GEM_NWCFG_JUMBO_FRAME) {
        size = s->regs[GEM_JUMBO_MAX_LEN];
        if (size > s->jumbo_max_len) {
            size = s->jumbo_max_len;
            qemu_log_mask(LOG_GUEST_ERROR, "GEM_JUMBO_MAX_LEN reg cannot be"
                " greater than 0x%" PRIx32 "\n", s->jumbo_max_len);
        }
    } else if (tx) {
        size = 1518;
    } else {
        size = s->regs[GEM_NWCFG] & GEM_NWCFG_RCV_1538 ? 1538 : 1518;
    }
    return size;
}


static inline void rx_desc_set_broadcast(uint32_t *desc)
{
    desc[1] |= R_DESC_1_RX_BROADCAST;
}


static hwaddr gem_get_rx_desc_addr(CadenceGEMState *s, int q)
{
    return gem_get_desc_addr(s, false, q);
}


static inline void rx_desc_set_ownership(uint32_t *desc)
{
    desc[0] |= DESC_0_RX_OWNERSHIP;
}


static void lan9118_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);

    dc->reset = lan9118_reset;
    device_class_set_props(dc, lan9118_properties);
    dc->vmsd = &vmstate_lan9118;
    dc->realize = lan9118_realize;
}


void lan9118_init(NICInfo *nd, uint32_t base, qemu_irq irq)
{
    DeviceState *dev;
    SysBusDevice *s;

    qemu_check_nic_model(nd, "lan9118");
    dev = qdev_new(TYPE_LAN9118);
    qdev_set_nic_properties(dev, nd);
    s = SYS_BUS_DEVICE(dev);
    sysbus_realize_and_unref(s, &error_fatal);
    sysbus_mmio_map(s, 0, base);
    sysbus_connect_irq(s, 0, irq);
}


static void do_phy_write(lan9118_state *s, int reg, uint32_t val)
{
    switch (reg) {
    case 0: /* Basic Control */
        if (val & 0x8000) {
            phy_reset(s);
            break;
        }
        s->phy_control = val & 0x7980;
        /* Complete autonegotiation immediately.  */
        if (val & 0x1000) {
            s->phy_status |= 0x0020;
        }
        break;
    case 4: /* Auto-neg advertisement */
        s->phy_advertise = (val & 0x2d7f) | 0x80;
        break;
        /* TODO 17, 18, 27, 31 */
    case 30: /* Interrupt mask */
        s->phy_int_mask = val & 0xff;
        phy_update_irq(s);
        break;
    default:
        BADF("PHY write reg %d = 0x%04x\n", reg, val);
    }
}


static void do_mac_write(lan9118_state *s, int reg, uint32_t val)
{
    switch (reg) {
    case MAC_CR:
        if ((s->mac_cr & MAC_CR_RXEN) != 0 && (val & MAC_CR_RXEN) == 0) {
            s->int_sts |= RXSTOP_INT;
        }
        s->mac_cr = val & ~MAC_CR_RESERVED;
        DPRINTF("MAC_CR: %08x\n", val);
        break;
    case MAC_ADDRH:
        s->conf.macaddr.a[4] = val & 0xff;
        s->conf.macaddr.a[5] = (val >> 8) & 0xff;
        lan9118_mac_changed(s);
        break;
    case MAC_ADDRL:
        s->conf.macaddr.a[0] = val & 0xff;
        s->conf.macaddr.a[1] = (val >> 8) & 0xff;
        s->conf.macaddr.a[2] = (val >> 16) & 0xff;
        s->conf.macaddr.a[3] = (val >> 24) & 0xff;
        lan9118_mac_changed(s);
        break;
    case MAC_HASHH:
        s->mac_hashh = val;
        break;
    case MAC_HASHL:
        s->mac_hashl = val;
        break;
    case MAC_MII_ACC:
        s->mac_mii_acc = val & 0xffc2;
        if (val & 2) {
            DPRINTF("PHY write %d = 0x%04x\n",
                    (val >> 6) & 0x1f, s->mac_mii_data);
            do_phy_write(s, (val >> 6) & 0x1f, s->mac_mii_data);
        } else {
            s->mac_mii_data = do_phy_read(s, (val >> 6) & 0x1f);
            DPRINTF("PHY read %d = 0x%04x\n",
                    (val >> 6) & 0x1f, s->mac_mii_data);
        }
        break;
    case MAC_MII_DATA:
        s->mac_mii_data = val & 0xffff;
        break;
    case MAC_FLOW:
        s->mac_flow = val & 0xffff0000;
        break;
    case MAC_VLAN1:
        /* Writing to this register changes a condition for
         * FrameTooLong bit in rx_status.  Since we do not set
         * FrameTooLong anyway, just ignore write to this.
         */
        break;
    default:
        qemu_log_mask(LOG_GUEST_ERROR,
                      "lan9118: Unimplemented MAC register write: %d = 0x%x\n",
                 s->mac_cmd & 0xf, val);
    }
}


static uint32_t tx_status_fifo_pop(lan9118_state *s)
{
    uint32_t val;

    val = s->tx_status_fifo[s->tx_status_fifo_head];
    if (s->tx_status_fifo_used != 0) {
        s->tx_status_fifo_used--;
        s->tx_status_fifo_head = (s->tx_status_fifo_head + 1) & 511;
        /* ??? What value should be returned when the FIFO is empty?  */
    }
    return val;
}


static void lan9118_writew(void *opaque, hwaddr offset,
                           uint32_t val)
{
    lan9118_state *s = (lan9118_state *)opaque;
    offset &= 0xff;

    if (s->write_word_prev_offset != (offset & ~0x3)) {
        /* New offset, reset word counter */
        s->write_word_n = 0;
        s->write_word_prev_offset = offset & ~0x3;
    }

    if (offset & 0x2) {
        s->write_word_h = val;
    } else {
        s->write_word_l = val;
    }

    //DPRINTF("Writew reg 0x%02x = 0x%08x\n", (int)offset, val);
    s->write_word_n++;
    if (s->write_word_n == 2) {
        s->write_word_n = 0;
        lan9118_writel(s, offset & ~3, s->write_word_l +
                (s->write_word_h << 16), 4);
    }
}


static void parse_xattrmap(struct lo_data *lo)
{
    const char *map = lo->xattrmap;
    const char *tmp;

    lo->xattr_map_nentries = 0;
    while (*map) {
        XattrMapEntry tmp_entry;
        char sep;

        if (isspace(*map)) {
            map++;
            continue;
        }
        /* The separator is the first non-space of the rule */
        sep = *map++;
        if (!sep) {
            break;
        }

        tmp_entry.flags = 0;
        /* Start of 'type' */
        if (strstart(map, "prefix", &map)) {
            tmp_entry.flags |= XATTR_MAP_FLAG_PREFIX;
        } else if (strstart(map, "ok", &map)) {
            tmp_entry.flags |= XATTR_MAP_FLAG_OK;
        } else if (strstart(map, "bad", &map)) {
            tmp_entry.flags |= XATTR_MAP_FLAG_BAD;
        } else {
            fuse_log(FUSE_LOG_ERR,
                     "%s: Unexpected type;"
                     "Expecting 'prefix', 'ok', or 'bad' in rule %zu\n",
                     __func__, lo->xattr_map_nentries);
            exit(1);
        }

        if (*map++ != sep) {
            fuse_log(FUSE_LOG_ERR,
                     "%s: Missing '%c' at end of type field of rule %zu\n",
                     __func__, sep, lo->xattr_map_nentries);
            exit(1);
        }

        /* Start of 'scope' */
        if (strstart(map, "client", &map)) {
            tmp_entry.flags |= XATTR_MAP_FLAG_CLIENT;
        } else if (strstart(map, "server", &map)) {
            tmp_entry.flags |= XATTR_MAP_FLAG_SERVER;
        } else if (strstart(map, "all", &map)) {
            tmp_entry.flags |= XATTR_MAP_FLAG_ALL;
        } else {
            fuse_log(FUSE_LOG_ERR,
                     "%s: Unexpected scope;"
                     " Expecting 'client', 'server', or 'all', in rule %zu\n",
                     __func__, lo->xattr_map_nentries);
            exit(1);
        }

        if (*map++ != sep) {
            fuse_log(FUSE_LOG_ERR,
                     "%s: Expecting '%c' found '%c'"
                     " after scope in rule %zu\n",
                     __func__, sep, *map, lo->xattr_map_nentries);
            exit(1);
        }

        /* At start of 'key' field */
        tmp = strchr(map, sep);
        if (!tmp) {
            fuse_log(FUSE_LOG_ERR,
                     "%s: Missing '%c' at end of key field of rule %zu",
                     __func__, sep, lo->xattr_map_nentries);
            exit(1);
        }
        tmp_entry.key = g_strndup(map, tmp - map);
        map = tmp + 1;

        /* At start of 'prepend' field */
        tmp = strchr(map, sep);
        if (!tmp) {
            fuse_log(FUSE_LOG_ERR,
                     "%s: Missing '%c' at end of prepend field of rule %zu",
                     __func__, sep, lo->xattr_map_nentries);
            exit(1);
        }
        tmp_entry.prepend = g_strndup(map, tmp - map);
        map = tmp + 1;

        add_xattrmap_entry(lo, &tmp_entry);
        /* End of rule - go around again for another rule */
    }

    if (!lo->xattr_map_nentries) {
        fuse_log(FUSE_LOG_ERR, "Empty xattr map\n");
        exit(1);
    }
}


static void fuse_lo_data_cleanup(struct lo_data *lo)
{
    if (lo->inodes) {
        g_hash_table_destroy(lo->inodes);
    }
    lo_map_destroy(&lo->fd_map);
    lo_map_destroy(&lo->dirp_map);
    lo_map_destroy(&lo->ino_map);

    if (lo->proc_self_fd >= 0) {
        close(lo->proc_self_fd);
    }

    if (lo->root.fd >= 0) {
        close(lo->root.fd);
    }

    free(lo->xattrmap);
    free_xattrmap(lo);
    free(lo->source);
}


int main(int argc, char *argv[])
{
    struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
    struct fuse_session *se;
    struct fuse_cmdline_opts opts;
    struct lo_data lo = {
        .sandbox = SANDBOX_NAMESPACE,
        .debug = 0,
        .writeback = 0,
        .posix_lock = 0,
        .allow_direct_io = 0,
        .proc_self_fd = -1,
    };
    struct lo_map_elem *root_elem;
    int ret = -1;

    /* Don't mask creation mode, kernel already did that */
    umask(0);

    qemu_init_exec_dir(argv[0]);

    pthread_mutex_init(&lo.mutex, NULL);
    lo.inodes = g_hash_table_new(lo_key_hash, lo_key_equal);
    lo.root.fd = -1;
    lo.root.fuse_ino = FUSE_ROOT_ID;
    lo.cache = CACHE_AUTO;

    /*
     * Set up the ino map like this:
     * [0] Reserved (will not be used)
     * [1] Root inode
     */
    lo_map_init(&lo.ino_map);
    lo_map_reserve(&lo.ino_map, 0)->in_use = false;
    root_elem = lo_map_reserve(&lo.ino_map, lo.root.fuse_ino);
    root_elem->inode = &lo.root;

    lo_map_init(&lo.dirp_map);
    lo_map_init(&lo.fd_map);

    if (fuse_parse_cmdline(&args, &opts) != 0) {
        goto err_out1;
    }
    fuse_set_log_func(log_func);
    use_syslog = opts.syslog;
    if (use_syslog) {
        openlog("virtiofsd", LOG_PID, LOG_DAEMON);
    }

    if (opts.show_help) {
        printf("usage: %s [options]\n\n", argv[0]);
        fuse_cmdline_help();
        printf("    -o source=PATH             shared directory tree\n");
        fuse_lowlevel_help();
        ret = 0;
        goto err_out1;
    } else if (opts.show_version) {
        fuse_lowlevel_version();
        ret = 0;
        goto err_out1;
    } else if (opts.print_capabilities) {
        print_capabilities();
        ret = 0;
        goto err_out1;
    }

    if (fuse_opt_parse(&args, &lo, lo_opts, NULL) == -1) {
        goto err_out1;
    }

    if (opts.log_level != 0) {
        current_log_level = opts.log_level;
    } else {
        /* default log level is INFO */
        current_log_level = FUSE_LOG_INFO;
    }
    lo.debug = opts.debug;
    if (lo.debug) {
        current_log_level = FUSE_LOG_DEBUG;
    }
    if (lo.source) {
        struct stat stat;
        int res;

        res = lstat(lo.source, &stat);
        if (res == -1) {
            fuse_log(FUSE_LOG_ERR, "failed to stat source (\"%s\"): %m\n",
                     lo.source);
            exit(1);
        }
        if (!S_ISDIR(stat.st_mode)) {
            fuse_log(FUSE_LOG_ERR, "source is not a directory\n");
            exit(1);
        }
    } else {
        lo.source = strdup("/");
    }

    if (lo.xattrmap) {
        parse_xattrmap(&lo);
    }

    if (!lo.timeout_set) {
        switch (lo.cache) {
        case CACHE_NONE:
            lo.timeout = 0.0;
            break;

        case CACHE_AUTO:
            lo.timeout = 1.0;
            break;

        case CACHE_ALWAYS:
            lo.timeout = 86400.0;
            break;
        }
    } else if (lo.timeout < 0) {
        fuse_log(FUSE_LOG_ERR, "timeout is negative (%lf)\n", lo.timeout);
        exit(1);
    }

    se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo);
    if (se == NULL) {
        goto err_out1;
    }

    if (fuse_set_signal_handlers(se) != 0) {
        goto err_out2;
    }

    if (fuse_session_mount(se) != 0) {
        goto err_out3;
    }

    fuse_daemonize(opts.foreground);

    setup_nofile_rlimit(opts.rlimit_nofile);

    /* Must be before sandbox since it wants /proc */
    setup_capng();

    setup_sandbox(&lo, se, opts.syslog);

    setup_root(&lo, &lo.root);
    /* Block until ctrl+c or fusermount -u */
    ret = virtio_loop(se);

    fuse_session_unmount(se);
    cleanup_capng();
err_out3:
    fuse_remove_signal_handlers(se);
err_out2:
    fuse_session_destroy(se);
err_out1:
    fuse_opt_free_args(&args);

    fuse_lo_data_cleanup(&lo);

    return ret ? 1 : 0;
}


static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
    int res;
    (void)ino;
    struct lo_inode *inode;
    struct lo_data *lo = lo_data(req);

    inode = lo_inode(req, ino);
    if (!inode) {
        fuse_reply_err(req, EBADF);
        return;
    }

    if (!S_ISREG(inode->filetype)) {
        lo_inode_put(lo, &inode);
        fuse_reply_err(req, EBADF);
        return;
    }

    /* An fd is going away. Cleanup associated posix locks */
    if (lo->posix_lock) {
        pthread_mutex_lock(&inode->plock_mutex);
        g_hash_table_remove(inode->posix_locks,
            GUINT_TO_POINTER(fi->lock_owner));
        pthread_mutex_unlock(&inode->plock_mutex);
    }
    res = close(dup(lo_fi_fd(req, fi)));
    lo_inode_put(lo, &inode);
    fuse_reply_err(req, res == -1 ? errno : 0);
}


static int xattr_map_client(const struct lo_data *lo, const char *client_name,
                            char **out_name)
{
    size_t i;
    for (i = 0; i < lo->xattr_map_nentries; i++) {
        const XattrMapEntry *cur_entry = lo->xattr_map_list + i;

        if ((cur_entry->flags & XATTR_MAP_FLAG_CLIENT) &&
            (strstart(client_name, cur_entry->key, NULL))) {
            if (cur_entry->flags & XATTR_MAP_FLAG_BAD) {
                return -EPERM;
            }
            if (cur_entry->flags & XATTR_MAP_FLAG_OK) {
                /* Unmodified name */
                return 0;
            }
            if (cur_entry->flags & XATTR_MAP_FLAG_PREFIX) {
                *out_name = g_try_malloc(strlen(client_name) +
                                         strlen(cur_entry->prepend) + 1);
                if (!*out_name) {
                    return -ENOMEM;
                }
                sprintf(*out_name, "%s%s", cur_entry->prepend, client_name);
                return 0;
            }
        }
    }

    return -EPERM;
}


static void setup_root(struct lo_data *lo, struct lo_inode *root)
{
    int fd, res;
    struct stat stat;
    uint64_t mnt_id;

    fd = open("/", O_PATH);
    if (fd == -1) {
        fuse_log(FUSE_LOG_ERR, "open(%s, O_PATH): %m\n", lo->source);
        exit(1);
    }

    res = do_statx(lo, fd, "", &stat, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW,
                   &mnt_id);
    if (res == -1) {
        fuse_log(FUSE_LOG_ERR, "fstatat(%s): %m\n", lo->source);
        exit(1);
    }

    root->filetype = S_IFDIR;
    root->fd = fd;
    root->key.ino = stat.st_ino;
    root->key.dev = stat.st_dev;
    root->key.mnt_id = mnt_id;
    root->nlookup = 2;
    g_atomic_int_set(&root->refcount, 2);
    if (lo->posix_lock) {
        pthread_mutex_init(&root->plock_mutex, NULL);
        root->posix_locks = g_hash_table_new_full(
            g_direct_hash, g_direct_equal, NULL, posix_locks_value_destroy);
    }
}


static inline void ConvertAdobe98ToXYZ(const double red,const double green,
  const double blue,double *X,double *Y,double *Z)
{
  double
    b,
    g,
    r;

  /*
    Convert Adobe '98 to XYZ colorspace.
  */
  assert(X != (double *) NULL);
  assert(Y != (double *) NULL);
  assert(Z != (double *) NULL);
  r=QuantumScale*DecodePixelGamma(red);
  g=QuantumScale*DecodePixelGamma(green);
  b=QuantumScale*DecodePixelGamma(blue);
  *X=0.57666904291013050*r+0.18555823790654630*g+0.18822864623499470*b;
  *Y=0.29734497525053605*r+0.62736356625546610*g+0.07529145849399788*b;
  *Z=0.02703136138641234*r+0.07068885253582723*g+0.99133753683763880*b;
}


static inline void ConvertLabToXYZ(const double L,const double a,const double b,
  double *X,double *Y,double *Z)
{
  double
    x,
    y,
    z;

  assert(X != (double *) NULL);
  assert(Y != (double *) NULL);
  assert(Z != (double *) NULL);
  y=(L+16.0)/116.0;
  x=y+a/500.0;
  z=y-b/200.0;
  if ((x*x*x) > CIEEpsilon)
    x=(x*x*x);
  else
    x=(116.0*x-16.0)/CIEK;
  if (L > (CIEK*CIEEpsilon))
    y=(y*y*y);
  else
    y=L/CIEK;
  if ((z*z*z) > CIEEpsilon)
    z=(z*z*z);
  else
    z=(116.0*z-16.0)/CIEK;
  *X=D65X*x;
  *Y=D65Y*y;
  *Z=D65Z*z;
}


static inline void ConvertXYZToRGB(const double X,const double Y,const double Z,
  Quantum *red,Quantum *green,Quantum *blue)
{
  double
    b,
    g,
    r;

  assert(red != (Quantum *) NULL);
  assert(green != (Quantum *) NULL);
  assert(blue != (Quantum *) NULL);
  r=3.2404542*X-1.5371385*Y-0.4985314*Z;
  g=(-0.9692660)*X+1.8760108*Y+0.0415560*Z;
  b=0.0556434*X-0.2040259*Y+1.0572252*Z;
  *red=ClampToQuantum((MagickRealType) EncodePixelGamma(QuantumRange*r));
  *green=ClampToQuantum((MagickRealType) EncodePixelGamma(QuantumRange*g));
  *blue=ClampToQuantum((MagickRealType) EncodePixelGamma(QuantumRange*b));
}


static size_t poly_number_terms(double order)
{
 /* Return the number of terms for a 2d polynomial */
  if ( order < 1 || order > 5 ||
       ( order != floor(order) && (order-1.5) > MagickEpsilon) )
    return 0; /* invalid polynomial order */
  return((size_t) floor((order+1)*(order+2)/2));
}


static double poly_basis_dx(ssize_t n, double x, double y)
{
  /* polynomial term for x derivative */
  switch(n) {
    case  0:  return( 0.0 ); /* constant */
    case  1:  return( 1.0 );
    case  2:  return( 0.0 ); /* affine      order = 1   terms = 3 */
    case  3:  return(  y  ); /* bilinear    order = 1.5 terms = 4 */
    case  4:  return(  x  );
    case  5:  return( 0.0 ); /* quadratic   order = 2   terms = 6 */
    case  6:  return( x*x );
    case  7:  return( x*y );
    case  8:  return( y*y );
    case  9:  return( 0.0 ); /* cubic       order = 3   terms = 10 */
    case 10:  return( x*x*x );
    case 11:  return( x*x*y );
    case 12:  return( x*y*y );
    case 13:  return( y*y*y );
    case 14:  return( 0.0 ); /* quartic     order = 4   terms = 15 */
    case 15:  return( x*x*x*x );
    case 16:  return( x*x*x*y );
    case 17:  return( x*x*y*y );
    case 18:  return( x*y*y*y );
    case 19:  return( y*y*y*y );
    case 20:  return( 0.0 ); /* quintic     order = 5   terms = 21 */
  }
  return( 0.0 ); /* should never happen */
}


MagickExport Image *RotateImage(const Image *image,const double degrees,
  ExceptionInfo *exception)
{
  Image
    *distort_image,
    *rotate_image;

  double
    angle;

  PointInfo
    shear;

  size_t
    rotations;

  /*
    Adjust rotation angle.
  */
  assert(image != (Image *) NULL);
  assert(image->signature == MagickCoreSignature);
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  assert(exception != (ExceptionInfo *) NULL);
  assert(exception->signature == MagickCoreSignature);
  angle=fmod(degrees,360.0);
  while (angle < -45.0)
    angle+=360.0;
  for (rotations=0; angle > 45.0; rotations++)
    angle-=90.0;
  rotations%=4;
  shear.x=(-tan((double) DegreesToRadians(angle)/2.0));
  shear.y=sin((double) DegreesToRadians(angle));
  if ((fabs(shear.x) < MagickEpsilon) && (fabs(shear.y) < MagickEpsilon))
    return(IntegralRotateImage(image,rotations,exception));
  distort_image=CloneImage(image,0,0,MagickTrue,exception);
  if (distort_image == (Image *) NULL)
    return((Image *) NULL);
  (void) SetImageVirtualPixelMethod(distort_image,BackgroundVirtualPixelMethod,
    exception);
  rotate_image=DistortImage(distort_image,ScaleRotateTranslateDistortion,1,
    &degrees,MagickTrue,exception);
  distort_image=DestroyImage(distort_image);
  return(rotate_image);
}


MagickExport Image *StatisticImage(const Image *image,const StatisticType type,
  const size_t width,const size_t height,ExceptionInfo *exception)
{
#define StatisticImageTag  "Statistic/Image"

  CacheView
    *image_view,
    *statistic_view;

  Image
    *statistic_image;

  MagickBooleanType
    status;

  MagickOffsetType
    progress;

  PixelList
    **magick_restrict pixel_list;

  ssize_t
    center,
    y;

  /*
    Initialize statistics image attributes.
  */
  assert(image != (Image *) NULL);
  assert(image->signature == MagickCoreSignature);
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  assert(exception != (ExceptionInfo *) NULL);
  assert(exception->signature == MagickCoreSignature);
  statistic_image=CloneImage(image,0,0,MagickTrue,
    exception);
  if (statistic_image == (Image *) NULL)
    return((Image *) NULL);
  status=SetImageStorageClass(statistic_image,DirectClass,exception);
  if (status == MagickFalse)
    {
      statistic_image=DestroyImage(statistic_image);
      return((Image *) NULL);
    }
  pixel_list=AcquirePixelListThreadSet(MagickMax(width,1),MagickMax(height,1));
  if (pixel_list == (PixelList **) NULL)
    {
      statistic_image=DestroyImage(statistic_image);
      ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
    }
  /*
    Make each pixel the min / max / median / mode / etc. of the neighborhood.
  */
  center=(ssize_t) GetPixelChannels(image)*(image->columns+MagickMax(width,1))*
    (MagickMax(height,1)/2L)+GetPixelChannels(image)*(MagickMax(width,1)/2L);
  status=MagickTrue;
  progress=0;
  image_view=AcquireVirtualCacheView(image,exception);
  statistic_view=AcquireAuthenticCacheView(statistic_image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
  #pragma omp parallel for schedule(static) shared(progress,status) \
    magick_number_threads(image,statistic_image,statistic_image->rows,1)
#endif
  for (y=0; y < (ssize_t) statistic_image->rows; y++)
  {
    const int
      id = GetOpenMPThreadId();

    const Quantum
      *magick_restrict p;

    Quantum
      *magick_restrict q;

    ssize_t
      x;

    if (status == MagickFalse)
      continue;
    p=GetCacheViewVirtualPixels(image_view,-((ssize_t) MagickMax(width,1)/2L),y-
      (ssize_t) (MagickMax(height,1)/2L),image->columns+MagickMax(width,1),
      MagickMax(height,1),exception);
    q=QueueCacheViewAuthenticPixels(statistic_view,0,y,statistic_image->columns,      1,exception);
    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
      {
        status=MagickFalse;
        continue;
      }
    for (x=0; x < (ssize_t) statistic_image->columns; x++)
    {
      ssize_t
        i;

      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
      {
        double
          area,
          maximum,
          minimum,
          sum,
          sum_squared;

        Quantum
          pixel;

        const Quantum
          *magick_restrict pixels;

        ssize_t
          u;

        ssize_t
          v;

        PixelChannel channel = GetPixelChannelChannel(image,i);
        PixelTrait traits = GetPixelChannelTraits(image,channel);
        PixelTrait statistic_traits=GetPixelChannelTraits(statistic_image,
          channel);
        if ((traits == UndefinedPixelTrait) ||
            (statistic_traits == UndefinedPixelTrait))
          continue;
        if (((statistic_traits & CopyPixelTrait) != 0) ||
            (GetPixelWriteMask(image,p) <= (QuantumRange/2)))
          {
            SetPixelChannel(statistic_image,channel,p[center+i],q);
            continue;
          }
        if ((statistic_traits & UpdatePixelTrait) == 0)
          continue;
        pixels=p;
        area=0.0;
        minimum=pixels[i];
        maximum=pixels[i];
        sum=0.0;
        sum_squared=0.0;
        ResetPixelList(pixel_list[id]);
        for (v=0; v < (ssize_t) MagickMax(height,1); v++)
        {
          for (u=0; u < (ssize_t) MagickMax(width,1); u++)
          {
            if ((type == MedianStatistic) || (type == ModeStatistic) ||
                (type == NonpeakStatistic))
              {
                InsertPixelList(pixels[i],pixel_list[id]);
                pixels+=GetPixelChannels(image);
                continue;
              }
            area++;
            if (pixels[i] < minimum)
              minimum=(double) pixels[i];
            if (pixels[i] > maximum)
              maximum=(double) pixels[i];
            sum+=(double) pixels[i];
            sum_squared+=(double) pixels[i]*pixels[i];
            pixels+=GetPixelChannels(image);
          }
          pixels+=GetPixelChannels(image)*image->columns;
        }
        switch (type)
        {
          case GradientStatistic:
          {
            pixel=ClampToQuantum(MagickAbsoluteValue(maximum-minimum));
            break;
          }
          case MaximumStatistic:
          {
            pixel=ClampToQuantum(maximum);
            break;
          }
          case MeanStatistic:
          default:
          {
            pixel=ClampToQuantum(sum/area);
            break;
          }
          case MedianStatistic:
          {
            GetMedianPixelList(pixel_list[id],&pixel);
            break;
          }
          case MinimumStatistic:
          {
            pixel=ClampToQuantum(minimum);
            break;
          }
          case ModeStatistic:
          {
            GetModePixelList(pixel_list[id],&pixel);
            break;
          }
          case NonpeakStatistic:
          {
            GetNonpeakPixelList(pixel_list[id],&pixel);
            break;
          }
          case RootMeanSquareStatistic:
          {
            pixel=ClampToQuantum(sqrt(sum_squared/area));
            break;
          }
          case StandardDeviationStatistic:
          {
            pixel=ClampToQuantum(sqrt(sum_squared/area-(sum/area*sum/area)));
            break;
          }
        }
        SetPixelChannel(statistic_image,channel,pixel,q);
      }
      p+=GetPixelChannels(image);
      q+=GetPixelChannels(statistic_image);
    }
    if (SyncCacheViewAuthenticPixels(statistic_view,exception) == MagickFalse)
      status=MagickFalse;
    if (image->progress_monitor != (MagickProgressMonitor) NULL)
      {
        MagickBooleanType
          proceed;

#if defined(MAGICKCORE_OPENMP_SUPPORT)
        #pragma omp atomic
#endif
        progress++;
        proceed=SetImageProgress(image,StatisticImageTag,progress,image->rows);
        if (proceed == MagickFalse)
          status=MagickFalse;
      }
  }
  statistic_view=DestroyCacheView(statistic_view);
  image_view=DestroyCacheView(image_view);
  pixel_list=DestroyPixelListThreadSet(pixel_list);
  if (status == MagickFalse)
    statistic_image=DestroyImage(statistic_image);
  return(statistic_image);
}


static PixelChannels **AcquirePixelThreadSet(const Image *images)
{
  const Image
    *next;

  PixelChannels
    **pixels;

  ssize_t
    i;

  size_t
    columns,
    number_images,
    rows;

  number_images=GetImageListLength(images);
  rows=MagickMax(number_images,(size_t) GetMagickResourceLimit(ThreadResource));
  pixels=(PixelChannels **) AcquireQuantumMemory(rows,sizeof(*pixels));
  if (pixels == (PixelChannels **) NULL)
    return((PixelChannels **) NULL);
  (void) memset(pixels,0,rows*sizeof(*pixels));
  columns=MagickMax(number_images,MaxPixelChannels);
  for (next=images; next != (Image *) NULL; next=next->next)
    columns=MagickMax(next->columns,columns);
  for (i=0; i < (ssize_t) rows; i++)
  {
    ssize_t
      j;

    pixels[i]=(PixelChannels *) AcquireQuantumMemory(columns,sizeof(**pixels));
    if (pixels[i] == (PixelChannels *) NULL)
      return(DestroyPixelThreadSet(images,pixels));
    for (j=0; j < (ssize_t) columns; j++)
    {
      ssize_t
        k;

      for (k=0; k < MaxPixelChannels; k++)
        pixels[i][j].channel[k]=0.0;
    }
  }
  return(pixels);
}


static void GetMaximumPixelList(PixelList *pixel_list,MagickPixelPacket *pixel)
{
  SkipList
    *list;

  ssize_t
    channel;

  size_t
    color,
    maximum;

  ssize_t
    count;

  unsigned short
    channels[ListChannels];

  /*
    Find the maximum value for each of the color.
  */
  for (channel=0; channel < 5; channel++)
  {
    list=pixel_list->lists+channel;
    color=65536L;
    count=0;
    maximum=list->nodes[color].next[0];
    do
    {
      color=list->nodes[color].next[0];
      if (color > maximum)
        maximum=color;
      count+=list->nodes[color].count;
    } while (count < (ssize_t) pixel_list->length);
    channels[channel]=(unsigned short) maximum;
  }
  pixel->red=(MagickRealType) ScaleShortToQuantum(channels[0]);
  pixel->green=(MagickRealType) ScaleShortToQuantum(channels[1]);
  pixel->blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
  pixel->opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
  pixel->index=(MagickRealType) ScaleShortToQuantum(channels[4]);
}


MagickPrivate int XMenuWidget(Display *display,XWindows *windows,
  const char *title,const char *const *selections,char *item)
{
  Cursor
    cursor;

  int
    id,
    x,
    y;

  unsigned int
    height,
    number_selections,
    title_height,
    top_offset,
    width;

  size_t
    state;

  XEvent
    event;

  XFontStruct
    *font_info;

  XSetWindowAttributes
    window_attributes;

  XWidgetInfo
    highlight_info,
    menu_info,
    selection_info;

  XWindowChanges
    window_changes;

  /*
    Determine Menu widget attributes.
  */
  assert(display != (Display *) NULL);
  assert(windows != (XWindows *) NULL);
  assert(title != (char *) NULL);
  assert(selections != (const char **) NULL);
  assert(item != (char *) NULL);
  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",title);
  font_info=windows->widget.font_info;
  windows->widget.width=submenu_info.active == 0 ?
    WidgetTextWidth(font_info,(char *) title) : 0;
  for (id=0; selections[id] != (char *) NULL; id++)
  {
    width=WidgetTextWidth(font_info,(char *) selections[id]);
    if (width > windows->widget.width)
      windows->widget.width=width;
  }
  number_selections=(unsigned int) id;
  XGetWidgetInfo((char *) NULL,&menu_info);
  title_height=(unsigned int) (submenu_info.active == 0 ?
    (3*(font_info->descent+font_info->ascent) >> 1)+5 : 2);
  width=WidgetTextWidth(font_info,(char *) title);
  height=(unsigned int) ((3*(font_info->ascent+font_info->descent)) >> 1);
  /*
    Position Menu widget.
  */
  windows->widget.width+=QuantumMargin+(menu_info.bevel_width << 1);
  top_offset=title_height+menu_info.bevel_width-1;
  windows->widget.height=top_offset+number_selections*height+4;
  windows->widget.min_width=windows->widget.width;
  windows->widget.min_height=windows->widget.height;
  XQueryPosition(display,windows->widget.root,&x,&y);
  windows->widget.x=x-(QuantumMargin >> 1);
  if (submenu_info.active != 0)
    {
      windows->widget.x=
        windows->command.x+windows->command.width-QuantumMargin;
      toggle_info.raised=MagickTrue;
      XDrawTriangleEast(display,&windows->command,&toggle_info);
    }
  windows->widget.y=submenu_info.active == 0 ? y-(int)
    ((3*title_height) >> 2) : y;
  if (submenu_info.active != 0)
    windows->widget.y=windows->command.y+submenu_info.y;
  XConstrainWindowPosition(display,&windows->widget);
  /*
    Map Menu widget.
  */
  window_attributes.override_redirect=MagickTrue;
  (void) XChangeWindowAttributes(display,windows->widget.id,
    (size_t) CWOverrideRedirect,&window_attributes);
  window_changes.width=(int) windows->widget.width;
  window_changes.height=(int) windows->widget.height;
  window_changes.x=windows->widget.x;
  window_changes.y=windows->widget.y;
  (void) XReconfigureWMWindow(display,windows->widget.id,windows->widget.screen,
    (unsigned int) (CWWidth | CWHeight | CWX | CWY),&window_changes);
  (void) XMapRaised(display,windows->widget.id);
  windows->widget.mapped=MagickFalse;
  /*
    Respond to X events.
  */
  selection_info.height=height;
  cursor=XCreateFontCursor(display,XC_right_ptr);
  (void) XCheckDefineCursor(display,windows->image.id,cursor);
  (void) XCheckDefineCursor(display,windows->command.id,cursor);
  (void) XCheckDefineCursor(display,windows->widget.id,cursor);
  state=UpdateConfigurationState;
  do
  {
    if (state & UpdateConfigurationState)
      {
        /*
          Initialize selection information.
        */
        XGetWidgetInfo((char *) NULL,&menu_info);
        menu_info.bevel_width--;
        menu_info.width=windows->widget.width-((menu_info.bevel_width) << 1);
        menu_info.height=windows->widget.height-((menu_info.bevel_width) << 1);
        menu_info.x=(int) menu_info.bevel_width;
        menu_info.y=(int) menu_info.bevel_width;
        XGetWidgetInfo((char *) NULL,&selection_info);
        selection_info.center=MagickFalse;
        selection_info.width=menu_info.width;
        selection_info.height=height;
        selection_info.x=menu_info.x;
        highlight_info=selection_info;
        highlight_info.bevel_width--;
        highlight_info.width-=(highlight_info.bevel_width << 1);
        highlight_info.height-=(highlight_info.bevel_width << 1);
        highlight_info.x+=highlight_info.bevel_width;
        state&=(~UpdateConfigurationState);
      }
    if (state & RedrawWidgetState)
      {
        /*
          Redraw Menu widget.
        */
        if (submenu_info.active == 0)
          {
            y=(int) title_height;
            XSetBevelColor(display,&windows->widget,MagickFalse);
            (void) XDrawLine(display,windows->widget.id,
              windows->widget.widget_context,selection_info.x,y-1,
              (int) selection_info.width,y-1);
            XSetBevelColor(display,&windows->widget,MagickTrue);
            (void) XDrawLine(display,windows->widget.id,
              windows->widget.widget_context,selection_info.x,y,
              (int) selection_info.width,y);
            (void) XSetFillStyle(display,windows->widget.widget_context,
              FillSolid);
          }
        /*
          Draw menu selections.
        */
        selection_info.center=MagickTrue;
        selection_info.y=(int) menu_info.bevel_width;
        selection_info.text=(char *) title;
        if (submenu_info.active == 0)
          XDrawWidgetText(display,&windows->widget,&selection_info);
        selection_info.center=MagickFalse;
        selection_info.y=(int) top_offset;
        for (id=0; id < (int) number_selections; id++)
        {
          selection_info.text=(char *) selections[id];
          XDrawWidgetText(display,&windows->widget,&selection_info);
          highlight_info.y=selection_info.y+highlight_info.bevel_width;
          if (id == selection_info.id)
            XDrawBevel(display,&windows->widget,&highlight_info);
          selection_info.y+=(int) selection_info.height;
        }
        XDrawBevel(display,&windows->widget,&menu_info);
        state&=(~RedrawWidgetState);
      }
    if (number_selections > 2)
      {
        /*
          Redraw Menu line.
        */
        y=(int) (top_offset+selection_info.height*(number_selections-1));
        XSetBevelColor(display,&windows->widget,MagickFalse);
        (void) XDrawLine(display,windows->widget.id,
          windows->widget.widget_context,selection_info.x,y-1,
          (int) selection_info.width,y-1);
        XSetBevelColor(display,&windows->widget,MagickTrue);
        (void) XDrawLine(display,windows->widget.id,
          windows->widget.widget_context,selection_info.x,y,
          (int) selection_info.width,y);
        (void) XSetFillStyle(display,windows->widget.widget_context,FillSolid);
      }
    /*
      Wait for next event.
    */
    (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
    switch (event.type)
    {
      case ButtonPress:
      {
        if (event.xbutton.window != windows->widget.id)
          {
            /*
              exit menu.
            */
            if (event.xbutton.window == windows->command.id)
              (void) XPutBackEvent(display,&event);
            selection_info.id=(~0);
            *item='\0';
            state|=ExitState;
            break;
          }
        state&=(~InactiveWidgetState);
        id=(event.xbutton.y-top_offset)/(int) selection_info.height;
        selection_info.id=id;
        if ((id < 0) || (id >= (int) number_selections))
          break;
        /*
          Highlight this selection.
        */
        selection_info.y=(int) (top_offset+id*selection_info.height);
        selection_info.text=(char *) selections[id];
        XDrawWidgetText(display,&windows->widget,&selection_info);
        highlight_info.y=selection_info.y+highlight_info.bevel_width;
        XDrawBevel(display,&windows->widget,&highlight_info);
        break;
      }
      case ButtonRelease:
      {
        if (windows->widget.mapped == MagickFalse)
          break;
        if (event.xbutton.window == windows->command.id)
          if ((state & InactiveWidgetState) == 0)
            break;
        /*
          exit menu.
        */
        XSetCursorState(display,windows,MagickFalse);
        *item='\0';
        state|=ExitState;
        break;
      }
      case ConfigureNotify:
      {
        /*
          Update widget configuration.
        */
        if (event.xconfigure.window != windows->widget.id)
          break;
        if ((event.xconfigure.width == (int) windows->widget.width) &&
            (event.xconfigure.height == (int) windows->widget.height))
          break;
        windows->widget.width=(unsigned int)
          MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
        windows->widget.height=(unsigned int)
          MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
        state|=UpdateConfigurationState;
        break;
      }
      case EnterNotify:
      {
        if (event.xcrossing.window != windows->widget.id)
          break;
        if (event.xcrossing.state == 0)
          break;
        state&=(~InactiveWidgetState);
        id=((event.xcrossing.y-top_offset)/(int) selection_info.height);
        if ((selection_info.id >= 0) &&
            (selection_info.id < (int) number_selections))
          {
            /*
              Unhighlight last selection.
            */
            if (id == selection_info.id)
              break;
            selection_info.y=(int)
              (top_offset+selection_info.id*selection_info.height);
            selection_info.text=(char *) selections[selection_info.id];
            XDrawWidgetText(display,&windows->widget,&selection_info);
          }
        if ((id < 0) || (id >= (int) number_selections))
          break;
        /*
          Highlight this selection.
        */
        selection_info.id=id;
        selection_info.y=(int)
          (top_offset+selection_info.id*selection_info.height);
        selection_info.text=(char *) selections[selection_info.id];
        XDrawWidgetText(display,&windows->widget,&selection_info);
        highlight_info.y=selection_info.y+highlight_info.bevel_width;
        XDrawBevel(display,&windows->widget,&highlight_info);
        break;
      }
      case Expose:
      {
        if (event.xexpose.window != windows->widget.id)
          break;
        if (event.xexpose.count != 0)
          break;
        state|=RedrawWidgetState;
        break;
      }
      case LeaveNotify:
      {
        if (event.xcrossing.window != windows->widget.id)
          break;
        state|=InactiveWidgetState;
        id=selection_info.id;
        if ((id < 0) || (id >= (int) number_selections))
          break;
        /*
          Unhighlight last selection.
        */
        selection_info.y=(int) (top_offset+id*selection_info.height);
        selection_info.id=(~0);
        selection_info.text=(char *) selections[id];
        XDrawWidgetText(display,&windows->widget,&selection_info);
        break;
      }
      case MotionNotify:
      {
        /*
          Discard pending button motion events.
        */
        while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
        if (submenu_info.active != 0)
          if (event.xmotion.window == windows->command.id)
            {
              if ((state & InactiveWidgetState) == 0)
                {
                  if (MatteIsActive(submenu_info,event.xmotion) == MagickFalse)
                    {
                      selection_info.id=(~0);
                        *item='\0';
                      state|=ExitState;
                      break;
                    }
                }
              else
                if (WindowIsActive(windows->command,event.xmotion))
                  {
                    selection_info.id=(~0);
                    *item='\0';
                    state|=ExitState;
                    break;
                  }
            }
        if (event.xmotion.window != windows->widget.id)
          break;
        if (state & InactiveWidgetState)
          break;
        id=(event.xmotion.y-top_offset)/(int) selection_info.height;
        if ((selection_info.id >= 0) &&
            (selection_info.id < (int) number_selections))
          {
            /*
              Unhighlight last selection.
            */
            if (id == selection_info.id)
              break;
            selection_info.y=(int)
              (top_offset+selection_info.id*selection_info.height);
            selection_info.text=(char *) selections[selection_info.id];
            XDrawWidgetText(display,&windows->widget,&selection_info);
          }
        selection_info.id=id;
        if ((id < 0) || (id >= (int) number_selections))
          break;
        /*
          Highlight this selection.
        */
        selection_info.y=(int) (top_offset+id*selection_info.height);
        selection_info.text=(char *) selections[id];
        XDrawWidgetText(display,&windows->widget,&selection_info);
        highlight_info.y=selection_info.y+highlight_info.bevel_width;
        XDrawBevel(display,&windows->widget,&highlight_info);
        break;
      }
      default:
        break;
    }
  } while ((state & ExitState) == 0);
  (void) XFreeCursor(display,cursor);
  window_attributes.override_redirect=MagickFalse;
  (void) XChangeWindowAttributes(display,windows->widget.id,
    (size_t) CWOverrideRedirect,&window_attributes);
  (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
  XCheckRefreshWindows(display,windows);
  if (submenu_info.active != 0)
    {
      submenu_info.active=MagickFalse;
      toggle_info.raised=MagickFalse;
      XDrawTriangleEast(display,&windows->command,&toggle_info);
    }
  if ((selection_info.id < 0) || (selection_info.id >= (int) number_selections))
    return(~0);
  (void) CopyMagickString(item,selections[selection_info.id],MagickPathExtent);
  return(selection_info.id);
}


static PolygonInfo **AcquirePolygonThreadSet(
  const PrimitiveInfo *primitive_info,ExceptionInfo *exception)
{
  PathInfo
    *magick_restrict path_info;

  PolygonInfo
    **polygon_info;

  ssize_t
    i;

  size_t
    number_threads;

  number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
  polygon_info=(PolygonInfo **) AcquireQuantumMemory(number_threads,
    sizeof(*polygon_info));
  if (polygon_info == (PolygonInfo **) NULL)
    {
      (void) ThrowMagickException(exception,GetMagickModule(),
        ResourceLimitError,"MemoryAllocationFailed","`%s'","");
      return((PolygonInfo **) NULL);
    }
  (void) memset(polygon_info,0,number_threads*sizeof(*polygon_info));
  path_info=ConvertPrimitiveToPath(primitive_info,exception);
  if (path_info == (PathInfo *) NULL)
    return(DestroyPolygonThreadSet(polygon_info));
  polygon_info[0]=ConvertPathToPolygon(path_info,exception);
  if (polygon_info[0] == (PolygonInfo *) NULL)
    {
      (void) ThrowMagickException(exception,GetMagickModule(),
        ResourceLimitError,"MemoryAllocationFailed","`%s'","");
      return(DestroyPolygonThreadSet(polygon_info));
    }
  for (i=1; i < (ssize_t) number_threads; i++)
  {
    EdgeInfo
      *edge_info;

    ssize_t
      j;

    polygon_info[i]=(PolygonInfo *) AcquireMagickMemory(
      sizeof(*polygon_info[i]));
    if (polygon_info[i] == (PolygonInfo *) NULL)
      {
        (void) ThrowMagickException(exception,GetMagickModule(),
          ResourceLimitError,"MemoryAllocationFailed","`%s'","");
        return(DestroyPolygonThreadSet(polygon_info));
      }
    polygon_info[i]->number_edges=0;
    edge_info=polygon_info[0]->edges;
    polygon_info[i]->edges=(EdgeInfo *) AcquireQuantumMemory(
      polygon_info[0]->number_edges,sizeof(*edge_info));
    if (polygon_info[i]->edges == (EdgeInfo *) NULL)
      {
        (void) ThrowMagickException(exception,GetMagickModule(),
          ResourceLimitError,"MemoryAllocationFailed","`%s'","");
        return(DestroyPolygonThreadSet(polygon_info));
      }
    (void) memcpy(polygon_info[i]->edges,edge_info,
      polygon_info[0]->number_edges*sizeof(*edge_info));
    for (j=0; j < (ssize_t) polygon_info[i]->number_edges; j++)
      polygon_info[i]->edges[j].points=(PointInfo *) NULL;
    polygon_info[i]->number_edges=polygon_info[0]->number_edges;
    for (j=0; j < (ssize_t) polygon_info[i]->number_edges; j++)
    {
      edge_info=polygon_info[0]->edges+j;
      polygon_info[i]->edges[j].points=(PointInfo *) AcquireQuantumMemory(
        edge_info->number_points,sizeof(*edge_info));
      if (polygon_info[i]->edges[j].points == (PointInfo *) NULL)
        {
          (void) ThrowMagickException(exception,GetMagickModule(),
            ResourceLimitError,"MemoryAllocationFailed","`%s'","");
          return(DestroyPolygonThreadSet(polygon_info));
        }
      (void) memcpy(polygon_info[i]->edges[j].points,edge_info->points,
        edge_info->number_points*sizeof(*edge_info->points));
    }
  }
  path_info=(PathInfo *) RelinquishMagickMemory(path_info);
  return(polygon_info);
}


static int MVGMacroCompare(const void *target,const void *source)
{
  const char
    *p,
    *q;

  p=(const char *) target;
  q=(const char *) source;
  return(strcmp(p,q));
}