int device_driver_attach(struct device_driver *drv, struct device *dev)
{
	int ret = 0;

	__device_driver_lock(dev, dev->parent);

	/*
	 * If device has been removed or someone has already successfully
	 * bound a driver before us just skip the driver probe call.
	 */
	if (!dev->p->dead && !dev->driver)
		ret = driver_probe_device(drv, dev);

	__device_driver_unlock(dev, dev->parent);

	return ret;
}


static ssize_t control_show(struct device *dev, struct device_attribute *attr,
			    char *buf)
{
	return sysfs_emit(buf, "%s\n",
				dev->power.runtime_auto ? ctrl_auto : ctrl_on);
}


int device_links_read_lock_held(void)
{
	return lockdep_is_held(&device_links_lock);
}


static ssize_t phys_device_show(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	struct memory_block *mem = to_memory_block(dev);
	return sysfs_emit(buf, "%d\n", mem->phys_device);
}


static void devlink_remove_symlinks(struct device *dev,
				   struct class_interface *class_intf)
{
	struct device_link *link = to_devlink(dev);
	size_t len;
	struct device *sup = link->supplier;
	struct device *con = link->consumer;
	char *buf;

	sysfs_remove_link(&link->link_dev.kobj, "consumer");
	sysfs_remove_link(&link->link_dev.kobj, "supplier");

	len = max(strlen(dev_name(sup)), strlen(dev_name(con)));
	len += strlen("supplier:") + 1;
	buf = kzalloc(len, GFP_KERNEL);
	if (!buf) {
		WARN(1, "Unable to properly free device link symlinks!\n");
		return;
	}

	snprintf(buf, len, "supplier:%s", dev_name(sup));
	sysfs_remove_link(&con->kobj, buf);
	snprintf(buf, len, "consumer:%s", dev_name(con));
	sysfs_remove_link(&sup->kobj, buf);
	kfree(buf);
}


struct platform_device *platform_device_register_full(
		const struct platform_device_info *pdevinfo)
{
	int ret;
	struct platform_device *pdev;

	pdev = platform_device_alloc(pdevinfo->name, pdevinfo->id);
	if (!pdev)
		return ERR_PTR(-ENOMEM);

	pdev->dev.parent = pdevinfo->parent;
	pdev->dev.fwnode = pdevinfo->fwnode;
	pdev->dev.of_node = of_node_get(to_of_node(pdev->dev.fwnode));
	pdev->dev.of_node_reused = pdevinfo->of_node_reused;

	if (pdevinfo->dma_mask) {
		pdev->platform_dma_mask = pdevinfo->dma_mask;
		pdev->dev.dma_mask = &pdev->platform_dma_mask;
		pdev->dev.coherent_dma_mask = pdevinfo->dma_mask;
	}

	ret = platform_device_add_resources(pdev,
			pdevinfo->res, pdevinfo->num_res);
	if (ret)
		goto err;

	ret = platform_device_add_data(pdev,
			pdevinfo->data, pdevinfo->size_data);
	if (ret)
		goto err;

	if (pdevinfo->properties) {
		ret = platform_device_add_properties(pdev,
						     pdevinfo->properties);
		if (ret)
			goto err;
	}

	ret = platform_device_add(pdev);
	if (ret) {
err:
		ACPI_COMPANION_SET(&pdev->dev, NULL);
		platform_device_put(pdev);
		return ERR_PTR(ret);
	}

	return pdev;
}


static void __driver_attach_async_helper(void *_dev, async_cookie_t cookie)
{
	struct device *dev = _dev;
	struct device_driver *drv;
	int ret = 0;

	__device_driver_lock(dev, dev->parent);

	drv = dev->p->async_driver;

	/*
	 * If device has been removed or someone has already successfully
	 * bound a driver before us just skip the driver probe call.
	 */
	if (!dev->p->dead && !dev->driver)
		ret = driver_probe_device(drv, dev);

	__device_driver_unlock(dev, dev->parent);

	dev_dbg(dev, "driver %s async attach completed: %d\n", drv->name, ret);

	put_device(dev);
}


void fw_devlink_pause(void)
{
	mutex_lock(&defer_fw_devlink_lock);
	defer_fw_devlink_count++;
	mutex_unlock(&defer_fw_devlink_lock);
}


static int __init sysfs_deprecated_setup(char *arg)
{
	return kstrtol(arg, 10, &sysfs_deprecated);
}


static ssize_t state_synced_show(struct device *dev,
				 struct device_attribute *attr, char *buf)
{
	bool val;

	device_lock(dev);
	val = dev->state_synced;
	device_unlock(dev);
	return sysfs_emit(buf, "%u\n", val);
}


static ssize_t firmware_loading_show(struct device *dev,
				     struct device_attribute *attr, char *buf)
{
	struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
	int loading = 0;

	mutex_lock(&fw_lock);
	if (fw_sysfs->fw_priv)
		loading = fw_sysfs_loading(fw_sysfs->fw_priv);
	mutex_unlock(&fw_lock);

	return sysfs_emit(buf, "%d\n", loading);
}


void root_device_unregister(struct device *dev)
{
	struct root_device *root = to_root_device(dev);

	if (root->owner)
		sysfs_remove_link(&root->dev.kobj, "module");

	device_unregister(dev);
}


static void parsing_done_workfn(struct work_struct *work)
{
	cpufreq_unregister_notifier(&init_cpu_capacity_notifier,
					 CPUFREQ_POLICY_NOTIFIER);
	free_cpumask_var(cpus_to_visit);
}


int device_change_owner(struct device *dev, kuid_t kuid, kgid_t kgid)
{
	int error;
	struct kobject *kobj = &dev->kobj;

	dev = get_device(dev);
	if (!dev)
		return -EINVAL;

	/*
	 * Change the kobject and the default attributes and groups of the
	 * ktype associated with it to @kuid/@kgid.
	 */
	error = sysfs_change_owner(kobj, kuid, kgid);
	if (error)
		goto out;

	/*
	 * Change the uevent file for @dev to the new owner. The uevent file
	 * was created in a separate step when @dev got added and we mirror
	 * that step here.
	 */
	error = sysfs_file_change_owner(kobj, dev_attr_uevent.attr.name, kuid,
					kgid);
	if (error)
		goto out;

	/*
	 * Change the device groups, the device groups associated with the
	 * device class, and the groups associated with the device type of @dev
	 * to @kuid/@kgid.
	 */
	error = device_attrs_change_owner(dev, kuid, kgid);
	if (error)
		goto out;

	error = dpm_sysfs_change_owner(dev, kuid, kgid);
	if (error)
		goto out;

#ifdef CONFIG_BLOCK
	if (sysfs_deprecated && dev->class == &block_class)
		goto out;
#endif

	/*
	 * Change the owner of the symlink located in the class directory of
	 * the device class associated with @dev which points to the actual
	 * directory entry for @dev to @kuid/@kgid. This ensures that the
	 * symlink shows the same permissions as its target.
	 */
	error = sysfs_link_change_owner(&dev->class->p->subsys.kobj, &dev->kobj,
					dev_name(dev), kuid, kgid);
	if (error)
		goto out;

out:
	put_device(dev);
	return error;
}


static int register_mem_sect_under_node(struct memory_block *mem_blk,
					 void *arg)
{
	unsigned long memory_block_pfns = memory_block_size_bytes() / PAGE_SIZE;
	unsigned long start_pfn = section_nr_to_pfn(mem_blk->start_section_nr);
	unsigned long end_pfn = start_pfn + memory_block_pfns - 1;
	int ret, nid = *(int *)arg;
	unsigned long pfn;

	for (pfn = start_pfn; pfn <= end_pfn; pfn++) {
		int page_nid;

		/*
		 * memory block could have several absent sections from start.
		 * skip pfn range from absent section
		 */
		if (!pfn_in_present_section(pfn)) {
			pfn = round_down(pfn + PAGES_PER_SECTION,
					 PAGES_PER_SECTION) - 1;
			continue;
		}

		/*
		 * We need to check if page belongs to nid only for the boot
		 * case, during hotplug we know that all pages in the memory
		 * block belong to the same node.
		 */
		if (system_state == SYSTEM_BOOTING) {
			page_nid = get_nid_for_pfn(pfn);
			if (page_nid < 0)
				continue;
			if (page_nid != nid)
				continue;
		}

		/*
		 * If this memory block spans multiple nodes, we only indicate
		 * the last processed node.
		 */
		mem_blk->nid = nid;

		ret = sysfs_create_link_nowarn(&node_devices[nid]->dev.kobj,
					&mem_blk->dev.kobj,
					kobject_name(&mem_blk->dev.kobj));
		if (ret)
			return ret;

		return sysfs_create_link_nowarn(&mem_blk->dev.kobj,
				&node_devices[nid]->dev.kobj,
				kobject_name(&node_devices[nid]->dev.kobj));
	}
	/* mem section does not span the specified node */
	return 0;
}


static int really_probe_debug(struct device *dev, struct device_driver *drv)
{
	ktime_t calltime, rettime;
	int ret;

	calltime = ktime_get();
	ret = really_probe(dev, drv);
	rettime = ktime_get();
	pr_debug("probe of %s returned %d after %lld usecs\n",
		 dev_name(dev), ret, ktime_us_delta(rettime, calltime));
	return ret;
}


bool device_is_bound(struct device *dev)
{
	return dev->p && klist_node_attached(&dev->p->knode_driver);
}


int devm_device_add_groups(struct device *dev,
			   const struct attribute_group **groups)
{
	union device_attr_group_devres *devres;
	int error;

	devres = devres_alloc(devm_attr_groups_remove,
			      sizeof(*devres), GFP_KERNEL);
	if (!devres)
		return -ENOMEM;

	error = sysfs_create_groups(&dev->kobj, groups);
	if (error) {
		devres_free(devres);
		return error;
	}

	devres->groups = groups;
	devres_add(dev, devres);
	return 0;
}


static ssize_t probe_store(struct device *dev, struct device_attribute *attr,
			   const char *buf, size_t count)
{
	u64 phys_addr;
	int nid, ret;
	unsigned long pages_per_block = PAGES_PER_SECTION * sections_per_block;

	ret = kstrtoull(buf, 0, &phys_addr);
	if (ret)
		return ret;

	if (phys_addr & ((pages_per_block << PAGE_SHIFT) - 1))
		return -EINVAL;

	ret = lock_device_hotplug_sysfs();
	if (ret)
		return ret;

	nid = memory_add_physaddr_to_nid(phys_addr);
	ret = __add_memory(nid, phys_addr,
			   MIN_MEMORY_BLOCK_SIZE * sections_per_block);

	if (ret)
		goto out;

	ret = count;
out:
	unlock_device_hotplug();
	return ret;
}


int device_match_devt(struct device *dev, const void *pdevt)
{
	return dev->devt == *(dev_t *)pdevt;
}


void wakeup_source_sysfs_remove(struct wakeup_source *ws)
{
	device_unregister(ws->dev);
}


static int fw_load_from_user_helper(struct firmware *firmware,
				    const char *name, struct device *device,
				    u32 opt_flags)
{
	struct fw_sysfs *fw_sysfs;
	long timeout;
	int ret;

	timeout = firmware_loading_timeout();
	if (opt_flags & FW_OPT_NOWAIT) {
		timeout = usermodehelper_read_lock_wait(timeout);
		if (!timeout) {
			dev_dbg(device, "firmware: %s loading timed out\n",
				name);
			return -EBUSY;
		}
	} else {
		ret = usermodehelper_read_trylock();
		if (WARN_ON(ret)) {
			dev_err(device, "firmware: %s will not be loaded\n",
				name);
			return ret;
		}
	}

	fw_sysfs = fw_create_instance(firmware, name, device, opt_flags);
	if (IS_ERR(fw_sysfs)) {
		ret = PTR_ERR(fw_sysfs);
		goto out_unlock;
	}

	fw_sysfs->fw_priv = firmware->priv;
	ret = fw_load_sysfs_fallback(fw_sysfs, opt_flags, timeout);

	if (!ret)
		ret = assign_fw(firmware, device, opt_flags);

out_unlock:
	usermodehelper_read_unlock();

	return ret;
}


static inline unsigned long memory_block_id(unsigned long section_nr)
{
	return section_nr / sections_per_block;
}


void register_hugetlbfs_with_node(node_registration_func_t doregister,
				  node_registration_func_t unregister)
{
	__hugetlb_register_node   = doregister;
	__hugetlb_unregister_node = unregister;
}


void __init memory_dev_init(void)
{
	int ret;
	unsigned long block_sz, nr;

	/* Validate the configured memory block size */
	block_sz = memory_block_size_bytes();
	if (!is_power_of_2(block_sz) || block_sz < MIN_MEMORY_BLOCK_SIZE)
		panic("Memory block size not suitable: 0x%lx\n", block_sz);
	sections_per_block = block_sz / MIN_MEMORY_BLOCK_SIZE;

	ret = subsys_system_register(&memory_subsys, memory_root_attr_groups);
	if (ret)
		panic("%s() failed to register subsystem: %d\n", __func__, ret);

	/*
	 * Create entries for memory sections that were found
	 * during boot and have been initialized
	 */
	for (nr = 0; nr <= __highest_present_section_nr;
	     nr += sections_per_block) {
		ret = add_memory_block(nr);
		if (ret)
			panic("%s() failed to add memory block: %d\n", __func__,
			      ret);
	}
}


bool driver_allows_async_probing(struct device_driver *drv)
{
	switch (drv->probe_type) {
	case PROBE_PREFER_ASYNCHRONOUS:
		return true;

	case PROBE_FORCE_SYNCHRONOUS:
		return false;

	default:
		if (cmdline_requested_async_probing(drv->name))
			return true;

		if (module_requested_async_probing(drv->owner))
			return true;

		return false;
	}
}


void device_remove_file(struct device *dev,
			const struct device_attribute *attr)
{
	if (dev)
		sysfs_remove_file(&dev->kobj, &attr->attr);
}


static void cache_get_line_size(struct cacheinfo *this_leaf,
				struct device_node *np)
{
	int i, lim, ct_idx;

	ct_idx = get_cacheinfo_idx(this_leaf->type);
	lim = ARRAY_SIZE(cache_type_info[ct_idx].line_size_props);

	for (i = 0; i < lim; i++) {
		int ret;
		u32 line_size;
		const char *propname;

		propname = cache_type_info[ct_idx].line_size_props[i];
		ret = of_property_read_u32(np, propname, &line_size);
		if (!ret) {
			this_leaf->coherency_line_size = line_size;
			break;
		}
	}
}


static int do_firmware_uevent(struct fw_sysfs *fw_sysfs, struct kobj_uevent_env *env)
{
	if (add_uevent_var(env, "FIRMWARE=%s", fw_sysfs->fw_priv->fw_name))
		return -ENOMEM;
	if (add_uevent_var(env, "TIMEOUT=%i", __firmware_loading_timeout()))
		return -ENOMEM;
	if (add_uevent_var(env, "ASYNC=%d", fw_sysfs->nowait))
		return -ENOMEM;

	return 0;
}


int dev_set_name(struct device *dev, const char *fmt, ...)
{
	va_list vargs;
	int err;

	va_start(vargs, fmt);
	err = kobject_set_name_vargs(&dev->kobj, fmt, vargs);
	va_end(vargs);
	return err;
}


static inline bool cmdline_requested_async_probing(const char *drv_name)
{
	return parse_option_str(async_probe_drv_names, drv_name);
}


static ssize_t soft_offline_page_store(struct device *dev,
				       struct device_attribute *attr,
				       const char *buf, size_t count)
{
	int ret;
	u64 pfn;
	if (!capable(CAP_SYS_ADMIN))
		return -EPERM;
	if (kstrtoull(buf, 0, &pfn) < 0)
		return -EINVAL;
	pfn >>= PAGE_SHIFT;
	ret = soft_offline_page(pfn, 0);
	return ret == 0 ? count : ret;
}


