in sdk/linux_kernel_drivers/xdma/cdev_xvc.c [98:230]
static long xvc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct xdma_cdev *xcdev = (struct xdma_cdev *)filp->private_data;
struct xdma_dev *xdev;
struct xvc_ioc xvc_obj;
unsigned int opcode;
unsigned int total_bits;
unsigned int total_bytes;
unsigned char *buffer = NULL;
unsigned char *tms_buf = NULL;
unsigned char *tdi_buf = NULL;
unsigned char *tdo_buf = NULL;
unsigned int bits, bits_left;
void __iomem *iobase;
int rv;
rv = xcdev_check(__func__, xcdev, 0);
if (rv < 0)
return rv;
xdev = xcdev->xdev;
if (cmd != XDMA_IOCXVC) {
pr_info("ioctl 0x%x, UNKNOWN cmd.\n", cmd);
return -ENOIOCTLCMD;
}
rv = copy_from_user((void *)&xvc_obj, (void __user *)arg,
sizeof(struct xvc_ioc));
/* anything not copied ? */
if (rv) {
pr_info("copy_from_user xvc_obj failed: %d.\n", rv);
goto cleanup;
}
opcode = xvc_obj.opcode;
/* Invalid operation type, no operation performed */
if (opcode != 0x01 && opcode != 0x02) {
pr_info("UNKNOWN opcode 0x%x.\n", opcode);
return -EINVAL;
}
total_bits = xvc_obj.length;
total_bytes = (total_bits + 7) >> 3;
buffer = kmalloc(total_bytes * 3, GFP_KERNEL);
if (!buffer) {
pr_info("OOM %u, op 0x%x, len %u bits, %u bytes.\n",
3 * total_bytes, opcode, total_bits, total_bytes);
rv = -ENOMEM;
goto cleanup;
}
tms_buf = buffer;
tdi_buf = tms_buf + total_bytes;
tdo_buf = tdi_buf + total_bytes;
rv = copy_from_user((void *)tms_buf,
(const char __user *)xvc_obj.tms_buf,
total_bytes);
if (rv) {
pr_info("copy tmfs_buf failed: %d/%u.\n", rv, total_bytes);
goto cleanup;
}
rv = copy_from_user((void *)tdi_buf,
(const char __user *)xvc_obj.tdi_buf,
total_bytes);
if (rv) {
pr_info("copy tdi_buf failed: %d/%u.\n", rv, total_bytes);
goto cleanup;
}
/* exclusive access */
spin_lock(&xcdev->lock);
iobase = xdev->bar[xcdev->bar] + xcdev->base;
/* set length register to 32 initially if more than one
* word-transaction is to be done
*/
if (total_bits >= 32)
write_register(0x20, iobase, XVC_BAR_LENGTH_REG);
for (bits = 0, bits_left = total_bits; bits < total_bits; bits += 32,
bits_left -= 32) {
unsigned int bytes = bits >> 3;
unsigned int shift_bytes = 4;
u32 tms_store = 0;
u32 tdi_store = 0;
u32 tdo_store = 0;
if (bits_left < 32) {
/* set number of bits to shift out */
write_register(bits_left, iobase, XVC_BAR_LENGTH_REG);
shift_bytes = (bits_left + 7) >> 3;
}
memcpy(&tms_store, tms_buf + bytes, shift_bytes);
memcpy(&tdi_store, tdi_buf + bytes, shift_bytes);
/* Shift data out and copy to output buffer */
rv = xvc_shift_bits(iobase, tms_store, tdi_store, &tdo_store);
if (rv < 0)
break;
memcpy(tdo_buf + bytes, &tdo_store, shift_bytes);
}
if (rv < 0)
goto unlock;
/* if testing bar access swap tdi and tdo bufferes to "loopback" */
if (opcode == 0x2) {
unsigned char *tmp = tdo_buf;
tdo_buf = tdi_buf;
tdi_buf = tmp;
}
rv = copy_to_user(xvc_obj.tdo_buf, (const void *)tdo_buf, total_bytes);
if (rv)
pr_info("copy back tdo_buf failed: %d/%u.\n", rv, total_bytes);
unlock:
#if KERNEL_VERSION(5, 1, 0) >= LINUX_VERSION_CODE
wmb();
#endif
spin_unlock(&xcdev->lock);
cleanup:
kfree(buffer);
return rv;
}