long bladerf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { bladerf_device_t *dev; void __user *data; struct bladeRF_version ver; int ret; int retval = -EINVAL; int sz, nread, nwrite; struct uart_cmd spi_reg; int sectors_to_wipe, sector_idx; int pages_to_write, page_idx; int pages_to_read; int check_idx; int count, tries; int targetdev; /* FIXME this large buffer should be kmalloc'd and kept with the dev, no? */ unsigned char buf[1024]; struct bladeRF_firmware brf_fw; struct bladeRF_sector brf_sector; unsigned char *fw_buf; dev = file->private_data; data = (void __user *)arg; switch (cmd) { case BLADE_QUERY_VERSION: retval = __bladerf_rcv_cmd(dev, BLADE_USB_CMD_QUERY_VERSION, &ver, sizeof(ver)); if (retval >= 0) { ver.major = le16_to_cpu(ver.major); ver.minor = le16_to_cpu(ver.minor); if (copy_to_user(data, &ver, sizeof(struct bladeRF_version))) { retval = -EFAULT; } else { retval = 0; } } break; case BLADE_QUERY_FPGA_STATUS: retval = __bladerf_rcv_one_word(dev, BLADE_USB_CMD_QUERY_FPGA_STATUS, data); break; case BLADE_BEGIN_PROG: if (dev->intnum != 0) { ret = usb_set_interface(dev->udev, 0,0); dev->intnum = 0; } retval = __bladerf_rcv_one_word(dev, BLADE_USB_CMD_BEGIN_PROG, data); break; case BLADE_END_PROG: // TODO: send another 2 DCLK cycles to ensure compliance with C4's boot procedure retval = __bladerf_rcv_one_word(dev, BLADE_USB_CMD_QUERY_FPGA_STATUS, data); if (!retval) { ret = usb_set_interface(dev->udev, 0,1); dev->intnum = 1; } break; case BLADE_CAL: if (dev->intnum != 2) { retval = usb_set_interface(dev->udev, 0,2); if (retval) break; dev->intnum = 2; } if (copy_from_user(&brf_fw, data, sizeof(struct bladeRF_firmware))) { return -EFAULT; } fw_buf = kzalloc(256, GFP_KERNEL); if (!fw_buf) return -EINVAL; memset(fw_buf, 0xff, 256); if (copy_from_user(fw_buf, brf_fw.ptr, brf_fw.len)) { retval = -EFAULT; break; } retval = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), BLADE_USB_CMD_FLASH_ERASE, BLADE_USB_TYPE_IN, 0x0000, 3, &ret, 4, BLADE_USB_TIMEOUT_MS * 100); if (!retval) { dev_err(&dev->interface->dev, "Could not erase NAND cal sector 3.\n"); break; } retval = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), BLADE_USB_CMD_FLASH_WRITE, BLADE_USB_TYPE_OUT, 0x0000, 768, fw_buf, 256, BLADE_USB_TIMEOUT_MS); if (!retval) { dev_err(&dev->interface->dev, "Could not write NAND cal sector 768.\n"); break; } memset(buf, 0, 256); retval = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), BLADE_USB_CMD_FLASH_READ, BLADE_USB_TYPE_IN, 0x0000, 768, buf, 256, BLADE_USB_TIMEOUT_MS); if (!retval) { dev_err(&dev->interface->dev, "Could not read NAND cal sector 768.\n"); break; } retval = memcmp(fw_buf, buf, 256); break; case BLADE_FLASH_ERASE: retval = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), BLADE_USB_CMD_FLASH_ERASE, BLADE_USB_TYPE_IN, 0x0000, arg, &ret, 4, BLADE_USB_TIMEOUT_MS * 100); if (!retval) { dev_err(&dev->interface->dev, "Could not read NAND cal sector 768.\n"); break; } retval = !ret; break; case BLADE_OTP: if (dev->intnum != 2) { retval = usb_set_interface(dev->udev, 0,2); if (retval) break; dev->intnum = 2; } if (copy_from_user(&brf_fw, data, sizeof(struct bladeRF_firmware))) { return -EFAULT; } fw_buf = kzalloc(256, GFP_KERNEL); if (!fw_buf) return -EINVAL; memset(fw_buf, 0xff, 256); if (copy_from_user(fw_buf, brf_fw.ptr, brf_fw.len)) { retval = -EFAULT; kfree(fw_buf); break; } memcpy(buf, fw_buf, brf_fw.len); retval = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), BLADE_USB_CMD_WRITE_OTP, BLADE_USB_TYPE_OUT, 0x0000, 0, fw_buf, 256, BLADE_USB_TIMEOUT_MS); if (!retval) { dev_err(&dev->interface->dev, "Could not write OTP.\n"); break; } memset(buf, 0, 256); retval = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), BLADE_USB_CMD_READ_OTP, BLADE_USB_TYPE_IN, 0x0000, 0, buf, 256, BLADE_USB_TIMEOUT_MS); if (!retval) { dev_err(&dev->interface->dev, "Could not read OTP.\n"); break; } retval = memcmp(fw_buf, buf, 256); break; case BLADE_OTP_READ: case BLADE_FLASH_READ: case BLADE_FLASH_WRITE: if (dev->intnum != 2) { retval = usb_set_interface(dev->udev, 0,2); if (retval) break; dev->intnum = 2; } if (copy_from_user(&brf_sector, data, sizeof(struct bladeRF_sector))) { return -EFAULT; } if (cmd == BLADE_OTP_READ) { if (brf_sector.idx != 0 || brf_sector.len != 0x100) dev_err(&dev->interface->dev, "Invalid OTP settings, expecting idx=0, len=256\n"); } sz = 0; if (dev->udev->speed == USB_SPEED_HIGH) { sz = 64; } else if (dev->udev->speed == USB_SPEED_SUPER) { sz = 256; } count = brf_sector.len + (sz - (brf_sector.len % sz)); fw_buf = kzalloc(count, GFP_KERNEL); if (!fw_buf) return -EFAULT; memset(fw_buf, 0xff, count); if (cmd == BLADE_FLASH_READ || cmd == BLADE_OTP_READ) { pages_to_read = (brf_sector.len + 255) / 0x100; nread = 0; for (page_idx = 0; page_idx < pages_to_read; page_idx++) { do { retval = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), (cmd == BLADE_FLASH_READ) ? BLADE_USB_CMD_FLASH_READ : BLADE_USB_CMD_READ_OTP, BLADE_USB_TYPE_IN, 0x0000, brf_sector.idx + page_idx, &fw_buf[nread], sz, BLADE_USB_TIMEOUT_MS); printk("%d read %d bytes %x %x %x %x\n", retval, sz, fw_buf[nread], fw_buf[nread+1], fw_buf[nread+2], fw_buf[nread+3]); nread += sz; if (retval != sz) break; } while (nread != 256); if (retval != sz) break; } if (!retval) { dev_err(&dev->interface->dev, "Could not read NAND cal page idx %d.\n", page_idx); break; } if (copy_to_user((void __user *)brf_sector.ptr, fw_buf, brf_sector.len)) { retval = -EFAULT; break; } } else if (cmd == BLADE_FLASH_WRITE) { if (copy_from_user(fw_buf, brf_sector.ptr, brf_sector.len)) { retval = -EFAULT; break; } pages_to_write = (brf_sector.len + 255) / 0x100; nwrite = 0; for (page_idx = 0; page_idx < pages_to_write; page_idx++) { do { retval = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), BLADE_USB_CMD_FLASH_WRITE, BLADE_USB_TYPE_OUT, 0x0000, brf_sector.idx + page_idx, &fw_buf[page_idx * 256 + nwrite], sz, BLADE_USB_TIMEOUT_MS); nwrite += sz; if (retval != sz) break; } while (nwrite != 256); if (retval != sz) break; } if (!retval) { dev_err(&dev->interface->dev, "Could not write NAND cal page idx %d.\n", page_idx); break; } } break; case BLADE_UPGRADE_FW: if (dev->intnum != 2) { retval = usb_set_interface(dev->udev, 0,2); if (retval) break; dev->intnum = 2; } if (copy_from_user(&brf_fw, data, sizeof(struct bladeRF_firmware))) { return -EFAULT; } brf_fw.len = ((brf_fw.len + 255) / 256) * 256; fw_buf = kzalloc(brf_fw.len, GFP_KERNEL); if (!fw_buf) goto leave_fw; if (copy_from_user(fw_buf, brf_fw.ptr, brf_fw.len)) { retval = -EFAULT; goto leave_fw; } retval = -ENODEV; sectors_to_wipe = (brf_fw.len + 0xffff) / 0x10000; printk("Going to wipe %d sectors\n", sectors_to_wipe); for (sector_idx = 0; sector_idx < sectors_to_wipe; sector_idx++) { printk("Erasing sector %d... ", sector_idx); retval = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), BLADE_USB_CMD_FLASH_ERASE, BLADE_USB_TYPE_IN, 0x0000, sector_idx, &ret, 4, BLADE_USB_TIMEOUT_MS * 100); printk("- erased\n"); if (retval != 4) { goto leave_fw; } ret = le32_to_cpu(ret); if (ret != 1) { printk("Unable to erase previous sector, quitting\n"); goto leave_fw; } } sz = 0; if (dev->udev->speed == USB_SPEED_HIGH) { sz = 64; } else if (dev->udev->speed == USB_SPEED_SUPER) { sz = 256; } pages_to_write = (brf_fw.len + 255) / 0x100; for (page_idx = pages_to_write - 1; page_idx >= 0; page_idx--) { nwrite = 0; do { retval = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), BLADE_USB_CMD_FLASH_WRITE, BLADE_USB_TYPE_OUT, 0x0000, page_idx, &fw_buf[page_idx * 256 + nwrite], sz, BLADE_USB_TIMEOUT_MS); nwrite += sz; } while (nwrite != 256); } pages_to_read = (brf_fw.len + 255) / 0x100; for (page_idx = 0; page_idx < pages_to_read; page_idx++) { nread = 0; do { retval = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), BLADE_USB_CMD_FLASH_READ, BLADE_USB_TYPE_IN, 0x0000, page_idx, &buf[nread], sz, BLADE_USB_TIMEOUT_MS); nread += sz; } while (nread != 256); for (check_idx = 0; check_idx < 256; check_idx++) { if (buf[check_idx] != fw_buf[page_idx * 256 + check_idx]) { printk("ERROR: bladeRF firmware verification detected a mismatch at byte offset 0x%.8x\n", page_idx * 256 + check_idx); printk("ERROR: expected byte 0x%.2X, got 0x%.2X\n", fw_buf[page_idx * 256 + check_idx], buf[check_idx]); retval = -EINVAL; goto leave_fw; } } } retval = 0; printk("SUCCESSFULLY VERIFIED\n"); leave_fw: kfree(fw_buf); break; case BLADE_DEVICE_RESET: ret = 1; retval = __bladerf_snd_cmd(dev, BLADE_USB_CMD_RESET, &ret, sizeof(ret)); break; case BLADE_CHECK_PROG: retval = 0; printk("ok %d\n", dev->intnum); if (dev->intnum == 0) { retval = __bladerf_rcv_cmd(dev, BLADE_USB_CMD_QUERY_FPGA_STATUS, &ret, sizeof(ret)); printk("retval =%d ret=%d\n", retval, ret); if (retval >= 0 && ret) { retval = 0; ret = usb_set_interface(dev->udev, 0,1); dev->intnum = 1; if (copy_to_user((void __user *)arg, &ret, sizeof(ret))){ retval = -EFAULT; } else { retval = 0; } } } break; case BLADE_RF_RX: if (dev->intnum != 1) { dev_err(&dev->interface->dev, "Cannot enable RX from config mode\n"); retval = -1; break; } printk("RF_RX!\n"); retval = __bladerf_snd_one_word(dev, BLADE_USB_CMD_RF_RX, data); break; case BLADE_RF_TX: if (dev->intnum != 1) { dev_err(&dev->interface->dev, "Cannot enable TX from config mode\n"); retval = -1; break; } printk("RF_TX!\n"); retval = __bladerf_snd_one_word(dev, BLADE_USB_CMD_RF_TX, data); break; case BLADE_LMS_WRITE: case BLADE_LMS_READ: case BLADE_SI5338_WRITE: case BLADE_SI5338_READ: case BLADE_GPIO_WRITE: case BLADE_GPIO_READ: case BLADE_VCTCXO_WRITE: if (copy_from_user(&spi_reg, (void __user *)arg, sizeof(struct uart_cmd))) { retval = -EFAULT; break; } nread = count = 16; memset(buf, 0, 20); buf[0] = 'N'; targetdev = UART_PKT_DEV_SI5338; if (cmd == BLADE_GPIO_WRITE || cmd == BLADE_GPIO_READ) targetdev = UART_PKT_DEV_GPIO; if (cmd == BLADE_LMS_WRITE || cmd == BLADE_LMS_READ) targetdev = UART_PKT_DEV_LMS; if (cmd == BLADE_VCTCXO_WRITE) targetdev = UART_PKT_DEV_VCTCXO; if (cmd == BLADE_LMS_WRITE || cmd == BLADE_GPIO_WRITE || cmd == BLADE_SI5338_WRITE || cmd == BLADE_VCTCXO_WRITE) { buf[1] = UART_PKT_MODE_DIR_WRITE | targetdev | 0x01; buf[2] = spi_reg.addr; buf[3] = spi_reg.data; } else if (cmd == BLADE_LMS_READ || cmd == BLADE_GPIO_READ || cmd == BLADE_SI5338_READ) { buf[1] = UART_PKT_MODE_DIR_READ | targetdev | 0x01; buf[2] = spi_reg.addr; buf[3] = 0xff; } retval = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2), buf, count, &nread, BLADE_USB_TIMEOUT_MS); if (!retval) { memset(buf, 0, 20); tries = 3; do { retval = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, 0x82), buf, count, &nread, BLADE_USB_TIMEOUT_MS); } while(retval == -ETIMEDOUT && tries--); if (!retval) { spi_reg.addr = buf[2]; spi_reg.data = buf[3]; } if (copy_to_user((void __user *)arg, &spi_reg, sizeof(struct uart_cmd))) { retval = -EFAULT; } else { retval = 0; } } break; case BLADE_GET_SPEED: ret = dev->udev->speed == USB_SPEED_SUPER; if (copy_to_user((void __user *)arg, &ret, sizeof(ret))) { retval = -EFAULT; } else { retval = 0; } break; case BLADE_GET_ADDR: ret = dev->udev->devnum; if (copy_to_user((void __user *)arg, &ret, sizeof(ret))) { retval = -EFAULT; } else { retval = 0; } break; case BLADE_GET_BUS: ret = dev->udev->bus->busnum; if (copy_to_user((void __user *)arg, &ret, sizeof(ret))) { retval = -EFAULT; } else { retval = 0; } break; } return retval; }
long bladerf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { bladerf_device_t *dev; void __user *data; struct bladeRF_version ver; int ret; int retval = -EINVAL; int sz, nread, nwrite; int sectors_to_wipe, sector_idx; int pages_to_write, page_idx; int pages_to_read; int check_idx, check_error; unsigned char buf[1024]; struct bladeRF_firmware brf_fw; unsigned char *fw_buf; dev = file->private_data; data = (void __user *)arg; switch (cmd) { case BLADE_QUERY_VERSION: retval = __bladerf_rcv_cmd(dev, BLADE_USB_CMD_QUERY_VERSION, &ver, sizeof(ver)); if (retval >= 0) { ver.major = le16_to_cpu(ver.major); ver.minor = le16_to_cpu(ver.minor); retval = copy_to_user(data, &ver, sizeof(struct bladeRF_version)); } break; case BLADE_QUERY_FPGA_STATUS: retval = __bladerf_rcv_one_word(dev, BLADE_USB_CMD_QUERY_FPGA_STATUS, data); break; case BLADE_BEGIN_PROG: if (dev->intnum != 0) { ret = usb_set_interface(dev->udev, 0,0); dev->intnum = 0; } retval = __bladerf_rcv_one_word(dev, BLADE_USB_CMD_BEGIN_PROG, data); break; case BLADE_END_PROG: // TODO: send another 2 DCLK cycles to ensure compliance with C4's boot procedure retval = __bladerf_rcv_one_word(dev, BLADE_USB_CMD_QUERY_FPGA_STATUS, data); if (!retval) { ret = usb_set_interface(dev->udev, 1,0); dev->intnum = 1; } break; case BLADE_UPGRADE_FW: if (dev->intnum != 2) { retval = usb_set_interface(dev->udev, 2,0); if (retval) break; dev->intnum = 2; } if (copy_from_user(&brf_fw, data, sizeof(struct bladeRF_firmware))) { return -EFAULT; } brf_fw.len = ((brf_fw.len + 255) / 256) * 256; fw_buf = kzalloc(brf_fw.len, GFP_KERNEL); if (!fw_buf) goto leave_fw; if (copy_from_user(fw_buf, brf_fw.ptr, brf_fw.len)) { retval = -EFAULT; goto leave_fw; } retval = -ENODEV; sectors_to_wipe = (brf_fw.len + 0xffff) / 0x10000; printk("Going to wipe %d sectors\n", sectors_to_wipe); for (sector_idx = 0; sector_idx < sectors_to_wipe; sector_idx++) { printk("Erasing sector %d... ", sector_idx); retval = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), BLADE_USB_CMD_FLASH_ERASE, BLADE_USB_TYPE_IN, 0x0000, sector_idx, &ret, 4, BLADE_USB_TIMEOUT_MS * 100); printk("- erased\n"); if (retval != 4) { goto leave_fw; } ret = le32_to_cpu(ret); if (ret != 1) { printk("Unable to erase previous sector, quitting\n"); goto leave_fw; } } if (dev->udev->speed == USB_SPEED_HIGH) { sz = 64; } else if (dev->udev->speed == USB_SPEED_SUPER) { sz = 256; } pages_to_write = (brf_fw.len + 255) / 0x100; for (page_idx = pages_to_write - 1; page_idx >= 0; page_idx--) { nwrite = 0; do { retval = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), BLADE_USB_CMD_FLASH_WRITE, BLADE_USB_TYPE_OUT, 0x0000, page_idx, &fw_buf[page_idx * 256 + nwrite], sz, BLADE_USB_TIMEOUT_MS); nwrite += sz; } while (nwrite != 256); } pages_to_read = (brf_fw.len + 255) / 0x100; check_error = 0; for (page_idx = 0; page_idx < pages_to_read; page_idx++) { nread = 0; do { retval = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), BLADE_USB_CMD_FLASH_READ, BLADE_USB_TYPE_IN, 0x0000, page_idx, &buf[nread], sz, BLADE_USB_TIMEOUT_MS); nread += sz; } while (nread != 256); for (check_idx = 0; check_idx < 256; check_idx++) { if (buf[check_idx] != fw_buf[page_idx * 256 + check_idx]) { printk("ERROR: bladeRF firmware verification detected a mismatch at byte offset 0x%.8x\n", page_idx * 256 + check_idx); printk("ERROR: expected byte 0x%.2X, got 0x%.2X\n", fw_buf[page_idx * 256 + check_idx], buf[check_idx]); check_error = 1; goto leave_fw; } } } retval = 0; printk("SUCCESSFULLY VERIFIED\n"); leave_fw: kfree(fw_buf); break; case BLADE_CHECK_PROG: retval = 0; printk("ok %d\n", dev->intnum); if (dev->intnum == 0) { retval = __bladerf_rcv_cmd(dev, BLADE_USB_CMD_QUERY_FPGA_STATUS, &ret, sizeof(ret)); printk("retval =%d ret=%d\n", retval, ret); if (retval >= 0 && ret) { retval = 0; ret = usb_set_interface(dev->udev, 1,0); dev->intnum = 1; retval = copy_to_user((void __user *)arg, &ret, sizeof(ret)); printk("ok changed intf\n"); } } break; case BLADE_RF_RX: if (dev->intnum != 1) { dev_err(&dev->interface->dev, "Cannot enable RX from config mode\n"); retval = -1; break; } printk("RF_RX!\n"); retval = __bladerf_snd_one_word(dev, BLADE_USB_CMD_RF_RX, data); break; case BLADE_RF_TX: if (dev->intnum != 1) { dev_err(&dev->interface->dev, "Cannot enable TX from config mode\n"); retval = -1; break; } printk("RF_TX!\n"); retval = __bladerf_snd_one_word(dev, BLADE_USB_CMD_RF_TX, data); break; } return retval; }