static void fw_dev_release(struct device *dev)
{
	struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);

	kfree(fw_sysfs);
}


int memhp_online_type_from_str(const char *str)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(online_type_to_str); i++) {
		if (sysfs_streq(str, online_type_to_str[i]))
			return i;
	}
	return -EINVAL;
}


static ssize_t status_show(struct device *dev,
			  struct device_attribute *attr, char *buf)
{
	char *status;

	switch (to_devlink(dev)->status) {
	case DL_STATE_NONE:
		status = "not tracked"; break;
	case DL_STATE_DORMANT:
		status = "dormant"; break;
	case DL_STATE_AVAILABLE:
		status = "available"; break;
	case DL_STATE_CONSUMER_PROBE:
		status = "consumer probing"; break;
	case DL_STATE_ACTIVE:
		status = "active"; break;
	case DL_STATE_SUPPLIER_UNBIND:
		status = "supplier unbinding"; break;
	default:
		status = "unknown"; break;
	}
	return sysfs_emit(buf, "%s\n", status);
}


static ssize_t show_node_state(struct device *dev,
			       struct device_attribute *attr, char *buf)
{
	struct node_attr *na = container_of(attr, struct node_attr, attr);
	return print_nodes_state(na->state, buf);
}


static int fw_realloc_pages(struct fw_sysfs *fw_sysfs, int min_size)
{
	int err;

	err = fw_grow_paged_buf(fw_sysfs->fw_priv,
				PAGE_ALIGN(min_size) >> PAGE_SHIFT);
	if (err)
		fw_load_abort(fw_sysfs);
	return err;
}


dbcs_char2len(
    int		c)
{
    if (c >= 0x100)
	return 2;
    return 1;
}


utf_allow_break_before(int cc)
{
    static const int BOL_prohibition_punct[] =
    {
	'!',
	'%',
	')',
	',',
	':',
	';',
	'>',
	'?',
	']',
	'}',
	0x2019, // ’ right single quotation mark
	0x201d, // ” right double quotation mark
	0x2020, // † dagger
	0x2021, // ‡ double dagger
	0x2026, // … horizontal ellipsis
	0x2030, // ‰ per mille sign
	0x2031, // ‱ per then thousand sign
	0x203c, // ‼ double exclamation mark
	0x2047, // ⁇ double question mark
	0x2048, // ⁈ question exclamation mark
	0x2049, // ⁉ exclamation question mark
	0x2103, // ℃ degree celsius
	0x2109, // ℉ degree fahrenheit
	0x3001, // 、 ideographic comma
	0x3002, // 。 ideographic full stop
	0x3009, // 〉 right angle bracket
	0x300b, // 》 right double angle bracket
	0x300d, // 」 right corner bracket
	0x300f, // 』 right white corner bracket
	0x3011, // 】 right black lenticular bracket
	0x3015, // 〕 right tortoise shell bracket
	0x3017, // 〗 right white lenticular bracket
	0x3019, // 〙 right white tortoise shell bracket
	0x301b, // 〛 right white square bracket
	0xff01, // ！ fullwidth exclamation mark
	0xff09, // ） fullwidth right parenthesis
	0xff0c, // ， fullwidth comma
	0xff0e, // ． fullwidth full stop
	0xff1a, // ： fullwidth colon
	0xff1b, // ； fullwidth semicolon
	0xff1f, // ？ fullwidth question mark
	0xff3d, // ］ fullwidth right square bracket
	0xff5d, // ｝ fullwidth right curly bracket
    };

    int first = 0;
    int last  = ARRAY_LENGTH(BOL_prohibition_punct) - 1;
    int mid   = 0;

    while (first < last)
    {
	mid = (first + last)/2;

	if (cc == BOL_prohibition_punct[mid])
	    return FALSE;
	else if (cc > BOL_prohibition_punct[mid])
	    first = mid + 1;
	else
	    last = mid - 1;
    }

    return cc != BOL_prohibition_punct[first];
}


latin_ptr2cells(char_u *p UNUSED)
{
    return 1;
}


enc_locale(void)
{
#ifdef MSWIN
    char	buf[50];
    long	acp = GetACP();

    if (acp == 1200)
	STRCPY(buf, "ucs-2le");
    else if (acp == 1252)	    // cp1252 is used as latin1
	STRCPY(buf, "latin1");
    else if (acp == 65001)
	STRCPY(buf, "utf-8");
    else
	sprintf(buf, "cp%ld", acp);

    return enc_canonize((char_u *)buf);
#else
    char	*s;

# ifdef HAVE_NL_LANGINFO_CODESET
    if ((s = nl_langinfo(CODESET)) == NULL || *s == NUL)
# endif
# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
	if ((s = setlocale(LC_CTYPE, NULL)) == NULL || *s == NUL)
# endif
	    s = NULL;

    return enc_locale_env(s);
#endif
}


dbcs_head_off(char_u *base, char_u *p)
{
    char_u	*q;

    // It can't be a trailing byte when not using DBCS, at the start of the
    // string or the previous byte can't start a double-byte.
    if (p <= base || MB_BYTE2LEN(p[-1]) == 1 || *p == NUL)
	return 0;

    // This is slow: need to start at the base and go forward until the
    // byte we are looking for.  Return 1 when we went past it, 0 otherwise.
    q = base;
    while (q < p)
	q += dbcs_ptr2len(q);
    return (q == p) ? 0 : 1;
}


f_setcellwidths(typval_T *argvars, typval_T *rettv UNUSED)
{
    list_T	    *l;
    listitem_T	    *li;
    int		    item;
    int		    i;
    listitem_T	    **ptrs;
    cw_interval_T   *table;
    cw_interval_T   *cw_table_save;
    size_t	    cw_table_size_save;
    char	    *error = NULL;

    if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
	return;

    if (argvars[0].v_type != VAR_LIST || argvars[0].vval.v_list == NULL)
    {
	emsg(_(e_list_required));
	return;
    }
    l = argvars[0].vval.v_list;
    if (l->lv_len == 0)
    {
	// Clearing the table.
	vim_free(cw_table);
	cw_table = NULL;
	cw_table_size = 0;
	return;
    }

    ptrs = ALLOC_MULT(listitem_T *, l->lv_len);
    if (ptrs == NULL)
	return;

    // Check that all entries are a list with three numbers, the range is
    // valid and the cell width is valid.
    item = 0;
    for (li = l->lv_first; li != NULL; li = li->li_next)
    {
	listitem_T *lili;
	varnumber_T n1;

	if (li->li_tv.v_type != VAR_LIST || li->li_tv.vval.v_list == NULL)
	{
	    semsg(_(e_list_item_nr_is_not_list), item);
	    vim_free(ptrs);
	    return;
	}

	lili = li->li_tv.vval.v_list->lv_first;
	ptrs[item] = lili;
	for (i = 0; lili != NULL; lili = lili->li_next, ++i)
	{
	    if (lili->li_tv.v_type != VAR_NUMBER)
		break;
	    if (i == 0)
	    {
		n1 = lili->li_tv.vval.v_number;
		if (n1 < 0x100)
		{
		    emsg(_(e_only_values_of_0x100_and_higher_supported));
		    vim_free(ptrs);
		    return;
		}
	    }
	    else if (i == 1 && lili->li_tv.vval.v_number < n1)
	    {
		semsg(_(e_list_item_nr_range_invalid), item);
		vim_free(ptrs);
		return;
	    }
	    else if (i == 2 && (lili->li_tv.vval.v_number < 1
					     || lili->li_tv.vval.v_number > 2))
	    {
		semsg(_(e_list_item_nr_cell_width_invalid), item);
		vim_free(ptrs);
		return;
	    }
	}
	if (i != 3)
	{
	    semsg(_(e_list_item_nr_does_not_contain_3_numbers), item);
	    vim_free(ptrs);
	    return;
	}
	++item;
    }

    // Sort the list on the first number.
    qsort((void *)ptrs, (size_t)l->lv_len, sizeof(listitem_T *), tv_nr_compare);

    table = ALLOC_MULT(cw_interval_T, l->lv_len);
    if (table == NULL)
    {
	vim_free(ptrs);
	return;
    }

    // Store the items in the new table.
    item = 0;
    for (item = 0; item < l->lv_len; ++item)
    {
	listitem_T	*lili = ptrs[item];
	varnumber_T	n1;

	n1 = lili->li_tv.vval.v_number;
	if (item > 0 && n1 <= table[item - 1].last)
	{
	    semsg(_(e_overlapping_ranges_for_nr), (long)n1);
	    vim_free(ptrs);
	    vim_free(table);
	    return;
	}
	table[item].first = n1;
	lili = lili->li_next;
	table[item].last = lili->li_tv.vval.v_number;
	lili = lili->li_next;
	table[item].width = lili->li_tv.vval.v_number;
    }

    vim_free(ptrs);

    cw_table_save = cw_table;
    cw_table_size_save = cw_table_size;
    cw_table = table;
    cw_table_size = l->lv_len;

    // Check that the new value does not conflict with 'listchars' or
    // 'fillchars'.
    error = check_chars_options();
    if (error != NULL)
    {
	emsg(_(error));
	cw_table = cw_table_save;
	cw_table_size = cw_table_size_save;
	vim_free(table);
	return;
    }

    vim_free(cw_table_save);
}


enc_canon_props(char_u *name)
{
    int		i;

    i = enc_canon_search(name);
    if (i >= 0)
	return enc_canon_table[i].prop;
#ifdef MSWIN
    if (name[0] == 'c' && name[1] == 'p' && VIM_ISDIGIT(name[2]))
    {
	CPINFO	cpinfo;

	// Get info on this codepage to find out what it is.
	if (GetCPInfo(atoi((char *)name + 2), &cpinfo) != 0)
	{
	    if (cpinfo.MaxCharSize == 1) // some single-byte encoding
		return ENC_8BIT;
	    if (cpinfo.MaxCharSize == 2
		    && (cpinfo.LeadByte[0] != 0 || cpinfo.LeadByte[1] != 0))
		// must be a DBCS encoding
		return ENC_DBCS;
	}
	return 0;
    }
#endif
    if (STRNCMP(name, "2byte-", 6) == 0)
	return ENC_DBCS;
    if (STRNCMP(name, "8bit-", 5) == 0 || STRNCMP(name, "iso-8859-", 9) == 0)
	return ENC_8BIT;
    return 0;
}


item_exists(char_u *name, size_t len, int cmd UNUSED, cctx_T *cctx)
{
    return variable_exists(name, len, cctx);
}


compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
{
    char_u	*var_start;
    char_u	*p;
    char_u	*end = arg;
    char_u	*ret = NULL;
    int		var_count = 0;
    int		var_idx;
    int		semicolon = 0;
    int		did_generate_slice = FALSE;
    garray_T	*instr = &cctx->ctx_instr;
    char_u	*op;
    int		oplen = 0;
    int		heredoc = FALSE;
    int		incdec = FALSE;
    type_T	*rhs_type = &t_any;
    char_u	*sp;
    int		is_decl = is_decl_command(cmdidx);
    lhs_T	lhs;
    long	start_lnum = SOURCING_LNUM;

    // Skip over the "varname" or "[varname, varname]" to get to any "=".
    p = skip_var_list(arg, TRUE, &var_count, &semicolon, TRUE);
    if (p == NULL)
	return *arg == '[' ? arg : NULL;

    lhs.lhs_name = NULL;

    if (eap->cmdidx == CMD_increment || eap->cmdidx == CMD_decrement)
    {
	if (VIM_ISWHITE(eap->cmd[2]))
	{
	    semsg(_(e_no_white_space_allowed_after_str_str),
			 eap->cmdidx == CMD_increment ? "++" : "--", eap->cmd);
	    return NULL;
	}
	op = (char_u *)(eap->cmdidx == CMD_increment ? "+=" : "-=");
	oplen = 2;
	incdec = TRUE;
    }
    else
    {
	sp = p;
	p = skipwhite(p);
	op = p;
	oplen = assignment_len(p, &heredoc);

	if (var_count > 0 && oplen == 0)
	    // can be something like "[1, 2]->func()"
	    return arg;

	if (oplen > 0 && (!VIM_ISWHITE(*sp) || !IS_WHITE_OR_NUL(op[oplen])))
	{
	    error_white_both(op, oplen);
	    return NULL;
	}
    }

    if (heredoc)
    {
	list_T	   *l;

	// [let] varname =<< [trim] {end}
	eap->getline = exarg_getline;
	eap->cookie = cctx;
	l = heredoc_get(eap, op + 3, FALSE, TRUE);
	if (l == NULL)
	    return NULL;

	list_free(l);
	p += STRLEN(p);
	end = p;
    }
    else if (var_count > 0)
    {
	char_u *wp;

	// for "[var, var] = expr" evaluate the expression here, loop over the
	// list of variables below.
	// A line break may follow the "=".

	wp = op + oplen;
	if (may_get_next_line_error(wp, &p, cctx) == FAIL)
	    return FAIL;
	if (compile_expr0(&p, cctx) == FAIL)
	    return NULL;
	end = p;

	if (cctx->ctx_skip != SKIP_YES)
	{
	    type_T	*stacktype;
	    int		needed_list_len;
	    int		did_check = FALSE;

	    stacktype = cctx->ctx_type_stack.ga_len == 0 ? &t_void
						  : get_type_on_stack(cctx, 0);
	    if (stacktype->tt_type == VAR_VOID)
	    {
		emsg(_(e_cannot_use_void_value));
		goto theend;
	    }
	    if (need_type(stacktype, &t_list_any, -1, 0, cctx,
							 FALSE, FALSE) == FAIL)
		goto theend;
	    // If a constant list was used we can check the length right here.
	    needed_list_len = semicolon ? var_count - 1 : var_count;
	    if (instr->ga_len > 0)
	    {
		isn_T	*isn = ((isn_T *)instr->ga_data) + instr->ga_len - 1;

		if (isn->isn_type == ISN_NEWLIST)
		{
		    did_check = TRUE;
		    if (semicolon ? isn->isn_arg.number < needed_list_len
			    : isn->isn_arg.number != needed_list_len)
		    {
			semsg(_(e_expected_nr_items_but_got_nr),
				    needed_list_len, (int)isn->isn_arg.number);
			goto theend;
		    }
		}
	    }
	    if (!did_check)
		generate_CHECKLEN(cctx, needed_list_len, semicolon);
	    if (stacktype->tt_member != NULL)
		rhs_type = stacktype->tt_member;
	}
    }

    /*
     * Loop over variables in "[var, var] = expr".
     * For "var = expr" and "let var: type" this is done only once.
     */
    if (var_count > 0)
	var_start = skipwhite(arg + 1);  // skip over the "["
    else
	var_start = arg;
    for (var_idx = 0; var_idx == 0 || var_idx < var_count; var_idx++)
    {
	int	instr_count = -1;
	int	save_lnum;
	int	skip_store = FALSE;
	type_T	*inferred_type = NULL;

	if (var_start[0] == '_' && !eval_isnamec(var_start[1]))
	{
	    // Ignore underscore in "[a, _, b] = list".
	    if (var_count > 0)
	    {
		var_start = skipwhite(var_start + 2);
		continue;
	    }
	    emsg(_(e_cannot_use_underscore_here));
	    goto theend;
	}
	vim_free(lhs.lhs_name);

	/*
	 * Figure out the LHS type and other properties.
	 */
	if (compile_assign_lhs(var_start, &lhs, cmdidx,
				is_decl, heredoc, var_start > eap->cmd,
				oplen, cctx) == FAIL)
	    goto theend;
	if (heredoc)
	{
	    SOURCING_LNUM = start_lnum;
	    if (lhs.lhs_has_type
		    && need_type(&t_list_string, lhs.lhs_type,
					    -1, 0, cctx, FALSE, FALSE) == FAIL)
		goto theend;
	}
	else
	{
	    if (cctx->ctx_skip == SKIP_YES)
	    {
		if (oplen > 0 && var_count == 0)
		{
		    // skip over the "=" and the expression
		    p = skipwhite(op + oplen);
		    (void)compile_expr0(&p, cctx);
		}
	    }
	    else if (oplen > 0)
	    {
		int	is_const = FALSE;
		char_u	*wp;

		// for "+=", "*=", "..=" etc. first load the current value
		if (*op != '='
			&& compile_load_lhs_with_index(&lhs, var_start,
								 cctx) == FAIL)
		    goto theend;

		// For "var = expr" evaluate the expression.
		if (var_count == 0)
		{
		    int	r;

		    // Compile the expression.
		    instr_count = instr->ga_len;
		    if (incdec)
		    {
			r = generate_PUSHNR(cctx, 1);
		    }
		    else
		    {
			// Temporarily hide the new local variable here, it is
			// not available to this expression.
			if (lhs.lhs_new_local)
			    --cctx->ctx_locals.ga_len;
			wp = op + oplen;
			if (may_get_next_line_error(wp, &p, cctx) == FAIL)
			{
			    if (lhs.lhs_new_local)
				++cctx->ctx_locals.ga_len;
			    goto theend;
			}
			r = compile_expr0_ext(&p, cctx, &is_const);
			if (lhs.lhs_new_local)
			    ++cctx->ctx_locals.ga_len;
			if (r == FAIL)
			    goto theend;
		    }
		}
		else if (semicolon && var_idx == var_count - 1)
		{
		    // For "[var; var] = expr" get the rest of the list
		    did_generate_slice = TRUE;
		    if (generate_SLICE(cctx, var_count - 1) == FAIL)
			goto theend;
		}
		else
		{
		    // For "[var, var] = expr" get the "var_idx" item from the
		    // list.
		    if (generate_GETITEM(cctx, var_idx, *op != '=') == FAIL)
			goto theend;
		}

		rhs_type = cctx->ctx_type_stack.ga_len == 0 ? &t_void
						  : get_type_on_stack(cctx, 0);
		if (lhs.lhs_lvar != NULL && (is_decl || !lhs.lhs_has_type))
		{
		    if ((rhs_type->tt_type == VAR_FUNC
				|| rhs_type->tt_type == VAR_PARTIAL)
			    && !lhs.lhs_has_index
			    && var_wrong_func_name(lhs.lhs_name, TRUE))
			goto theend;

		    if (lhs.lhs_new_local && !lhs.lhs_has_type)
		    {
			if (rhs_type->tt_type == VAR_VOID)
			{
			    emsg(_(e_cannot_use_void_value));
			    goto theend;
			}
			else
			{
			    // An empty list or dict has a &t_unknown member,
			    // for a variable that implies &t_any.
			    if (rhs_type == &t_list_empty)
				lhs.lhs_lvar->lv_type = &t_list_any;
			    else if (rhs_type == &t_dict_empty)
				lhs.lhs_lvar->lv_type = &t_dict_any;
			    else if (rhs_type == &t_unknown)
				lhs.lhs_lvar->lv_type = &t_any;
			    else
			    {
				lhs.lhs_lvar->lv_type = rhs_type;
				inferred_type = rhs_type;
			    }
			}
		    }
		    else if (*op == '=')
		    {
			type_T *use_type = lhs.lhs_lvar->lv_type;
			where_T where = WHERE_INIT;

			// Without operator check type here, otherwise below.
			// Use the line number of the assignment.
			SOURCING_LNUM = start_lnum;
			where.wt_index = var_count > 0 ? var_idx + 1 : 0;
			where.wt_variable = var_count > 0;
			// If assigning to a list or dict member, use the
			// member type.  Not for "list[:] =".
			if (lhs.lhs_has_index
				&& !has_list_index(var_start + lhs.lhs_varlen,
									 cctx))
			    use_type = lhs.lhs_member_type;
			if (need_type_where(rhs_type, use_type, -1, where,
						cctx, FALSE, is_const) == FAIL)
			    goto theend;
		    }
		}
		else
		{
		    type_T *lhs_type = lhs.lhs_member_type;

		    // Special case: assigning to @# can use a number or a
		    // string.
		    // Also: can assign a number to a float.
		    if ((lhs_type == &t_number_or_string
				|| lhs_type == &t_float)
			    && rhs_type->tt_type == VAR_NUMBER)
			lhs_type = &t_number;
		    if (*p != '=' && need_type(rhs_type, lhs_type,
					    -1, 0, cctx, FALSE, FALSE) == FAIL)
		    goto theend;
		}
	    }
	    else if (cmdidx == CMD_final)
	    {
		emsg(_(e_final_requires_a_value));
		goto theend;
	    }
	    else if (cmdidx == CMD_const)
	    {
		emsg(_(e_const_requires_a_value));
		goto theend;
	    }
	    else if (!lhs.lhs_has_type || lhs.lhs_dest == dest_option
					   || lhs.lhs_dest == dest_func_option)
	    {
		emsg(_(e_type_or_initialization_required));
		goto theend;
	    }
	    else
	    {
		int r = OK;

		// variables are always initialized
		if (GA_GROW_FAILS(instr, 1))
		    goto theend;
		switch (lhs.lhs_member_type->tt_type)
		{
		    case VAR_BOOL:
			r = generate_PUSHBOOL(cctx, VVAL_FALSE);
			break;
		    case VAR_FLOAT:
#ifdef FEAT_FLOAT
			r = generate_PUSHF(cctx, 0.0);
#endif
			break;
		    case VAR_STRING:
			r = generate_PUSHS(cctx, NULL);
			break;
		    case VAR_BLOB:
			r = generate_PUSHBLOB(cctx, blob_alloc());
			break;
		    case VAR_FUNC:
			r = generate_PUSHFUNC(cctx, NULL, &t_func_void);
			break;
		    case VAR_LIST:
			r = generate_NEWLIST(cctx, 0, FALSE);
			break;
		    case VAR_DICT:
			r = generate_NEWDICT(cctx, 0, FALSE);
			break;
		    case VAR_JOB:
			r = generate_PUSHJOB(cctx);
			break;
		    case VAR_CHANNEL:
			r = generate_PUSHCHANNEL(cctx);
			break;
		    case VAR_NUMBER:
		    case VAR_UNKNOWN:
		    case VAR_ANY:
		    case VAR_PARTIAL:
		    case VAR_VOID:
		    case VAR_INSTR:
		    case VAR_SPECIAL:  // cannot happen
			// This is skipped for local variables, they are always
			// initialized to zero.  But in a "for" or "while" loop
			// the value may have been changed.
			if (lhs.lhs_dest == dest_local
						   && !inside_loop_scope(cctx))
			    skip_store = TRUE;
			else
			{
			    instr_count = instr->ga_len;
			    r = generate_PUSHNR(cctx, 0);
			}
			break;
		}
		if (r == FAIL)
		    goto theend;
	    }
	    if (var_count == 0)
		end = p;
	}

	// no need to parse more when skipping
	if (cctx->ctx_skip == SKIP_YES)
	    break;

	if (oplen > 0 && *op != '=')
	{
	    type_T	    *expected;
	    type_T	    *stacktype = NULL;

	    if (*op == '.')
	    {
		if (may_generate_2STRING(-1, FALSE, cctx) == FAIL)
		    goto theend;
	    }
	    else
	    {
		expected = lhs.lhs_member_type;
		stacktype = get_type_on_stack(cctx, 0);
		if (
#ifdef FEAT_FLOAT
		    // If variable is float operation with number is OK.
		    !(expected == &t_float && (stacktype == &t_number
			    || stacktype == &t_number_bool)) &&
#endif
		    need_type(stacktype, expected, -1, 0, cctx,
							 FALSE, FALSE) == FAIL)
		    goto theend;
	    }

	    if (*op == '.')
	    {
		if (generate_CONCAT(cctx, 2) == FAIL)
		    goto theend;
	    }
	    else if (*op == '+')
	    {
		if (generate_add_instr(cctx,
			    operator_type(lhs.lhs_member_type, stacktype),
				       lhs.lhs_member_type, stacktype,
							  EXPR_APPEND) == FAIL)
		    goto theend;
	    }
	    else if (generate_two_op(cctx, op) == FAIL)
		goto theend;
	}

	// Use the line number of the assignment for store instruction.
	save_lnum = cctx->ctx_lnum;
	cctx->ctx_lnum = start_lnum - 1;

	if (lhs.lhs_has_index)
	{
	    // Use the info in "lhs" to store the value at the index in the
	    // list or dict.
	    if (compile_assign_unlet(var_start, &lhs, TRUE, rhs_type, cctx)
								       == FAIL)
	    {
		cctx->ctx_lnum = save_lnum;
		goto theend;
	    }
	}
	else
	{
	    if (is_decl && cmdidx == CMD_const && (lhs.lhs_dest == dest_script
						|| lhs.lhs_dest == dest_global
						|| lhs.lhs_dest == dest_local))
		// ":const var": lock the value, but not referenced variables
		generate_LOCKCONST(cctx);

	    if ((lhs.lhs_type->tt_type == VAR_DICT
					  || lhs.lhs_type->tt_type == VAR_LIST)
		    && lhs.lhs_type->tt_member != NULL
		    && lhs.lhs_type->tt_member != &t_any
		    && lhs.lhs_type->tt_member != &t_unknown)
		// Set the type in the list or dict, so that it can be checked,
		// also in legacy script.
		generate_SETTYPE(cctx, lhs.lhs_type);
	    else if (inferred_type != NULL
		    && (inferred_type->tt_type == VAR_DICT
					|| inferred_type->tt_type == VAR_LIST)
		    && inferred_type->tt_member != NULL
		    && inferred_type->tt_member != &t_unknown
		    && inferred_type->tt_member != &t_any)
		// Set the type in the list or dict, so that it can be checked,
		// also in legacy script.
		generate_SETTYPE(cctx, inferred_type);

	    if (!skip_store && generate_store_lhs(cctx, &lhs,
						 instr_count, is_decl) == FAIL)
	    {
		cctx->ctx_lnum = save_lnum;
		goto theend;
	    }
	}
	cctx->ctx_lnum = save_lnum;

	if (var_idx + 1 < var_count)
	    var_start = skipwhite(lhs.lhs_end + 1);
    }

    // For "[var, var] = expr" drop the "expr" value.
    // Also for "[var, var; _] = expr".
    if (var_count > 0 && (!semicolon || !did_generate_slice))
    {
	if (generate_instr_drop(cctx, ISN_DROP, 1) == NULL)
	    goto theend;
    }

    ret = skipwhite(end);

theend:
    vim_free(lhs.lhs_name);
    return ret;
}


find_script_var(char_u *name, size_t len, cctx_T *cctx, cstack_T *cstack)
{
    scriptitem_T    *si = SCRIPT_ITEM(current_sctx.sc_sid);
    hashitem_T	    *hi;
    int		    cc;
    sallvar_T	    *sav;
    ufunc_T	    *ufunc;

    // Find the list of all script variables with the right name.
    if (len > 0)
    {
	cc = name[len];
	name[len] = NUL;
    }
    hi = hash_find(&si->sn_all_vars.dv_hashtab, name);
    if (len > 0)
	name[len] = cc;
    if (HASHITEM_EMPTY(hi))
	return NULL;

    sav = HI2SAV(hi);
    if (sav->sav_block_id == 0)
	// variable defined in the top script scope is always visible
	return sav;

    if (cctx == NULL)
    {
	// Not in a function scope, find variable with block ID equal to or
	// smaller than the current block id.  Use "cstack" to go up the block
	// scopes.
	while (sav != NULL)
	{
	    int idx;

	    for (idx = cstack->cs_idx; idx >= 0; --idx)
		if (cstack->cs_block_id[idx] == sav->sav_block_id)
		    break;
	    if (idx >= 0)
		break;
	    sav = sav->sav_next;
	}
	return sav;
    }

    // Go over the variables with this name and find one that was visible
    // from the function.
    ufunc = cctx->ctx_ufunc;
    while (sav != NULL)
    {
	int idx;

	// Go over the blocks that this function was defined in.  If the
	// variable block ID matches it was visible to the function.
	for (idx = 0; idx < ufunc->uf_block_depth; ++idx)
	    if (ufunc->uf_block_ids[idx] == sav->sav_block_id)
		return sav;
	sav = sav->sav_next;
    }

    // Not found, variable was not visible.
    return NULL;
}


static struct sk_buff *__pfkey_xfrm_state2msg(const struct xfrm_state *x,
					      int add_keys, int hsc)
{
	struct sk_buff *skb;
	struct sadb_msg *hdr;
	struct sadb_sa *sa;
	struct sadb_lifetime *lifetime;
	struct sadb_address *addr;
	struct sadb_key *key;
	struct sadb_x_sa2 *sa2;
	struct sadb_x_sec_ctx *sec_ctx;
	struct xfrm_sec_ctx *xfrm_ctx;
	int ctx_size = 0;
	int size;
	int auth_key_size = 0;
	int encrypt_key_size = 0;
	int sockaddr_size;
	struct xfrm_encap_tmpl *natt = NULL;
	int mode;

	/* address family check */
	sockaddr_size = pfkey_sockaddr_size(x->props.family);
	if (!sockaddr_size)
		return ERR_PTR(-EINVAL);

	/* base, SA, (lifetime (HSC),) address(SD), (address(P),)
	   key(AE), (identity(SD),) (sensitivity)> */
	size = sizeof(struct sadb_msg) +sizeof(struct sadb_sa) +
		sizeof(struct sadb_lifetime) +
		((hsc & 1) ? sizeof(struct sadb_lifetime) : 0) +
		((hsc & 2) ? sizeof(struct sadb_lifetime) : 0) +
			sizeof(struct sadb_address)*2 +
				sockaddr_size*2 +
					sizeof(struct sadb_x_sa2);

	if ((xfrm_ctx = x->security)) {
		ctx_size = PFKEY_ALIGN8(xfrm_ctx->ctx_len);
		size += sizeof(struct sadb_x_sec_ctx) + ctx_size;
	}

	/* identity & sensitivity */
	if (!xfrm_addr_equal(&x->sel.saddr, &x->props.saddr, x->props.family))
		size += sizeof(struct sadb_address) + sockaddr_size;

	if (add_keys) {
		if (x->aalg && x->aalg->alg_key_len) {
			auth_key_size =
				PFKEY_ALIGN8((x->aalg->alg_key_len + 7) / 8);
			size += sizeof(struct sadb_key) + auth_key_size;
		}
		if (x->ealg && x->ealg->alg_key_len) {
			encrypt_key_size =
				PFKEY_ALIGN8((x->ealg->alg_key_len+7) / 8);
			size += sizeof(struct sadb_key) + encrypt_key_size;
		}
	}
	if (x->encap)
		natt = x->encap;

	if (natt && natt->encap_type) {
		size += sizeof(struct sadb_x_nat_t_type);
		size += sizeof(struct sadb_x_nat_t_port);
		size += sizeof(struct sadb_x_nat_t_port);
	}

	skb =  alloc_skb(size + 16, GFP_ATOMIC);
	if (skb == NULL)
		return ERR_PTR(-ENOBUFS);

	/* call should fill header later */
	hdr = skb_put(skb, sizeof(struct sadb_msg));
	memset(hdr, 0, size);	/* XXX do we need this ? */
	hdr->sadb_msg_len = size / sizeof(uint64_t);

	/* sa */
	sa = skb_put(skb, sizeof(struct sadb_sa));
	sa->sadb_sa_len = sizeof(struct sadb_sa)/sizeof(uint64_t);
	sa->sadb_sa_exttype = SADB_EXT_SA;
	sa->sadb_sa_spi = x->id.spi;
	sa->sadb_sa_replay = x->props.replay_window;
	switch (x->km.state) {
	case XFRM_STATE_VALID:
		sa->sadb_sa_state = x->km.dying ?
			SADB_SASTATE_DYING : SADB_SASTATE_MATURE;
		break;
	case XFRM_STATE_ACQ:
		sa->sadb_sa_state = SADB_SASTATE_LARVAL;
		break;
	default:
		sa->sadb_sa_state = SADB_SASTATE_DEAD;
		break;
	}
	sa->sadb_sa_auth = 0;
	if (x->aalg) {
		struct xfrm_algo_desc *a = xfrm_aalg_get_byname(x->aalg->alg_name, 0);
		sa->sadb_sa_auth = (a && a->pfkey_supported) ?
					a->desc.sadb_alg_id : 0;
	}
	sa->sadb_sa_encrypt = 0;
	BUG_ON(x->ealg && x->calg);
	if (x->ealg) {
		struct xfrm_algo_desc *a = xfrm_ealg_get_byname(x->ealg->alg_name, 0);
		sa->sadb_sa_encrypt = (a && a->pfkey_supported) ?
					a->desc.sadb_alg_id : 0;
	}
	/* KAME compatible: sadb_sa_encrypt is overloaded with calg id */
	if (x->calg) {
		struct xfrm_algo_desc *a = xfrm_calg_get_byname(x->calg->alg_name, 0);
		sa->sadb_sa_encrypt = (a && a->pfkey_supported) ?
					a->desc.sadb_alg_id : 0;
	}

	sa->sadb_sa_flags = 0;
	if (x->props.flags & XFRM_STATE_NOECN)
		sa->sadb_sa_flags |= SADB_SAFLAGS_NOECN;
	if (x->props.flags & XFRM_STATE_DECAP_DSCP)
		sa->sadb_sa_flags |= SADB_SAFLAGS_DECAP_DSCP;
	if (x->props.flags & XFRM_STATE_NOPMTUDISC)
		sa->sadb_sa_flags |= SADB_SAFLAGS_NOPMTUDISC;

	/* hard time */
	if (hsc & 2) {
		lifetime = skb_put(skb, sizeof(struct sadb_lifetime));
		lifetime->sadb_lifetime_len =
			sizeof(struct sadb_lifetime)/sizeof(uint64_t);
		lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD;
		lifetime->sadb_lifetime_allocations =  _X2KEY(x->lft.hard_packet_limit);
		lifetime->sadb_lifetime_bytes = _X2KEY(x->lft.hard_byte_limit);
		lifetime->sadb_lifetime_addtime = x->lft.hard_add_expires_seconds;
		lifetime->sadb_lifetime_usetime = x->lft.hard_use_expires_seconds;
	}
	/* soft time */
	if (hsc & 1) {
		lifetime = skb_put(skb, sizeof(struct sadb_lifetime));
		lifetime->sadb_lifetime_len =
			sizeof(struct sadb_lifetime)/sizeof(uint64_t);
		lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT;
		lifetime->sadb_lifetime_allocations =  _X2KEY(x->lft.soft_packet_limit);
		lifetime->sadb_lifetime_bytes = _X2KEY(x->lft.soft_byte_limit);
		lifetime->sadb_lifetime_addtime = x->lft.soft_add_expires_seconds;
		lifetime->sadb_lifetime_usetime = x->lft.soft_use_expires_seconds;
	}
	/* current time */
	lifetime = skb_put(skb, sizeof(struct sadb_lifetime));
	lifetime->sadb_lifetime_len =
		sizeof(struct sadb_lifetime)/sizeof(uint64_t);
	lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT;
	lifetime->sadb_lifetime_allocations = x->curlft.packets;
	lifetime->sadb_lifetime_bytes = x->curlft.bytes;
	lifetime->sadb_lifetime_addtime = x->curlft.add_time;
	lifetime->sadb_lifetime_usetime = x->curlft.use_time;
	/* src address */
	addr = skb_put(skb, sizeof(struct sadb_address) + sockaddr_size);
	addr->sadb_address_len =
		(sizeof(struct sadb_address)+sockaddr_size)/
			sizeof(uint64_t);
	addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
	/* "if the ports are non-zero, then the sadb_address_proto field,
	   normally zero, MUST be filled in with the transport
	   protocol's number." - RFC2367 */
	addr->sadb_address_proto = 0;
	addr->sadb_address_reserved = 0;

	addr->sadb_address_prefixlen =
		pfkey_sockaddr_fill(&x->props.saddr, 0,
				    (struct sockaddr *) (addr + 1),
				    x->props.family);
	BUG_ON(!addr->sadb_address_prefixlen);

	/* dst address */
	addr = skb_put(skb, sizeof(struct sadb_address) + sockaddr_size);
	addr->sadb_address_len =
		(sizeof(struct sadb_address)+sockaddr_size)/
			sizeof(uint64_t);
	addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
	addr->sadb_address_proto = 0;
	addr->sadb_address_reserved = 0;

	addr->sadb_address_prefixlen =
		pfkey_sockaddr_fill(&x->id.daddr, 0,
				    (struct sockaddr *) (addr + 1),
				    x->props.family);
	BUG_ON(!addr->sadb_address_prefixlen);

	if (!xfrm_addr_equal(&x->sel.saddr, &x->props.saddr,
			     x->props.family)) {
		addr = skb_put(skb,
			       sizeof(struct sadb_address) + sockaddr_size);
		addr->sadb_address_len =
			(sizeof(struct sadb_address)+sockaddr_size)/
			sizeof(uint64_t);
		addr->sadb_address_exttype = SADB_EXT_ADDRESS_PROXY;
		addr->sadb_address_proto =
			pfkey_proto_from_xfrm(x->sel.proto);
		addr->sadb_address_prefixlen = x->sel.prefixlen_s;
		addr->sadb_address_reserved = 0;

		pfkey_sockaddr_fill(&x->sel.saddr, x->sel.sport,
				    (struct sockaddr *) (addr + 1),
				    x->props.family);
	}

	/* auth key */
	if (add_keys && auth_key_size) {
		key = skb_put(skb, sizeof(struct sadb_key) + auth_key_size);
		key->sadb_key_len = (sizeof(struct sadb_key) + auth_key_size) /
			sizeof(uint64_t);
		key->sadb_key_exttype = SADB_EXT_KEY_AUTH;
		key->sadb_key_bits = x->aalg->alg_key_len;
		key->sadb_key_reserved = 0;
		memcpy(key + 1, x->aalg->alg_key, (x->aalg->alg_key_len+7)/8);
	}
	/* encrypt key */
	if (add_keys && encrypt_key_size) {
		key = skb_put(skb, sizeof(struct sadb_key) + encrypt_key_size);
		key->sadb_key_len = (sizeof(struct sadb_key) +
				     encrypt_key_size) / sizeof(uint64_t);
		key->sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
		key->sadb_key_bits = x->ealg->alg_key_len;
		key->sadb_key_reserved = 0;
		memcpy(key + 1, x->ealg->alg_key,
		       (x->ealg->alg_key_len+7)/8);
	}

	/* sa */
	sa2 = skb_put(skb, sizeof(struct sadb_x_sa2));
	sa2->sadb_x_sa2_len = sizeof(struct sadb_x_sa2)/sizeof(uint64_t);
	sa2->sadb_x_sa2_exttype = SADB_X_EXT_SA2;
	if ((mode = pfkey_mode_from_xfrm(x->props.mode)) < 0) {
		kfree_skb(skb);
		return ERR_PTR(-EINVAL);
	}
	sa2->sadb_x_sa2_mode = mode;
	sa2->sadb_x_sa2_reserved1 = 0;
	sa2->sadb_x_sa2_reserved2 = 0;
	sa2->sadb_x_sa2_sequence = 0;
	sa2->sadb_x_sa2_reqid = x->props.reqid;

	if (natt && natt->encap_type) {
		struct sadb_x_nat_t_type *n_type;
		struct sadb_x_nat_t_port *n_port;

		/* type */
		n_type = skb_put(skb, sizeof(*n_type));
		n_type->sadb_x_nat_t_type_len = sizeof(*n_type)/sizeof(uint64_t);
		n_type->sadb_x_nat_t_type_exttype = SADB_X_EXT_NAT_T_TYPE;
		n_type->sadb_x_nat_t_type_type = natt->encap_type;
		n_type->sadb_x_nat_t_type_reserved[0] = 0;
		n_type->sadb_x_nat_t_type_reserved[1] = 0;
		n_type->sadb_x_nat_t_type_reserved[2] = 0;

		/* source port */
		n_port = skb_put(skb, sizeof(*n_port));
		n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
		n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_SPORT;
		n_port->sadb_x_nat_t_port_port = natt->encap_sport;
		n_port->sadb_x_nat_t_port_reserved = 0;

		/* dest port */
		n_port = skb_put(skb, sizeof(*n_port));
		n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
		n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_DPORT;
		n_port->sadb_x_nat_t_port_port = natt->encap_dport;
		n_port->sadb_x_nat_t_port_reserved = 0;
	}

	/* security context */
	if (xfrm_ctx) {
		sec_ctx = skb_put(skb,
				  sizeof(struct sadb_x_sec_ctx) + ctx_size);
		sec_ctx->sadb_x_sec_len =
		  (sizeof(struct sadb_x_sec_ctx) + ctx_size) / sizeof(uint64_t);
		sec_ctx->sadb_x_sec_exttype = SADB_X_EXT_SEC_CTX;
		sec_ctx->sadb_x_ctx_doi = xfrm_ctx->ctx_doi;
		sec_ctx->sadb_x_ctx_alg = xfrm_ctx->ctx_alg;
		sec_ctx->sadb_x_ctx_len = xfrm_ctx->ctx_len;
		memcpy(sec_ctx + 1, xfrm_ctx->ctx_str,
		       xfrm_ctx->ctx_len);
	}

	return skb;
}


static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
{
	struct net *net = sock_net(sk);
	int err;
	struct sadb_address *sa;
	struct sadb_x_policy *pol;
	struct xfrm_policy *xp;
	struct xfrm_selector sel;
	struct km_event c;
	struct sadb_x_sec_ctx *sec_ctx;
	struct xfrm_sec_ctx *pol_ctx = NULL;

	if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
				     ext_hdrs[SADB_EXT_ADDRESS_DST-1]) ||
	    !ext_hdrs[SADB_X_EXT_POLICY-1])
		return -EINVAL;

	pol = ext_hdrs[SADB_X_EXT_POLICY-1];
	if (!pol->sadb_x_policy_dir || pol->sadb_x_policy_dir >= IPSEC_DIR_MAX)
		return -EINVAL;

	memset(&sel, 0, sizeof(sel));

	sa = ext_hdrs[SADB_EXT_ADDRESS_SRC-1];
	sel.family = pfkey_sadb_addr2xfrm_addr(sa, &sel.saddr);
	sel.prefixlen_s = sa->sadb_address_prefixlen;
	sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
	sel.sport = ((struct sockaddr_in *)(sa+1))->sin_port;
	if (sel.sport)
		sel.sport_mask = htons(0xffff);

	sa = ext_hdrs[SADB_EXT_ADDRESS_DST-1];
	pfkey_sadb_addr2xfrm_addr(sa, &sel.daddr);
	sel.prefixlen_d = sa->sadb_address_prefixlen;
	sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
	sel.dport = ((struct sockaddr_in *)(sa+1))->sin_port;
	if (sel.dport)
		sel.dport_mask = htons(0xffff);

	sec_ctx = ext_hdrs[SADB_X_EXT_SEC_CTX - 1];
	if (sec_ctx != NULL) {
		struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx, GFP_KERNEL);

		if (!uctx)
			return -ENOMEM;

		err = security_xfrm_policy_alloc(&pol_ctx, uctx, GFP_KERNEL);
		kfree(uctx);
		if (err)
			return err;
	}

	xp = xfrm_policy_bysel_ctx(net, &dummy_mark, 0, XFRM_POLICY_TYPE_MAIN,
				   pol->sadb_x_policy_dir - 1, &sel, pol_ctx,
				   1, &err);
	security_xfrm_policy_free(pol_ctx);
	if (xp == NULL)
		return -ENOENT;

	xfrm_audit_policy_delete(xp, err ? 0 : 1, true);

	if (err)
		goto out;

	c.seq = hdr->sadb_msg_seq;
	c.portid = hdr->sadb_msg_pid;
	c.data.byid = 0;
	c.event = XFRM_MSG_DELPOLICY;
	km_policy_notify(xp, pol->sadb_x_policy_dir-1, &c);

out:
	xfrm_pol_put(xp);
	return err;
}


static x3f_huffnode_t *new_node(x3f_hufftree_t *tree)
{
	if (tree->free_node_index >= tree->total_node_index)
		throw LIBRAW_EXCEPTION_IO_CORRUPT;
  x3f_huffnode_t *t = &tree->nodes[tree->free_node_index];

  t->branch[0] = NULL;
  t->branch[1] = NULL;
  t->leaf = UNDEFINED_LEAF;

  tree->free_node_index++;

  return t;
}


static void new_huffman_tree(x3f_hufftree_t *HTP, int bits)
{
  int leaves = 1 << bits;

  HTP->free_node_index = 0;
  HTP->total_node_index = HUF_TREE_MAX_NODES(leaves);
  HTP->nodes = (x3f_huffnode_t *)calloc(1, HUF_TREE_MAX_NODES(leaves) *
                                               sizeof(x3f_huffnode_t));
}


int LibRaw::ljpeg_start(struct jhead *jh, int info_only)
{
  ushort c, tag, len;
  int cnt = 0;
  uchar data[0x10000];
  const uchar *dp;

  memset(jh, 0, sizeof *jh);
  jh->restart = INT_MAX;
  if ((fgetc(ifp), fgetc(ifp)) != 0xd8)
    return 0;
  do
  {
    if (feof(ifp))
      return 0;
    if (cnt++ > 1024)
      return 0; // 1024 tags limit
    if (!fread(data, 2, 2, ifp))
      return 0;
    tag = data[0] << 8 | data[1];
    len = (data[2] << 8 | data[3]) - 2;
    if (tag <= 0xff00)
      return 0;
    fread(data, 1, len, ifp);
    switch (tag)
    {
    case 0xffc3: // start of frame; lossless, Huffman
      jh->sraw = ((data[7] >> 4) * (data[7] & 15) - 1) & 3;
    case 0xffc1:
    case 0xffc0:
      jh->algo = tag & 0xff;
      jh->bits = data[0];
      jh->high = data[1] << 8 | data[2];
      jh->wide = data[3] << 8 | data[4];
      jh->clrs = data[5] + jh->sraw;
      if (len == 9 && !dng_version)
        getc(ifp);
      break;
    case 0xffc4: // define Huffman tables
      if (info_only)
        break;
      for (dp = data; dp < data + len && !((c = *dp++) & -20);)
        jh->free[c] = jh->huff[c] = make_decoder_ref(&dp);
      break;
    case 0xffda: // start of scan
      jh->psv = data[1 + data[0] * 2];
      jh->bits -= data[3 + data[0] * 2] & 15;
      break;
    case 0xffdb:
      FORC(64) jh->quant[c] = data[c * 2 + 1] << 8 | data[c * 2 + 2];
      break;
    case 0xffdd:
      jh->restart = data[0] << 8 | data[1];
    }
  } while (tag != 0xffda);
  if (jh->bits > 16 || jh->clrs > 6 || !jh->bits || !jh->high || !jh->wide ||
      !jh->clrs)
    return 0;
  if (info_only)
    return 1;
  if (!jh->huff[0])
    return 0;
  FORC(19) if (!jh->huff[c + 1]) jh->huff[c + 1] = jh->huff[c];
  if (jh->sraw)
  {
    FORC(4) jh->huff[2 + c] = jh->huff[1];
    FORC(jh->sraw) jh->huff[1 + c] = jh->huff[0];
  }
  jh->row = (ushort *)calloc(jh->wide * jh->clrs, 16);
  merror(jh->row, "ljpeg_start()");
  return zero_after_ff = 1;
}


int crxReadImageHeaders(crx_data_header_t *hdr, CrxImage *img, uint8_t *mdatPtr,
                        uint32_t mdatSize)
{
  int nTiles = img->tileRows * img->tileCols;

  if (!nTiles)
    return -1;

  if (!img->tiles)
  {
    img->tiles = (CrxTile *)
#ifdef LIBRAW_CR3_MEMPOOL
		img->memmgr.
#endif
		calloc(
        sizeof(CrxTile) * nTiles +
        sizeof(CrxPlaneComp) * nTiles * img->nPlanes +
        sizeof(CrxSubband) * nTiles * img->nPlanes * img->subbandCount,1);
    if (!img->tiles)
      return -1;

    // memory areas in allocated chunk
    CrxTile *tile = img->tiles;
    CrxPlaneComp *comps = (CrxPlaneComp *)(tile + nTiles);
    CrxSubband *bands = (CrxSubband *)(comps + img->nPlanes * nTiles);

    for (int curTile = 0; curTile < nTiles; curTile++, tile++)
    {
      tile->tileFlag = 0; // tile neighbouring flags
      tile->tileNumber = curTile;
      tile->tileSize = 0;
      tile->comps = comps + curTile * img->nPlanes;

      if ((curTile + 1) % img->tileCols)
      {
        // not the last tile in a tile row
        tile->width = hdr->tileWidth;
        if (img->tileCols > 1)
        {
          tile->tileFlag = E_HAS_TILES_ON_THE_RIGHT;
          if (curTile % img->tileCols)
            // not the first tile in tile row
            tile->tileFlag |= E_HAS_TILES_ON_THE_LEFT;
        }
      }
      else
      {
        // last tile in a tile row
        tile->width = img->planeWidth - hdr->tileWidth * (img->tileCols - 1);
        if (img->tileCols > 1)
          tile->tileFlag = E_HAS_TILES_ON_THE_LEFT;
      }
      if (curTile < nTiles - img->tileCols)
      {
        // in first tile row
        tile->height = hdr->tileHeight;
        if (img->tileRows > 1)
        {
          tile->tileFlag |= E_HAS_TILES_ON_THE_BOTTOM;
          if (curTile >= img->tileCols)
            tile->tileFlag |= E_HAS_TILES_ON_THE_TOP;
        }
      }
      else
      {
        // non first tile row
        tile->height = img->planeHeight - hdr->tileHeight * (img->tileRows - 1);
        if (img->tileRows > 1)
          tile->tileFlag |= E_HAS_TILES_ON_THE_TOP;
      }
      if (img->nPlanes)
      {
        CrxPlaneComp *comp = tile->comps;
        CrxSubband *band = bands + curTile * img->nPlanes * img->subbandCount;

        for (int curComp = 0; curComp < img->nPlanes; curComp++, comp++)
        {
          comp->compNumber = curComp;
          comp->supportsPartial = 1;
          comp->tileFlag = tile->tileFlag;
          comp->subBands = band;
          comp->compBuf = 0;
          comp->waveletTransform = 0;
          if (img->subbandCount)
          {
            for (int curBand = 0; curBand < img->subbandCount;
                 curBand++, band++)
            {
              band->supportsPartial = 0;
              band->quantValue = 4;
              band->bandParam = 0;
              band->dataSize = 0;
            }
          }
        }
      }
    }
  }

  uint32_t tileOffset = 0;
  uint32_t dataSize = mdatSize;
  uint8_t *dataPtr = mdatPtr;
  CrxTile *tile = img->tiles;

  for (int curTile = 0; curTile < nTiles; curTile++, tile++)
  {
    if (dataSize < 0xC)
      return -1;

    if (LibRaw::sgetn(2, dataPtr) != 0xFF01)
      return -1;
    if (LibRaw::sgetn(2, dataPtr + 8) != curTile)
      return -1;

    dataSize -= 0xC;

    tile->tileSize = LibRaw::sgetn(4, dataPtr + 4);
    tile->dataOffset = tileOffset;

    int32_t hdrExtraBytes = LibRaw::sgetn(2, dataPtr + 2) - 8;
    tileOffset += tile->tileSize;
    dataPtr += hdrExtraBytes + 0xC;
    dataSize -= hdrExtraBytes;

    uint32_t compOffset = 0;
    CrxPlaneComp *comp = tile->comps;

    for (int compNum = 0; compNum < img->nPlanes; compNum++, comp++)
    {
      if (dataSize < 0xC)
        return -1;

      if (LibRaw::sgetn(2, dataPtr) != 0xFF02)
        return -1;
      if (compNum != dataPtr[8] >> 4)
        return -1;

      comp->compSize = LibRaw::sgetn(4, dataPtr + 4);

      int32_t compHdrRoundedBits = (dataPtr[8] >> 1) & 3;
      comp->supportsPartial = (dataPtr[8] & 8) != 0;

      comp->dataOffset = compOffset;
      comp->tileFlag = tile->tileFlag;

      compOffset += comp->compSize;
      dataSize -= 0xC;
      dataPtr += 0xC;

      comp->roundedBitsMask = 0;

      if (compHdrRoundedBits)
      {
        if (img->levels || !comp->supportsPartial)
          return -1;

        comp->roundedBitsMask = 1 << (compHdrRoundedBits - 1);
      }

      if (crxReadSubbandHeaders(hdr, img, tile, comp, &dataPtr, &dataSize))
        return -1;
    }
  }
  return 0;
}


void LibRaw::process_Sony_0x9050(uchar *buf, ushort len, unsigned long long id)
{
  ushort lid;
  uchar s[4];
  int c;

  if ((ilm.CameraMount != LIBRAW_MOUNT_Sony_E) &&
      (ilm.CameraMount != LIBRAW_MOUNT_FixedLens))
  {
    if (len < 2)
      return;
    if (buf[0])
      ilm.MaxAp4CurFocal =
        my_roundf(
          libraw_powf64l(2.0f, ((float)SonySubstitution[buf[0]] / 8.0 - 1.06f) / 2.0f) *
             10.0f) / 10.0f;

    if (buf[1])
      ilm.MinAp4CurFocal =
        my_roundf(
          libraw_powf64l(2.0f, ((float)SonySubstitution[buf[1]] / 8.0 - 1.06f) / 2.0f) *
             10.0f) / 10.0f;
  }

  if (ilm.CameraMount != LIBRAW_MOUNT_FixedLens)
  {
    if (len <= 0x106)
      return;
    if (buf[0x3d] | buf[0x3c])
    {
      lid = SonySubstitution[buf[0x3d]] << 8 | SonySubstitution[buf[0x3c]];
      ilm.CurAp = libraw_powf64l(2.0f, ((float)lid / 256.0f - 16.0f) / 2.0f);
    }
    if (buf[0x105] &&
        (ilm.LensMount != LIBRAW_MOUNT_Canon_EF) &&
        (ilm.LensMount != LIBRAW_MOUNT_Sigma_X3F)) {
      switch (SonySubstitution[buf[0x105]]) {
        case 1:
          ilm.LensMount = LIBRAW_MOUNT_Minolta_A;
          break;
        case 2:
          ilm.LensMount = LIBRAW_MOUNT_Sony_E;
        break;
      }
    }
    if (buf[0x106]) {
      switch (SonySubstitution[buf[0x106]]) {
        case 1:
          ilm.LensFormat = LIBRAW_FORMAT_APSC;
          break;
        case 2:
          ilm.LensFormat = LIBRAW_FORMAT_FF;
        break;
      }
    }
  }

  if (ilm.CameraMount == LIBRAW_MOUNT_Sony_E)
  {
    if (len <= 0x108)
      return;
    parseSonyLensType2(
        SonySubstitution[buf[0x0108]], // LensType2 - Sony lens ids
        SonySubstitution[buf[0x0107]]);
  }

  if (len <= 0x10a)
    return;
  if ((ilm.LensID == -1) && (ilm.CameraMount == LIBRAW_MOUNT_Minolta_A) &&
      (buf[0x010a] | buf[0x0109]))
  {
    ilm.LensID = // LensType - Minolta/Sony lens ids
        SonySubstitution[buf[0x010a]] << 8 | SonySubstitution[buf[0x0109]];

    if ((ilm.LensID > 0x4900) && (ilm.LensID <= 0x5900))
    {
      ilm.AdapterID = 0x4900;
      ilm.LensID -= ilm.AdapterID;
      ilm.LensMount = LIBRAW_MOUNT_Sigma_X3F;
      strcpy(ilm.Adapter, "MC-11");
    }

    else if ((ilm.LensID > 0xef00) && (ilm.LensID < 0xffff) &&
             (ilm.LensID != 0xff00))
    {
      ilm.AdapterID = 0xef00;
      ilm.LensID -= ilm.AdapterID;
      ilm.LensMount = LIBRAW_MOUNT_Canon_EF;
    }
  }

  if ((id >= SonyID_SLT_A65) && (id <= SonyID_NEX_F3))
  {
    if (len <= 0x116)
      return;
    // "SLT-A65", "SLT-A77", "NEX-7", "NEX-VG20",
    // "SLT-A37", "SLT-A57", "NEX-F3", "Lunar"
    parseSonyLensFeatures(SonySubstitution[buf[0x115]],
                          SonySubstitution[buf[0x116]]);
  }
  else if (ilm.CameraMount != LIBRAW_MOUNT_FixedLens)
  {
    if (len <= 0x117)
      return;
    parseSonyLensFeatures(SonySubstitution[buf[0x116]],
                          SonySubstitution[buf[0x117]]);
  }

  if ((id == SonyID_ILCE_7RM2) ||
      (id == SonyID_ILCE_7SM2) ||
      (id == SonyID_ILCA_99M2) ||
      (id == SonyID_ILCE_6300) ||
      (id == SonyID_ILCE_9)    ||
      (id == SonyID_ILCE_6500) ||
      (id == SonyID_ILCE_7RM3) ||
      (id == SonyID_ILCE_7M3)  ||
      (id == SonyID_ILCE_6400) ||
      (id == SonyID_ILCE_7RM4) ||
      (id == SonyID_ILCE_9M2)  ||
      (id == SonyID_ILCE_6600) ||
      (id == SonyID_ILCE_6100))
  {
    if (len <= 0x8d)
      return;
    unsigned long long b88 = SonySubstitution[buf[0x88]];
    unsigned long long b89 = SonySubstitution[buf[0x89]];
    unsigned long long b8a = SonySubstitution[buf[0x8a]];
    unsigned long long b8b = SonySubstitution[buf[0x8b]];
    unsigned long long b8c = SonySubstitution[buf[0x8c]];
    unsigned long long b8d = SonySubstitution[buf[0x8d]];
    sprintf(imgdata.shootinginfo.InternalBodySerial, "%06llx",
            (b88 << 40) + (b89 << 32) + (b8a << 24) + (b8b << 16) + (b8c << 8) +
                b8d);
  }
  else if (ilm.CameraMount == LIBRAW_MOUNT_Minolta_A)
  {
    if (len <= 0xf4)
      return;
    unsigned long long bf0 = SonySubstitution[buf[0xf0]];
    unsigned long long bf1 = SonySubstitution[buf[0xf1]];
    unsigned long long bf2 = SonySubstitution[buf[0xf2]];
    unsigned long long bf3 = SonySubstitution[buf[0xf3]];
    unsigned long long bf4 = SonySubstitution[buf[0xf4]];
    sprintf(imgdata.shootinginfo.InternalBodySerial, "%05llx",
            (bf0 << 32) + (bf1 << 24) + (bf2 << 16) + (bf3 << 8) + bf4);
  }
  else if ((ilm.CameraMount == LIBRAW_MOUNT_Sony_E) &&
           (id != SonyID_NEX_5N) &&
           (id != SonyID_NEX_7)  &&
           (id != SonyID_NEX_VG20))
  {
    if (len <= 0x7f)
      return;
    unsigned b7c = SonySubstitution[buf[0x7c]];
    unsigned b7d = SonySubstitution[buf[0x7d]];
    unsigned b7e = SonySubstitution[buf[0x7e]];
    unsigned b7f = SonySubstitution[buf[0x7f]];
    sprintf(imgdata.shootinginfo.InternalBodySerial, "%04x",
            (b7c << 24) + (b7d << 16) + (b7e << 8) + b7f);
  }

  if ((imSony.ImageCount3_offset != 0xffff) &&
      (len >= (imSony.ImageCount3_offset + 4)))
  {
    FORC4 s[c] = SonySubstitution[buf[imSony.ImageCount3_offset + c]];
    imSony.ImageCount3 = sget4(s);
  }

  return;
}


start_pass_merged_upsample(j_decompress_ptr cinfo)
{
  my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample;

  /* Mark the spare buffer empty */
  upsample->spare_full = FALSE;
  /* Initialize total-height counter for detecting bottom of image */
  upsample->rows_to_go = cinfo->output_height;
}


h2v2_merged_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf,
                     JDIMENSION in_row_group_ctr, JSAMPARRAY output_buf)
{
  switch (cinfo->out_color_space) {
  case JCS_EXT_RGB:
    extrgb_h2v2_merged_upsample_internal(cinfo, input_buf, in_row_group_ctr,
                                         output_buf);
    break;
  case JCS_EXT_RGBX:
  case JCS_EXT_RGBA:
    extrgbx_h2v2_merged_upsample_internal(cinfo, input_buf, in_row_group_ctr,
                                          output_buf);
    break;
  case JCS_EXT_BGR:
    extbgr_h2v2_merged_upsample_internal(cinfo, input_buf, in_row_group_ctr,
                                         output_buf);
    break;
  case JCS_EXT_BGRX:
  case JCS_EXT_BGRA:
    extbgrx_h2v2_merged_upsample_internal(cinfo, input_buf, in_row_group_ctr,
                                          output_buf);
    break;
  case JCS_EXT_XBGR:
  case JCS_EXT_ABGR:
    extxbgr_h2v2_merged_upsample_internal(cinfo, input_buf, in_row_group_ctr,
                                          output_buf);
    break;
  case JCS_EXT_XRGB:
  case JCS_EXT_ARGB:
    extxrgb_h2v2_merged_upsample_internal(cinfo, input_buf, in_row_group_ctr,
                                          output_buf);
    break;
  default:
    h2v2_merged_upsample_internal(cinfo, input_buf, in_row_group_ctr,
                                  output_buf);
    break;
  }
}


h2v1_merged_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf,
                     JDIMENSION in_row_group_ctr, JSAMPARRAY output_buf)
{
  switch (cinfo->out_color_space) {
  case JCS_EXT_RGB:
    extrgb_h2v1_merged_upsample_internal(cinfo, input_buf, in_row_group_ctr,
                                         output_buf);
    break;
  case JCS_EXT_RGBX:
  case JCS_EXT_RGBA:
    extrgbx_h2v1_merged_upsample_internal(cinfo, input_buf, in_row_group_ctr,
                                          output_buf);
    break;
  case JCS_EXT_BGR:
    extbgr_h2v1_merged_upsample_internal(cinfo, input_buf, in_row_group_ctr,
                                         output_buf);
    break;
  case JCS_EXT_BGRX:
  case JCS_EXT_BGRA:
    extbgrx_h2v1_merged_upsample_internal(cinfo, input_buf, in_row_group_ctr,
                                          output_buf);
    break;
  case JCS_EXT_XBGR:
  case JCS_EXT_ABGR:
    extxbgr_h2v1_merged_upsample_internal(cinfo, input_buf, in_row_group_ctr,
                                          output_buf);
    break;
  case JCS_EXT_XRGB:
  case JCS_EXT_ARGB:
    extxrgb_h2v1_merged_upsample_internal(cinfo, input_buf, in_row_group_ctr,
                                          output_buf);
    break;
  default:
    h2v1_merged_upsample_internal(cinfo, input_buf, in_row_group_ctr,
                                  output_buf);
    break;
  }
}


h2v1_merged_upsample_565_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf,
                                  JDIMENSION in_row_group_ctr,
                                  JSAMPARRAY output_buf)
{
  my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample;
  register int y, cred, cgreen, cblue;
  int cb, cr;
  register JSAMPROW outptr;
  JSAMPROW inptr0, inptr1, inptr2;
  JDIMENSION col;
  /* copy these pointers into registers if possible */
  register JSAMPLE *range_limit = cinfo->sample_range_limit;
  int *Crrtab = upsample->Cr_r_tab;
  int *Cbbtab = upsample->Cb_b_tab;
  JLONG *Crgtab = upsample->Cr_g_tab;
  JLONG *Cbgtab = upsample->Cb_g_tab;
  unsigned int r, g, b;
  JLONG rgb;
  SHIFT_TEMPS

  inptr0 = input_buf[0][in_row_group_ctr];
  inptr1 = input_buf[1][in_row_group_ctr];
  inptr2 = input_buf[2][in_row_group_ctr];
  outptr = output_buf[0];

  /* Loop for each pair of output pixels */
  for (col = cinfo->output_width >> 1; col > 0; col--) {
    /* Do the chroma part of the calculation */
    cb = GETJSAMPLE(*inptr1++);
    cr = GETJSAMPLE(*inptr2++);
    cred = Crrtab[cr];
    cgreen = (int)RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS);
    cblue = Cbbtab[cb];

    /* Fetch 2 Y values and emit 2 pixels */
    y  = GETJSAMPLE(*inptr0++);
    r = range_limit[y + cred];
    g = range_limit[y + cgreen];
    b = range_limit[y + cblue];
    rgb = PACK_SHORT_565(r, g, b);

    y  = GETJSAMPLE(*inptr0++);
    r = range_limit[y + cred];
    g = range_limit[y + cgreen];
    b = range_limit[y + cblue];
    rgb = PACK_TWO_PIXELS(rgb, PACK_SHORT_565(r, g, b));

    WRITE_TWO_PIXELS(outptr, rgb);
    outptr += 4;
  }

  /* If image width is odd, do the last output column separately */
  if (cinfo->output_width & 1) {
    cb = GETJSAMPLE(*inptr1);
    cr = GETJSAMPLE(*inptr2);
    cred = Crrtab[cr];
    cgreen = (int)RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS);
    cblue = Cbbtab[cb];
    y  = GETJSAMPLE(*inptr0);
    r = range_limit[y + cred];
    g = range_limit[y + cgreen];
    b = range_limit[y + cblue];
    rgb = PACK_SHORT_565(r, g, b);
    *(INT16 *)outptr = (INT16)rgb;
  }
}


static void i740fb_ddc_setsda(void *data, int val)
{
	struct i740fb_par *par = data;

	i740outreg_mask(par, XRX, REG_DDC_DRIVE, DDC_SDA, DDC_SDA);
	i740outreg_mask(par, XRX, REG_DDC_STATE, val ? DDC_SDA : 0, DDC_SDA);
}


static int i740fb_decode_var(const struct fb_var_screeninfo *var,
			     struct i740fb_par *par, struct fb_info *info)
{
	/*
	 * Get the video params out of 'var'.
	 * If a value doesn't fit, round it up, if it's too big, return -EINVAL.
	 */

	u32 xres, right, hslen, left, xtotal;
	u32 yres, lower, vslen, upper, ytotal;
	u32 vxres, xoffset, vyres, yoffset;
	u32 bpp, base, dacspeed24, mem;
	u8 r7;
	int i;

	dev_dbg(info->device, "decode_var: xres: %i, yres: %i, xres_v: %i, xres_v: %i\n",
		  var->xres, var->yres, var->xres_virtual, var->xres_virtual);
	dev_dbg(info->device, "	xoff: %i, yoff: %i, bpp: %i, graysc: %i\n",
		  var->xoffset, var->yoffset, var->bits_per_pixel,
		  var->grayscale);
	dev_dbg(info->device, "	activate: %i, nonstd: %i, vmode: %i\n",
		  var->activate, var->nonstd, var->vmode);
	dev_dbg(info->device, "	pixclock: %i, hsynclen:%i, vsynclen:%i\n",
		  var->pixclock, var->hsync_len, var->vsync_len);
	dev_dbg(info->device, "	left: %i, right: %i, up:%i, lower:%i\n",
		  var->left_margin, var->right_margin, var->upper_margin,
		  var->lower_margin);

	bpp = var->bits_per_pixel;
	switch (bpp) {
	case 1 ... 8:
		bpp = 8;
		if ((1000000 / var->pixclock) > DACSPEED8) {
			dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 8bpp)\n",
				1000000 / var->pixclock, DACSPEED8);
			return -EINVAL;
		}
		break;
	case 9 ... 15:
		bpp = 15;
		fallthrough;
	case 16:
		if ((1000000 / var->pixclock) > DACSPEED16) {
			dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 15/16bpp)\n",
				1000000 / var->pixclock, DACSPEED16);
			return -EINVAL;
		}
		break;
	case 17 ... 24:
		bpp = 24;
		dacspeed24 = par->has_sgram ? DACSPEED24_SG : DACSPEED24_SD;
		if ((1000000 / var->pixclock) > dacspeed24) {
			dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 24bpp)\n",
				1000000 / var->pixclock, dacspeed24);
			return -EINVAL;
		}
		break;
	case 25 ... 32:
		bpp = 32;
		if ((1000000 / var->pixclock) > DACSPEED32) {
			dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 32bpp)\n",
				1000000 / var->pixclock, DACSPEED32);
			return -EINVAL;
		}
		break;
	default:
		return -EINVAL;
	}

	xres = ALIGN(var->xres, 8);
	vxres = ALIGN(var->xres_virtual, 16);
	if (vxres < xres)
		vxres = xres;

	xoffset = ALIGN(var->xoffset, 8);
	if (xres + xoffset > vxres)
		xoffset = vxres - xres;

	left = ALIGN(var->left_margin, 8);
	right = ALIGN(var->right_margin, 8);
	hslen = ALIGN(var->hsync_len, 8);

	yres = var->yres;
	vyres = var->yres_virtual;
	if (yres > vyres)
		vyres = yres;

	yoffset = var->yoffset;
	if (yres + yoffset > vyres)
		yoffset = vyres - yres;

	lower = var->lower_margin;
	vslen = var->vsync_len;
	upper = var->upper_margin;

	mem = vxres * vyres * ((bpp + 1) / 8);
	if (mem > info->screen_size) {
		dev_err(info->device, "not enough video memory (%d KB requested, %ld KB available)\n",
			mem >> 10, info->screen_size >> 10);
		return -ENOMEM;
	}

	if (yoffset + yres > vyres)
		yoffset = vyres - yres;

	xtotal = xres + right + hslen + left;
	ytotal = yres + lower + vslen + upper;

	par->crtc[VGA_CRTC_H_TOTAL] = (xtotal >> 3) - 5;
	par->crtc[VGA_CRTC_H_DISP] = (xres >> 3) - 1;
	par->crtc[VGA_CRTC_H_BLANK_START] = ((xres + right) >> 3) - 1;
	par->crtc[VGA_CRTC_H_SYNC_START] = (xres + right) >> 3;
	par->crtc[VGA_CRTC_H_SYNC_END] = (((xres + right + hslen) >> 3) & 0x1F)
		| ((((xres + right + hslen) >> 3) & 0x20) << 2);
	par->crtc[VGA_CRTC_H_BLANK_END] = ((xres + right + hslen) >> 3 & 0x1F)
		| 0x80;

	par->crtc[VGA_CRTC_V_TOTAL] = ytotal - 2;

	r7 = 0x10;	/* disable linecompare */
	if (ytotal & 0x100)
		r7 |= 0x01;
	if (ytotal & 0x200)
		r7 |= 0x20;

	par->crtc[VGA_CRTC_PRESET_ROW] = 0;
	par->crtc[VGA_CRTC_MAX_SCAN] = 0x40;	/* 1 scanline, no linecmp */
	if (var->vmode & FB_VMODE_DOUBLE)
		par->crtc[VGA_CRTC_MAX_SCAN] |= 0x80;
	par->crtc[VGA_CRTC_CURSOR_START] = 0x00;
	par->crtc[VGA_CRTC_CURSOR_END] = 0x00;
	par->crtc[VGA_CRTC_CURSOR_HI] = 0x00;
	par->crtc[VGA_CRTC_CURSOR_LO] = 0x00;
	par->crtc[VGA_CRTC_V_DISP_END] = yres-1;
	if ((yres-1) & 0x100)
		r7 |= 0x02;
	if ((yres-1) & 0x200)
		r7 |= 0x40;

	par->crtc[VGA_CRTC_V_BLANK_START] = yres + lower - 1;
	par->crtc[VGA_CRTC_V_SYNC_START] = yres + lower - 1;
	if ((yres + lower - 1) & 0x100)
		r7 |= 0x0C;
	if ((yres + lower - 1) & 0x200) {
		par->crtc[VGA_CRTC_MAX_SCAN] |= 0x20;
		r7 |= 0x80;
	}

	/* disabled IRQ */
	par->crtc[VGA_CRTC_V_SYNC_END] =
		((yres + lower - 1 + vslen) & 0x0F) & ~0x10;
	/* 0x7F for VGA, but some SVGA chips require all 8 bits to be set */
	par->crtc[VGA_CRTC_V_BLANK_END] = (yres + lower - 1 + vslen) & 0xFF;

	par->crtc[VGA_CRTC_UNDERLINE] = 0x00;
	par->crtc[VGA_CRTC_MODE] = 0xC3 ;
	par->crtc[VGA_CRTC_LINE_COMPARE] = 0xFF;
	par->crtc[VGA_CRTC_OVERFLOW] = r7;

	par->vss = 0x00;	/* 3DA */

	for (i = 0x00; i < 0x10; i++)
		par->atc[i] = i;
	par->atc[VGA_ATC_MODE] = 0x81;
	par->atc[VGA_ATC_OVERSCAN] = 0x00;	/* 0 for EGA, 0xFF for VGA */
	par->atc[VGA_ATC_PLANE_ENABLE] = 0x0F;
	par->atc[VGA_ATC_COLOR_PAGE] = 0x00;

	par->misc = 0xC3;
	if (var->sync & FB_SYNC_HOR_HIGH_ACT)
		par->misc &= ~0x40;
	if (var->sync & FB_SYNC_VERT_HIGH_ACT)
		par->misc &= ~0x80;

	par->seq[VGA_SEQ_CLOCK_MODE] = 0x01;
	par->seq[VGA_SEQ_PLANE_WRITE] = 0x0F;
	par->seq[VGA_SEQ_CHARACTER_MAP] = 0x00;
	par->seq[VGA_SEQ_MEMORY_MODE] = 0x06;

	par->gdc[VGA_GFX_SR_VALUE] = 0x00;
	par->gdc[VGA_GFX_SR_ENABLE] = 0x00;
	par->gdc[VGA_GFX_COMPARE_VALUE] = 0x00;
	par->gdc[VGA_GFX_DATA_ROTATE] = 0x00;
	par->gdc[VGA_GFX_PLANE_READ] = 0;
	par->gdc[VGA_GFX_MODE] = 0x02;
	par->gdc[VGA_GFX_MISC] = 0x05;
	par->gdc[VGA_GFX_COMPARE_MASK] = 0x0F;
	par->gdc[VGA_GFX_BIT_MASK] = 0xFF;

	base = (yoffset * vxres + (xoffset & ~7)) >> 2;
	switch (bpp) {
	case 8:
		par->crtc[VGA_CRTC_OFFSET] = vxres >> 3;
		par->ext_offset = vxres >> 11;
		par->pixelpipe_cfg1 = DISPLAY_8BPP_MODE;
		par->bitblt_cntl = COLEXP_8BPP;
		break;
	case 15: /* 0rrrrrgg gggbbbbb */
	case 16: /* rrrrrggg gggbbbbb */
		par->pixelpipe_cfg1 = (var->green.length == 6) ?
			DISPLAY_16BPP_MODE : DISPLAY_15BPP_MODE;
		par->crtc[VGA_CRTC_OFFSET] = vxres >> 2;
		par->ext_offset = vxres >> 10;
		par->bitblt_cntl = COLEXP_16BPP;
		base *= 2;
		break;
	case 24:
		par->crtc[VGA_CRTC_OFFSET] = (vxres * 3) >> 3;
		par->ext_offset = (vxres * 3) >> 11;
		par->pixelpipe_cfg1 = DISPLAY_24BPP_MODE;
		par->bitblt_cntl = COLEXP_24BPP;
		base &= 0xFFFFFFFE; /* ...ignore the last bit. */
		base *= 3;
		break;
	case 32:
		par->crtc[VGA_CRTC_OFFSET] = vxres >> 1;
		par->ext_offset = vxres >> 9;
		par->pixelpipe_cfg1 = DISPLAY_32BPP_MODE;
		par->bitblt_cntl = COLEXP_RESERVED; /* Unimplemented on i740 */
		base *= 4;
		break;
	}

	par->crtc[VGA_CRTC_START_LO] = base & 0x000000FF;
	par->crtc[VGA_CRTC_START_HI] = (base & 0x0000FF00) >>  8;
	par->ext_start_addr =
		((base & 0x003F0000) >> 16) | EXT_START_ADDR_ENABLE;
	par->ext_start_addr_hi = (base & 0x3FC00000) >> 22;

	par->pixelpipe_cfg0 = DAC_8_BIT;

	par->pixelpipe_cfg2 = DISPLAY_GAMMA_ENABLE | OVERLAY_GAMMA_ENABLE;
	par->io_cntl = EXTENDED_CRTC_CNTL;
	par->address_mapping = LINEAR_MODE_ENABLE | PAGE_MAPPING_ENABLE;
	par->display_cntl = HIRES_MODE;

	/* Set the MCLK freq */
	par->pll_cntl = PLL_MEMCLK_100000KHZ; /* 100 MHz -- use as default */

	/* Calculate the extended CRTC regs */
	par->ext_vert_total = (ytotal - 2) >> 8;
	par->ext_vert_disp_end = (yres - 1) >> 8;
	par->ext_vert_sync_start = (yres + lower) >> 8;
	par->ext_vert_blank_start = (yres + lower) >> 8;
	par->ext_horiz_total = ((xtotal >> 3) - 5) >> 8;
	par->ext_horiz_blank = (((xres + right) >> 3) & 0x40) >> 6;

	par->interlace_cntl = INTERLACE_DISABLE;

	/* Set the overscan color to 0. (NOTE: This only affects >8bpp mode) */
	par->atc[VGA_ATC_OVERSCAN] = 0;

	/* Calculate VCLK that most closely matches the requested dot clock */
	i740_calc_vclk((((u32)1e9) / var->pixclock) * (u32)(1e3), par);

	/* Since we program the clocks ourselves, always use VCLK2. */
	par->misc |= 0x0C;

	/* Calculate the FIFO Watermark and Burst Length. */
	par->lmi_fifo_watermark =
		i740_calc_fifo(par, 1000000 / var->pixclock, bpp);

	return 0;
}


static int i740fb_pan_display(struct fb_var_screeninfo *var,
				 struct fb_info *info)
{
	struct i740fb_par *par = info->par;
	u32 base = (var->yoffset * info->var.xres_virtual
		 + (var->xoffset & ~7)) >> 2;

	dev_dbg(info->device, "pan_display: xoffset: %i yoffset: %i base: %i\n",
		var->xoffset, var->yoffset, base);

	switch (info->var.bits_per_pixel) {
	case 8:
		break;
	case 15:
	case 16:
		base *= 2;
		break;
	case 24:
		/*
		 * The last bit does not seem to have any effect on the start
		 * address register in 24bpp mode, so...
		 */
		base &= 0xFFFFFFFE; /* ...ignore the last bit. */
		base *= 3;
		break;
	case 32:
		base *= 4;
		break;
	}

	par->crtc[VGA_CRTC_START_LO] = base & 0x000000FF;
	par->crtc[VGA_CRTC_START_HI] = (base & 0x0000FF00) >>  8;
	par->ext_start_addr_hi = (base & 0x3FC00000) >> 22;
	par->ext_start_addr =
			((base & 0x003F0000) >> 16) | EXT_START_ADDR_ENABLE;

	i740outreg(par, VGA_CRT_IC, VGA_CRTC_START_LO,  base & 0x000000FF);
	i740outreg(par, VGA_CRT_IC, VGA_CRTC_START_HI,
			(base & 0x0000FF00) >> 8);
	i740outreg(par, VGA_CRT_IC, EXT_START_ADDR_HI,
			(base & 0x3FC00000) >> 22);
	i740outreg(par, VGA_CRT_IC, EXT_START_ADDR,
			((base & 0x003F0000) >> 16) | EXT_START_ADDR_ENABLE);

	return 0;
}


static int i740fb_blank(int blank_mode, struct fb_info *info)
{
	struct i740fb_par *par = info->par;

	unsigned char SEQ01;
	int DPMSSyncSelect;

	switch (blank_mode) {
	case FB_BLANK_UNBLANK:
	case FB_BLANK_NORMAL:
		SEQ01 = 0x00;
		DPMSSyncSelect = HSYNC_ON | VSYNC_ON;
		break;
	case FB_BLANK_VSYNC_SUSPEND:
		SEQ01 = 0x20;
		DPMSSyncSelect = HSYNC_ON | VSYNC_OFF;
		break;
	case FB_BLANK_HSYNC_SUSPEND:
		SEQ01 = 0x20;
		DPMSSyncSelect = HSYNC_OFF | VSYNC_ON;
		break;
	case FB_BLANK_POWERDOWN:
		SEQ01 = 0x20;
		DPMSSyncSelect = HSYNC_OFF | VSYNC_OFF;
		break;
	default:
		return -EINVAL;
	}
	/* Turn the screen on/off */
	i740outb(par, SRX, 0x01);
	SEQ01 |= i740inb(par, SRX + 1) & ~0x20;
	i740outb(par, SRX, 0x01);
	i740outb(par, SRX + 1, SEQ01);

	/* Set the DPMS mode */
	i740outreg(par, XRX, DPMS_SYNC_SELECT, DPMSSyncSelect);

	/* Let fbcon do a soft blank for us */
	return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
}


setup_key_sizes(void) {
    memset(&keySizes, 0, sizeof(struct key_sizes));

    keySizes.sym_sig_keyLen = DEFAULT_SYM_SIGNING_KEY_LENGTH;
    keySizes.sym_enc_blockSize = DEFAULT_SYM_ENCRYPTION_BLOCK_SIZE;
    keySizes.sym_enc_keyLen = DEFAULT_SYM_ENCRYPTION_KEY_LENGTH;
    keySizes.sym_sig_size = DEFAULT_SYM_SIGNATURE_SIZE;

    keySizes.asym_lcl_sig_size = DEFAULT_ASYM_LOCAL_SIGNATURE_SIZE;
    keySizes.asym_rmt_sig_size = DEFAULT_ASYM_REMOTE_SIGNATURE_SIZE;

    keySizes.asym_rmt_ptext_blocksize = DEFAULT_ASYM_REMOTE_PLAINTEXT_BLOCKSIZE;
    keySizes.asym_rmt_blocksize = DEFAULT_ASYM_REMOTE_BLOCKSIZE;
    keySizes.asym_rmt_enc_key_size = 2048;
    keySizes.asym_lcl_enc_key_size = 1024;
}


setup_secureChannel(void) {
    TestingPolicy(&dummyPolicy, dummyCertificate, &fCalled, &keySizes);
    UA_SecureChannel_init(&testChannel, &UA_ConnectionConfig_default);
    UA_SecureChannel_setSecurityPolicy(&testChannel, &dummyPolicy, &dummyCertificate);

    testingConnection =
        createDummyConnection(UA_ConnectionConfig_default.sendBufferSize, &sentData);
    UA_Connection_attachSecureChannel(&testingConnection, &testChannel);
    testChannel.connection = &testingConnection;

    testChannel.state = UA_SECURECHANNELSTATE_OPEN;
}


START_TEST(SecureChannel_sendAsymmetricOPNMessage_SecurityModeNone) {
    // Configure our channel correctly for OPN messages and setup dummy message
    UA_OpenSecureChannelResponse dummyResponse;
    createDummyResponse(&dummyResponse);
    testChannel.securityMode = UA_MESSAGESECURITYMODE_NONE;

    UA_StatusCode retval =
        UA_SecureChannel_sendAsymmetricOPNMessage(&testChannel, 42, &dummyResponse,
                                                  &UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE]);
    ck_assert_msg(retval == UA_STATUSCODE_GOOD, "Expected function to succeed");
    ck_assert_msg(!fCalled.asym_enc, "Message encryption was called but should not have been");
    ck_assert_msg(!fCalled.asym_sign, "Message signing was called but should not have been");
}


START_TEST(SecureChannel_sendAsymmetricOPNMessage_SecurityModeInvalid) {
    // Configure our channel correctly for OPN messages and setup dummy message
    UA_OpenSecureChannelResponse dummyResponse;
    createDummyResponse(&dummyResponse);

    testChannel.securityMode = UA_MESSAGESECURITYMODE_INVALID;

    UA_StatusCode retval =
        UA_SecureChannel_sendAsymmetricOPNMessage(&testChannel, 42, &dummyResponse,
                                                  &UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE]);
    ck_assert_msg(retval == UA_STATUSCODE_BADSECURITYMODEREJECTED,
                  "Expected SecurityMode rejected error");
}


START_TEST(Securechannel_sendAsymmetricOPNMessage_extraPaddingPresentWhenKeyLargerThan2048Bits) {
    keySizes.asym_rmt_enc_key_size = 4096;
    keySizes.asym_rmt_blocksize = 4096;
    keySizes.asym_rmt_ptext_blocksize = 4096;

    UA_OpenSecureChannelResponse dummyResponse;
    createDummyResponse(&dummyResponse);

    testChannel.securityMode = UA_MESSAGESECURITYMODE_SIGNANDENCRYPT;
    UA_UInt32 requestId = UA_UInt32_random();

    UA_StatusCode retval =
        UA_SecureChannel_sendAsymmetricOPNMessage(&testChannel, requestId, &dummyResponse,
                                                  &UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE]);
    ck_assert_msg(retval == UA_STATUSCODE_GOOD, "Expected function to succeed");

    size_t offset = 0;
    UA_TcpMessageHeader header;
    UA_TcpMessageHeader_decodeBinary(&sentData, &offset, &header);
    UA_UInt32 secureChannelId;
    UA_UInt32_decodeBinary(&sentData, &offset, &secureChannelId);

    UA_AsymmetricAlgorithmSecurityHeader asymSecurityHeader;
    UA_AsymmetricAlgorithmSecurityHeader_decodeBinary(&sentData, &offset, &asymSecurityHeader);
    ck_assert_msg(UA_ByteString_equal(&dummyCertificate, &asymSecurityHeader.senderCertificate),
                  "Expected the certificate to be equal to the one used  by the secureChannel");
    ck_assert_msg(UA_ByteString_equal(&testChannel.securityPolicy->policyUri,
                                      &asymSecurityHeader.securityPolicyUri),
                  "Expected securityPolicyUri to be equal to the one used by the secureChannel");
    UA_ByteString thumbPrint = {20, testChannel.remoteCertificateThumbprint};
    ck_assert_msg(UA_ByteString_equal(&thumbPrint,
                                      &asymSecurityHeader.receiverCertificateThumbprint),
                  "Expected receiverCertificateThumbprint to be equal to the one set "
                  "in the secureChannel");

    for(size_t i = offset; i < header.messageSize; ++i) {
        sentData.data[i] = (UA_Byte)((sentData.data[i] - 1) % (UA_BYTE_MAX + 1));
    }

    UA_SequenceHeader sequenceHeader;
    UA_SequenceHeader_decodeBinary(&sentData, &offset, &sequenceHeader);
    ck_assert_msg(sequenceHeader.requestId == requestId, "Expected requestId to be %i but was %i",
                  requestId, sequenceHeader.requestId);

    UA_NodeId requestTypeId;
    UA_NodeId_decodeBinary(&sentData, &offset, &requestTypeId);
    ck_assert_msg(UA_NodeId_equal(&UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE].binaryEncodingId, &requestTypeId), "Expected nodeIds to be equal");

    UA_OpenSecureChannelResponse sentResponse;
    UA_OpenSecureChannelResponse_decodeBinary(&sentData, &offset, &sentResponse);

    ck_assert_msg(memcmp(&sentResponse, &dummyResponse, sizeof(UA_OpenSecureChannelResponse)) == 0,
                  "Expected the sent response to be equal to the one supplied to the send function");

    UA_Byte paddingByte = sentData.data[offset];
    UA_Byte extraPaddingByte = sentData.data[sentData.length - keySizes.asym_lcl_sig_size - 1];
    size_t paddingSize = (size_t)paddingByte;
    paddingSize |= extraPaddingByte << 8;

    for(size_t i = 0; i <= paddingSize; ++i) {
        ck_assert_msg(sentData.data[offset + i] == paddingByte,
                      "Expected padding byte %i to be %i but got value %i",
                      (int)i, paddingByte, sentData.data[offset + i]);
    }

    ck_assert_msg(sentData.data[offset + paddingSize + 1] == extraPaddingByte,
                  "Expected extra padding byte to be %i but got %i",
                  extraPaddingByte, sentData.data[offset + paddingSize + 1]);
    ck_assert_msg(sentData.data[offset + paddingSize + 2] == '*',
                  "Expected first byte 42 of signature but got %i",
                  sentData.data[offset + paddingSize + 2]);

    UA_AsymmetricAlgorithmSecurityHeader_clear(&asymSecurityHeader);
    UA_SequenceHeader_clear(&sequenceHeader);
    UA_OpenSecureChannelResponse_clear(&sentResponse);
}END_TEST


setup_funcs_called(void) {
    memset(&fCalled, 0, sizeof(struct funcs_called));
}


qf_get_cur_idx(exarg_T *eap)
{
    qf_info_T	*qi;

    if ((qi = qf_cmd_get_stack(eap, FALSE)) == NULL)
	return 0;

    return qf_get_curlist(qi)->qf_index;
}


vgr_display_fname(char_u *fname)
{
    char_u	*p;

    msg_start();
    p = msg_strtrunc(fname, TRUE);
    if (p == NULL)
	msg_outtrans(fname);
    else
    {
	msg_outtrans(p);
	vim_free(p);
    }
    msg_clr_eos();
    msg_didout = FALSE;	    // overwrite this message
    msg_nowait = TRUE;	    // don't wait for this message
    msg_col = 0;
    out_flush();
}


ex_copen(exarg_T *eap)
{
    qf_info_T	*qi;
    qf_list_T	*qfl;
    int		height;
    int		status = FAIL;
    int		lnum;

    if ((qi = qf_cmd_get_stack(eap, TRUE)) == NULL)
	return;

    incr_quickfix_busy();

    if (eap->addr_count != 0)
	height = eap->line2;
    else
	height = QF_WINHEIGHT;

    reset_VIsual_and_resel();			// stop Visual mode
#ifdef FEAT_GUI
    need_mouse_correct = TRUE;
#endif

    // Find an existing quickfix window, or open a new one.
    if (cmdmod.cmod_tab == 0)
	status = qf_goto_cwindow(qi, eap->addr_count != 0, height,
						cmdmod.cmod_split & WSP_VERT);
    if (status == FAIL)
	if (qf_open_new_cwindow(qi, height) == FAIL)
	{
	    decr_quickfix_busy();
	    return;
	}

    qfl = qf_get_curlist(qi);
    qf_set_title_var(qfl);
    // Save the current index here, as updating the quickfix buffer may free
    // the quickfix list
    lnum = qfl->qf_index;

    // Fill the buffer with the quickfix list.
    qf_fill_buffer(qfl, curbuf, NULL, curwin->w_id);

    decr_quickfix_busy();

    curwin->w_cursor.lnum = lnum;
    curwin->w_cursor.col = 0;
    check_cursor();
    update_topline();		// scroll to show the line
}


win_set_loclist(win_T *wp, qf_info_T *qi)
{
    wp->w_llist = qi;
    qi->qf_refcount++;
}


call_qftf_func(qf_list_T *qfl, int qf_winid, long start_idx, long end_idx)
{
    callback_T	*cb = &qftf_cb;
    list_T	*qftf_list = NULL;
    static int	recursive = FALSE;

    if (recursive)
	return NULL;  // this doesn't work properly recursively
    recursive = TRUE;

    // If 'quickfixtextfunc' is set, then use the user-supplied function to get
    // the text to display. Use the local value of 'quickfixtextfunc' if it is
    // set.
    if (qfl->qf_qftf_cb.cb_name != NULL)
	cb = &qfl->qf_qftf_cb;
    if (cb->cb_name != NULL)
    {
	typval_T	args[1];
	dict_T		*d;
	typval_T	rettv;

	// create the dict argument
	if ((d = dict_alloc_lock(VAR_FIXED)) == NULL)
	{
	    recursive = FALSE;
	    return NULL;
	}
	dict_add_number(d, "quickfix", (long)IS_QF_LIST(qfl));
	dict_add_number(d, "winid", (long)qf_winid);
	dict_add_number(d, "id", (long)qfl->qf_id);
	dict_add_number(d, "start_idx", start_idx);
	dict_add_number(d, "end_idx", end_idx);
	++d->dv_refcount;
	args[0].v_type = VAR_DICT;
	args[0].vval.v_dict = d;

	qftf_list = NULL;
	if (call_callback(cb, 0, &rettv, 1, args) != FAIL)
	{
	    if (rettv.v_type == VAR_LIST)
	    {
		qftf_list = rettv.vval.v_list;
		qftf_list->lv_refcount++;
	    }
	    clear_tv(&rettv);
	}
	dict_unref(d);
    }

    recursive = FALSE;
    return qftf_list;
}


copy_loclist_stack(win_T *from, win_T *to)
{
    qf_info_T	*qi;
    int		idx;

    // When copying from a location list window, copy the referenced
    // location list. For other windows, copy the location list for
    // that window.
    if (IS_LL_WINDOW(from))
	qi = from->w_llist_ref;
    else
	qi = from->w_llist;

    if (qi == NULL)		    // no location list to copy
	return;

    // allocate a new location list
    if ((to->w_llist = qf_alloc_stack(QFLT_LOCATION)) == NULL)
	return;

    to->w_llist->qf_listcount = qi->qf_listcount;

    // Copy the location lists one at a time
    for (idx = 0; idx < qi->qf_listcount; ++idx)
    {
	to->w_llist->qf_curlist = idx;

	if (copy_loclist(qf_get_list(qi, idx),
			qf_get_list(to->w_llist, idx)) == FAIL)
	{
	    qf_free_all(to);
	    return;
	}
    }

    to->w_llist->qf_curlist = qi->qf_curlist;	// current list
}


qf_id2nr(qf_info_T *qi, int_u qfid)
{
    int		qf_idx;

    for (qf_idx = 0; qf_idx < qi->qf_listcount; qf_idx++)
	if (qi->qf_lists[qf_idx].qf_id == qfid)
	    return qf_idx;
    return INVALID_QFIDX;
}


parse_efm_option(char_u *efm)
{
    efm_T	*fmt_ptr = NULL;
    efm_T	*fmt_first = NULL;
    efm_T	*fmt_last = NULL;
    char_u	*fmtstr = NULL;
    int		len;
    int		sz;

    // Each part of the format string is copied and modified from errorformat
    // to regex prog.  Only a few % characters are allowed.

    // Get some space to modify the format string into.
    sz = efm_regpat_bufsz(efm);
    if ((fmtstr = alloc_id(sz, aid_qf_efm_fmtstr)) == NULL)
	goto parse_efm_error;

    while (efm[0] != NUL)
    {
	// Allocate a new eformat structure and put it at the end of the list
	fmt_ptr = ALLOC_CLEAR_ONE_ID(efm_T, aid_qf_efm_fmtpart);
	if (fmt_ptr == NULL)
	    goto parse_efm_error;
	if (fmt_first == NULL)	    // first one
	    fmt_first = fmt_ptr;
	else
	    fmt_last->next = fmt_ptr;
	fmt_last = fmt_ptr;

	// Isolate one part in the 'errorformat' option
	len = efm_option_part_len(efm);

	if (efm_to_regpat(efm, len, fmt_ptr, fmtstr) == FAIL)
	    goto parse_efm_error;
	if ((fmt_ptr->prog = vim_regcomp(fmtstr, RE_MAGIC + RE_STRING)) == NULL)
	    goto parse_efm_error;
	// Advance to next part
	efm = skip_to_option_part(efm + len);	// skip comma and spaces
    }

    if (fmt_first == NULL)	// nothing found
	emsg(_(e_errorformat_contains_no_pattern));

    goto parse_efm_end;

parse_efm_error:
    free_efm_list(&fmt_first);

parse_efm_end:
    vim_free(fmtstr);

    return fmt_first;
}


qf_history(exarg_T *eap)
{
    qf_info_T	*qi = qf_cmd_get_stack(eap, FALSE);
    int		i;

    if (eap->addr_count > 0)
    {
	if (qi == NULL)
	{
	    emsg(_(e_no_location_list));
	    return;
	}

	// Jump to the specified quickfix list
	if (eap->line2 > 0 && eap->line2 <= qi->qf_listcount)
	{
	    qi->qf_curlist = eap->line2 - 1;
	    qf_msg(qi, qi->qf_curlist, "");
	    qf_update_buffer(qi, NULL);
	}
	else
	    emsg(_(e_invalid_range));

	return;
    }

    if (qf_stack_empty(qi))
	msg(_("No entries"));
    else
	for (i = 0; i < qi->qf_listcount; ++i)
	    qf_msg(qi, i, i == qi->qf_curlist ? "> " : "  ");
}


qf_getprop_defaults(qf_info_T *qi, int flags, int locstack, dict_T *retdict)
{
    int		status = OK;

    if (flags & QF_GETLIST_TITLE)
	status = dict_add_string(retdict, "title", (char_u *)"");
    if ((status == OK) && (flags & QF_GETLIST_ITEMS))
    {
	list_T	*l = list_alloc();
	if (l != NULL)
	    status = dict_add_list(retdict, "items", l);
	else
	    status = FAIL;
    }
    if ((status == OK) && (flags & QF_GETLIST_NR))
	status = dict_add_number(retdict, "nr", 0);
    if ((status == OK) && (flags & QF_GETLIST_WINID))
	status = dict_add_number(retdict, "winid", qf_winid(qi));
    if ((status == OK) && (flags & QF_GETLIST_CONTEXT))
	status = dict_add_string(retdict, "context", (char_u *)"");
    if ((status == OK) && (flags & QF_GETLIST_ID))
	status = dict_add_number(retdict, "id", 0);
    if ((status == OK) && (flags & QF_GETLIST_IDX))
	status = dict_add_number(retdict, "idx", 0);
    if ((status == OK) && (flags & QF_GETLIST_SIZE))
	status = dict_add_number(retdict, "size", 0);
    if ((status == OK) && (flags & QF_GETLIST_TICK))
	status = dict_add_number(retdict, "changedtick", 0);
    if ((status == OK) && locstack && (flags & QF_GETLIST_FILEWINID))
	status = dict_add_number(retdict, "filewinid", 0);
    if ((status == OK) && (flags & QF_GETLIST_QFBUFNR))
	status = qf_getprop_qfbufnr(qi, retdict);
    if ((status == OK) && (flags & QF_GETLIST_QFTF))
	status = dict_add_string(retdict, "quickfixtextfunc", (char_u *)"");

    return status;
}


incr_quickfix_busy(void)
{
    quickfix_busy++;
}


decr_quickfix_busy(void)
{
    if (--quickfix_busy == 0)
    {
	// No longer referencing the location lists. Process all the pending
	// delete requests.
	while (qf_delq_head != NULL)
	{
	    qf_delq_T	*q = qf_delq_head;

	    qf_delq_head = q->next;
	    ll_free_all(&q->qi);
	    vim_free(q);
	}
    }
#ifdef ABORT_ON_INTERNAL_ERROR
    if (quickfix_busy < 0)
    {
	emsg("quickfix_busy has become negative");
	abort();
    }
#endif
}


qf_jump_edit_buffer(
	qf_info_T	*qi,
	qfline_T	*qf_ptr,
	int		forceit,
	int		prev_winid,
	int		*opened_window)
{
    qf_list_T	*qfl = qf_get_curlist(qi);
    int		old_changedtick = qfl->qf_changedtick;
    qfltype_T	qfl_type = qfl->qfl_type;
    int		retval = OK;
    int		old_qf_curlist = qi->qf_curlist;
    int		save_qfid = qfl->qf_id;

    if (qf_ptr->qf_type == 1)
    {
	// Open help file (do_ecmd() will set b_help flag, readfile() will
	// set b_p_ro flag).
	if (!can_abandon(curbuf, forceit))
	{
	    no_write_message();
	    return FAIL;
	}

	retval = do_ecmd(qf_ptr->qf_fnum, NULL, NULL, NULL, (linenr_T)1,
		ECMD_HIDE + ECMD_SET_HELP,
		prev_winid == curwin->w_id ? curwin : NULL);
    }
    else
	retval = buflist_getfile(qf_ptr->qf_fnum,
		(linenr_T)1, GETF_SETMARK | GETF_SWITCH, forceit);

    // If a location list, check whether the associated window is still
    // present.
    if (qfl_type == QFLT_LOCATION)
    {
	win_T	*wp = win_id2wp(prev_winid);

	if (wp == NULL && curwin->w_llist != qi)
	{
	    emsg(_(e_current_window_was_closed));
	    *opened_window = FALSE;
	    return QF_ABORT;
	}
    }

    if (qfl_type == QFLT_QUICKFIX && !qflist_valid(NULL, save_qfid))
    {
	emsg(_(e_current_quickfix_list_was_changed));
	return QF_ABORT;
    }

    // Check if the list was changed.  The pointers may happen to be identical,
    // thus also check qf_changedtick.
    if (old_qf_curlist != qi->qf_curlist
	    || old_changedtick != qfl->qf_changedtick
	    || !is_qf_entry_present(qfl, qf_ptr))
    {
	if (qfl_type == QFLT_QUICKFIX)
	    emsg(_(e_current_quickfix_list_was_changed));
	else
	    emsg(_(e_current_location_list_was_changed));
	return QF_ABORT;
    }

    return retval;
}


qf_fmt_text(char_u *text, char_u *buf, int bufsize)
{
    int		i;
    char_u	*p = text;

    for (i = 0; *p != NUL && i < bufsize - 1; ++i)
    {
	if (*p == '\n')
	{
	    buf[i] = ' ';
	    while (*++p != NUL)
		if (!VIM_ISWHITE(*p) && *p != '\n')
		    break;
	}
	else
	    buf[i] = *p++;
    }
    buf[i] = NUL;
}


qf_getprop_ctx(qf_list_T *qfl, dict_T *retdict)
{
    int		status;
    dictitem_T	*di;

    if (qfl->qf_ctx != NULL)
    {
	di = dictitem_alloc((char_u *)"context");
	if (di != NULL)
	{
	    copy_tv(qfl->qf_ctx, &di->di_tv);
	    status = dict_add(retdict, di);
	    if (status == FAIL)
		dictitem_free(di);
	}
	else
	    status = FAIL;
    }
    else
	status = dict_add_string(retdict, "context", (char_u *)"");

    return status;
}


f_getqflist(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
{
# ifdef FEAT_QUICKFIX
    if (in_vim9script() && check_for_opt_dict_arg(argvars, 0) == FAIL)
	return;

    get_qf_loc_list(TRUE, NULL, &argvars[0], rettv);
# endif
}


qf_msg(qf_info_T *qi, int which, char *lead)
{
    char   *title = (char *)qi->qf_lists[which].qf_title;
    int    count = qi->qf_lists[which].qf_count;
    char_u buf[IOSIZE];

    vim_snprintf((char *)buf, IOSIZE, _("%serror list %d of %d; %d errors "),
	    lead,
	    which + 1,
	    qi->qf_listcount,
	    count);

    if (title != NULL)
    {
	size_t	len = STRLEN(buf);

	if (len < 34)
	{
	    vim_memset(buf + len, ' ', 34 - len);
	    buf[34] = NUL;
	}
	vim_strcat(buf, (char_u *)title, IOSIZE);
    }
    trunc_string(buf, buf, Columns - 1, IOSIZE);
    msg((char *)buf);
}


static u32 vidtv_s302m_write_h(struct vidtv_encoder *e, u32 p_sz)
{
	struct vidtv_smpte_s302m_es h = {};
	u32 nbytes = 0;

	/* 2 channels, ident: 0, 16 bits per sample */
	h.bitfield = cpu_to_be32((p_sz << 16));

	nbytes += vidtv_memcpy(e->encoder_buf,
			       e->encoder_buf_offset,
			       e->encoder_buf_sz,
			       &h,
			       sizeof(h));

	e->encoder_buf_offset += nbytes;
	return nbytes;
}


static struct vidtv_access_unit *vidtv_s302m_access_unit_init(struct vidtv_access_unit *head)
{
	struct vidtv_access_unit *au;

	au = kzalloc(sizeof(*au), GFP_KERNEL);
	if (!au)
		return NULL;

	if (head) {
		while (head->next)
			head = head->next;

		head->next = au;
	}

	return au;
}


void vidtv_s302m_encoder_destroy(struct vidtv_encoder *e)
{
	if (e->id != S302M) {
		pr_err_ratelimited("Encoder type mismatch, skipping.\n");
		return;
	}

	vidtv_s302m_access_unit_destroy(e);
	kfree(e->name);
	vfree(e->encoder_buf);
	kfree(e->ctx);
	kfree(e);
}


mlx5_check_vec_rx_support(struct rte_eth_dev *dev __rte_unused)
{
	return -ENOTSUP;
}


mlx5_rx_burst_mprq_vec(void *dpdk_rxq __rte_unused,
		       struct rte_mbuf **pkts __rte_unused,
		       uint16_t pkts_n __rte_unused)
{
	return 0;
}


mlx5_rxq_lwm_to_percentage(struct mlx5_rxq_priv *rxq)
{
	struct mlx5_rxq_data *rxq_data = &rxq->ctrl->rxq;
	uint32_t wqe_cnt = 1 << (rxq_data->elts_n - rxq_data->sges_n);

	return rxq->lwm * 100 / wqe_cnt;
}


int mlx5_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
{
	struct mlx5_rxq_data *rxq = rx_queue;
	const unsigned int cqe_num = 1 << rxq->cqe_n;
	const unsigned int cqe_mask = cqe_num - 1;
	const uint16_t idx = rxq->cq_ci & cqe_num;
	volatile struct mlx5_cqe *cqe = &(*rxq->cqes)[rxq->cq_ci & cqe_mask];

	if (unlikely(rxq->cqes == NULL)) {
		rte_errno = EINVAL;
		return -rte_errno;
	}
	pmc->addr = &cqe->op_own;
	pmc->opaque[CLB_VAL_IDX] = !!idx;
	pmc->opaque[CLB_MSK_IDX] = MLX5_CQE_OWNER_MASK;
	pmc->fn = mlx5_monitor_callback;
	pmc->size = sizeof(uint8_t);
	return 0;
}


mlx5_rxq_info_get(struct rte_eth_dev *dev, uint16_t rx_queue_id,
		  struct rte_eth_rxq_info *qinfo)
{
	struct mlx5_rxq_ctrl *rxq_ctrl = mlx5_rxq_ctrl_get(dev, rx_queue_id);
	struct mlx5_rxq_data *rxq = mlx5_rxq_data_get(dev, rx_queue_id);
	struct mlx5_rxq_priv *rxq_priv = mlx5_rxq_get(dev, rx_queue_id);

	if (!rxq)
		return;
	qinfo->mp = mlx5_rxq_mprq_enabled(rxq) ?
					rxq->mprq_mp : rxq->mp;
	qinfo->conf.rx_thresh.pthresh = 0;
	qinfo->conf.rx_thresh.hthresh = 0;
	qinfo->conf.rx_thresh.wthresh = 0;
	qinfo->conf.rx_free_thresh = rxq->rq_repl_thresh;
	qinfo->conf.rx_drop_en = 1;
	if (rxq_ctrl == NULL || rxq_ctrl->obj == NULL)
		qinfo->conf.rx_deferred_start = 0;
	else
		qinfo->conf.rx_deferred_start = 1;
	qinfo->conf.offloads = dev->data->dev_conf.rxmode.offloads;
	qinfo->scattered_rx = dev->data->scattered_rx;
	qinfo->nb_desc = mlx5_rxq_mprq_enabled(rxq) ?
		RTE_BIT32(rxq->elts_n) * RTE_BIT32(rxq->log_strd_num) :
		RTE_BIT32(rxq->elts_n);
	qinfo->avail_thresh = rxq_priv ?
		mlx5_rxq_lwm_to_percentage(rxq_priv) : 0;
}


mlx5_rx_burst_mode_get(struct rte_eth_dev *dev,
		       uint16_t rx_queue_id __rte_unused,
		       struct rte_eth_burst_mode *mode)
{
	eth_rx_burst_t pkt_burst = dev->rx_pkt_burst;
	struct mlx5_priv *priv = dev->data->dev_private;
	struct mlx5_rxq_data *rxq;

	rxq = (*priv->rxqs)[rx_queue_id];
	if (!rxq) {
		rte_errno = EINVAL;
		return -rte_errno;
	}
	if (pkt_burst == mlx5_rx_burst) {
		snprintf(mode->info, sizeof(mode->info), "%s", "Scalar");
	} else if (pkt_burst == mlx5_rx_burst_mprq) {
		snprintf(mode->info, sizeof(mode->info), "%s", "Multi-Packet RQ");
	} else if (pkt_burst == mlx5_rx_burst_vec) {
#if defined RTE_ARCH_X86_64
		snprintf(mode->info, sizeof(mode->info), "%s", "Vector SSE");
#elif defined RTE_ARCH_ARM64
		snprintf(mode->info, sizeof(mode->info), "%s", "Vector Neon");
#elif defined RTE_ARCH_PPC_64
		snprintf(mode->info, sizeof(mode->info), "%s", "Vector AltiVec");
#else
		return -EINVAL;
#endif
	} else if (pkt_burst == mlx5_rx_burst_mprq_vec) {
#if defined RTE_ARCH_X86_64
		snprintf(mode->info, sizeof(mode->info), "%s", "MPRQ Vector SSE");
#elif defined RTE_ARCH_ARM64
		snprintf(mode->info, sizeof(mode->info), "%s", "MPRQ Vector Neon");
#elif defined RTE_ARCH_PPC_64
		snprintf(mode->info, sizeof(mode->info), "%s", "MPRQ Vector AltiVec");
#else
		return -EINVAL;
#endif
	} else {
		return -EINVAL;
	}
	return 0;
}


mlx5_tx_comp_flush(struct mlx5_txq_data *__rte_restrict txq,
		   volatile struct mlx5_cqe *last_cqe,
		   unsigned int olx __rte_unused)
{
	if (likely(last_cqe != NULL)) {
		uint16_t tail;

		txq->wqe_pi = rte_be_to_cpu_16(last_cqe->wqe_counter);
		tail = txq->fcqs[(txq->cq_ci - 1) & txq->cqe_m];
		if (likely(tail != txq->elts_tail)) {
			mlx5_tx_free_elts(txq, tail, olx);
			MLX5_ASSERT(tail == txq->elts_tail);
		}
	}
}