3 * Copyright 2007 Free Software Foundation, Inc.
5 * This file is part of GNU Radio
7 * GNU Radio is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3, or (at your option)
12 * GNU Radio is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 #include <usrp_inband_usb_packet.h>
28 #include <usrp_bytesex.h>
33 bool usrp_inband_usb_packet::align32()
35 int p_len = payload_len();
37 int bytes_needed = 4 - (p_len % 4);
42 // If the room left in the packet is less than the number of bytes
43 // needed, return false to indicate no room to align
44 if((MAX_PAYLOAD - p_len) < bytes_needed)
47 p_len += bytes_needed;
49 int h_flags = flags();
52 int h_payload_len = p_len;
54 set_header(h_flags, h_chan, h_tag, h_payload_len);
59 bool usrp_inband_usb_packet::cs_ping(long rid, long ping_val)
64 int p_len = payload_len();
66 if((MAX_PAYLOAD - p_len) < (CS_PING_LEN + CS_FIXED_LEN))
70 ((OP_PING_FIXED & CS_OPCODE_MASK) << CS_OPCODE_SHIFT)
71 | ((CS_PING_LEN & CS_LEN_MASK) << CS_LEN_SHIFT)
72 | ((rid & CS_RID_MASK) << CS_RID_SHIFT)
73 | (ping_val & CS_PINGVAL_MASK)
77 uint32_t *payload = (uint32_t *) (d_payload + p_len);
78 *payload = host_to_usrp_u32(ping);
80 // Update payload length
81 int h_flags = flags();
84 int h_payload_len = payload_len() + CS_FIXED_LEN + CS_PING_LEN;
86 set_header(h_flags, h_chan, h_tag, h_payload_len);
92 bool usrp_inband_usb_packet::cs_ping_reply(long rid, long ping_val)
97 int p_len = payload_len();
99 if((MAX_PAYLOAD - p_len) < (CS_PING_LEN + CS_FIXED_LEN))
103 ((OP_PING_FIXED_REPLY & CS_OPCODE_MASK) << CS_OPCODE_SHIFT)
104 | ((CS_PING_LEN & CS_LEN_MASK) << CS_LEN_SHIFT)
105 | ((rid & CS_RID_MASK) << CS_RID_SHIFT)
106 | ((ping_val & CS_PINGVAL_MASK) << CS_PINGVAL_SHIFT)
110 uint32_t *payload = (uint32_t *) (d_payload + p_len);
111 *payload = host_to_usrp_u32(ping);
113 // Update payload length
114 int h_flags = flags();
117 int h_payload_len = payload_len() + CS_FIXED_LEN + CS_PING_LEN;
119 set_header(h_flags, h_chan, h_tag, h_payload_len);
124 bool usrp_inband_usb_packet::cs_write_reg(long reg_num, long val)
129 int p_len = payload_len();
131 if((MAX_PAYLOAD - p_len) < (CS_WRITEREG_LEN + CS_FIXED_LEN))
136 // Build the first word which includes the register number
138 ((OP_WRITE_REG & CS_OPCODE_MASK) << CS_OPCODE_SHIFT)
139 | ((CS_WRITEREG_LEN & CS_LEN_MASK) << CS_LEN_SHIFT)
140 | ((reg_num & CS_REGNUM_MASK) << CS_REGNUM_SHIFT)
143 uint32_t *payload = (uint32_t *) (d_payload + p_len);
144 *payload = host_to_usrp_u32(word0);
146 // The second word is solely the register value to be written
147 // FIXME: should this be unsigned?
149 *payload = host_to_usrp_u32((uint32_t) val);
151 // Rebuild the header to update the payload length
152 int h_flags = flags();
155 int h_payload_len = payload_len() + CS_FIXED_LEN + CS_WRITEREG_LEN;
157 set_header(h_flags, h_chan, h_tag, h_payload_len);
162 bool usrp_inband_usb_packet::cs_write_reg_masked(long reg_num, long val, long mask)
167 int p_len = payload_len();
169 if((MAX_PAYLOAD - p_len) < (CS_WRITEREGMASKED_LEN + CS_FIXED_LEN))
174 // Build the first word which includes the register number
176 ((OP_WRITE_REG_MASKED & CS_OPCODE_MASK) << CS_OPCODE_SHIFT)
177 | ((CS_WRITEREGMASKED_LEN & CS_LEN_MASK) << CS_LEN_SHIFT)
178 | ((reg_num & CS_REGNUM_MASK) << CS_REGNUM_SHIFT)
181 uint32_t *payload = (uint32_t *) (d_payload + p_len);
182 *payload = host_to_usrp_u32(word0);
184 // Skip over the first word and write the register value
186 *payload = host_to_usrp_u32((uint32_t) val);
188 // Skip over the register value and write the mask
190 *payload = host_to_usrp_u32((uint32_t) mask);
192 // Rebuild the header to update the payload length
193 int h_flags = flags();
196 int h_payload_len = payload_len() + CS_FIXED_LEN + CS_WRITEREGMASKED_LEN;
198 set_header(h_flags, h_chan, h_tag, h_payload_len);
203 bool usrp_inband_usb_packet::cs_read_reg(long rid, long reg_num)
208 int p_len = payload_len();
210 if((MAX_PAYLOAD - p_len) < (CS_READREG_LEN + CS_FIXED_LEN))
213 uint32_t read_reg = (
214 ((OP_READ_REG & CS_OPCODE_MASK) << CS_OPCODE_SHIFT)
215 | ((CS_READREG_LEN & CS_LEN_MASK) << CS_LEN_SHIFT)
216 | ((rid & CS_RID_MASK) << CS_RID_SHIFT)
217 | ((reg_num & CS_REGNUM_MASK) << CS_REGNUM_SHIFT)
221 uint32_t *payload = (uint32_t *) (d_payload + p_len);
222 *payload = host_to_usrp_u32(read_reg);
224 // Update payload length
225 int h_flags = flags();
228 int h_payload_len = payload_len() + CS_FIXED_LEN + CS_READREG_LEN;
230 set_header(h_flags, h_chan, h_tag, h_payload_len);
235 bool usrp_inband_usb_packet::cs_read_reg_reply(long rid, long reg_num, long reg_val)
240 int p_len = payload_len();
242 if((MAX_PAYLOAD - p_len) < (CS_READREGREPLY_LEN + CS_FIXED_LEN))
246 ((OP_READ_REG_REPLY & CS_OPCODE_MASK) << CS_OPCODE_SHIFT)
247 | ((CS_READREGREPLY_LEN & CS_LEN_MASK) << CS_LEN_SHIFT)
248 | ((rid & CS_RID_MASK) << CS_RID_SHIFT)
249 | ((reg_num & CS_REGNUM_MASK) << CS_REGNUM_SHIFT)
253 uint32_t *payload = (uint32_t *) (d_payload + p_len);
254 *payload = host_to_usrp_u32(word0);
256 // Hop to the next word and write the reg value
258 *payload = host_to_usrp_u32((uint32_t) reg_val);
260 // Update payload length
261 int h_flags = flags();
264 int h_payload_len = payload_len() + CS_FIXED_LEN + CS_READREGREPLY_LEN;
266 set_header(h_flags, h_chan, h_tag, h_payload_len);
271 bool usrp_inband_usb_packet::cs_delay(long ticks)
276 int p_len = payload_len();
278 if((MAX_PAYLOAD - p_len) < (CS_DELAY_LEN + CS_FIXED_LEN))
282 ((OP_DELAY & CS_OPCODE_MASK) << CS_OPCODE_SHIFT)
283 | ((CS_DELAY_LEN & CS_LEN_MASK) << CS_LEN_SHIFT)
284 | ((ticks & CS_DELAY_MASK) << CS_DELAY_SHIFT)
288 uint32_t *payload = (uint32_t *) (d_payload + p_len);
289 *payload = host_to_usrp_u32(delay);
291 // Update payload length
292 int h_flags = flags();
295 int h_payload_len = payload_len() + CS_FIXED_LEN + CS_DELAY_LEN;
297 set_header(h_flags, h_chan, h_tag, h_payload_len);
302 bool usrp_inband_usb_packet::cs_i2c_write(long i2c_addr, uint8_t *i2c_data, size_t data_len)
307 int p_len = payload_len();
309 int i2c_len = data_len + 2; // 2 bytes between mbz and addr
311 if((MAX_PAYLOAD - p_len) < (i2c_len + CS_FIXED_LEN))
317 ((OP_I2C_WRITE & CS_OPCODE_MASK) << CS_OPCODE_SHIFT)
318 | ((i2c_len & CS_LEN_MASK) << CS_LEN_SHIFT)
319 | ((i2c_addr & CS_I2CADDR_MASK) << CS_I2CADDR_SHIFT)
322 uint32_t *payload = (uint32_t *) (d_payload + p_len);
323 *payload = host_to_usrp_u32(word0);
325 // Jump over the first word and write the data
326 // FIXME: Should the data be changed to usrp byte order?
328 memcpy(payload, i2c_data, data_len);
330 // Update payload length
331 int h_flags = flags();
334 int h_payload_len = payload_len() + CS_FIXED_LEN + i2c_len;
336 set_header(h_flags, h_chan, h_tag, h_payload_len);
341 bool usrp_inband_usb_packet::cs_i2c_read(long rid, long i2c_addr, long n_bytes)
346 int p_len = payload_len();
348 if((MAX_PAYLOAD - p_len) < (CS_I2CREAD_LEN + CS_FIXED_LEN))
354 ((OP_I2C_READ & CS_OPCODE_MASK) << CS_OPCODE_SHIFT)
355 | ((CS_I2CREAD_LEN & CS_LEN_MASK) << CS_LEN_SHIFT)
356 | ((rid & CS_RID_MASK) << CS_RID_SHIFT)
357 | ((i2c_addr & CS_I2CADDR_MASK) << CS_I2CADDR_SHIFT)
360 uint32_t *payload = (uint32_t *) (d_payload + p_len);
361 *payload = host_to_usrp_u32(word0);
363 // Jump a word and write the number of bytes to read
366 (n_bytes & CS_I2CREADBYTES_MASK) << CS_I2CREADBYTES_SHIFT;
367 *payload = host_to_usrp_u32(word1);
369 // Update payload length
370 int h_flags = flags();
373 int h_payload_len = payload_len() + CS_FIXED_LEN + CS_I2CREAD_LEN;
375 set_header(h_flags, h_chan, h_tag, h_payload_len);
380 bool usrp_inband_usb_packet::cs_i2c_read_reply(long rid, long i2c_addr, uint8_t *i2c_data, long i2c_data_len)
385 int p_len = payload_len();
387 int i2c_len = i2c_data_len + 2;
389 if((MAX_PAYLOAD - p_len) < (i2c_len + CS_FIXED_LEN))
395 ((OP_I2C_READ_REPLY & CS_OPCODE_MASK) << CS_OPCODE_SHIFT)
396 | ((i2c_len & CS_LEN_MASK) << CS_LEN_SHIFT)
397 | ((rid & CS_RID_MASK) << CS_RID_SHIFT)
398 | ((i2c_addr & CS_I2CADDR_MASK) << CS_I2CADDR_SHIFT)
401 uint32_t *payload = (uint32_t *) (d_payload + p_len);
402 *payload = host_to_usrp_u32(word0);
404 // Jump a word and write the actual data
406 memcpy(payload, i2c_data, i2c_data_len);
408 // Update payload length
409 int h_flags = flags();
412 int h_payload_len = payload_len() + CS_FIXED_LEN + i2c_len;
414 set_header(h_flags, h_chan, h_tag, h_payload_len);
419 bool usrp_inband_usb_packet::cs_spi_write(long enables, long format, long opt_header_bytes, uint8_t *spi_data, long spi_data_len)
424 int p_len = payload_len();
426 int spi_len = spi_data_len + 6;
428 if((MAX_PAYLOAD - p_len) < (spi_len + CS_FIXED_LEN))
433 // First word contains the opcode and length, then mbz
435 ((OP_SPI_WRITE & CS_OPCODE_MASK) << CS_OPCODE_SHIFT)
436 | ((spi_len & CS_LEN_MASK) << CS_LEN_SHIFT)
438 uint32_t *payload = (uint32_t *) (d_payload + p_len);
439 *payload = host_to_usrp_u32(word);
443 // Second word contains the enables, format, and optional tx bytes
446 ((enables & CS_SPIENABLES_MASK) << CS_SPIENABLES_SHIFT)
447 | ((format & CS_SPIFORMAT_MASK) << CS_SPIFORMAT_SHIFT)
448 | ((opt_header_bytes & CS_SPIOPT_MASK) << CS_SPIOPT_SHIFT)
450 payload = (uint32_t *) (d_payload + p_len);
451 *payload = host_to_usrp_u32(word);
454 memcpy(payload, spi_data, spi_data_len);
456 // Update payload length
457 int h_flags = flags();
460 int h_payload_len = payload_len() + CS_FIXED_LEN + spi_len;
462 set_header(h_flags, h_chan, h_tag, h_payload_len);
467 bool usrp_inband_usb_packet::cs_spi_read(long rid, long enables, long format, long opt_header_bytes, long n_bytes)
472 int p_len = payload_len();
474 if((MAX_PAYLOAD - p_len) < (CS_SPIREAD_LEN + CS_FIXED_LEN))
479 // First word contains the opcode, length, and RID
481 ((OP_SPI_READ & CS_OPCODE_MASK) << CS_OPCODE_SHIFT)
482 | ((CS_SPIREAD_LEN & CS_LEN_MASK) << CS_LEN_SHIFT)
483 | ((rid & CS_RID_MASK) << CS_RID_SHIFT)
485 uint32_t *payload = (uint32_t *) (d_payload + p_len);
486 *payload = host_to_usrp_u32(word);
490 // Second word contains the enables, format, and optional tx bytes
493 ((enables & CS_SPIENABLES_MASK) << CS_SPIENABLES_SHIFT)
494 | ((format & CS_SPIFORMAT_MASK) << CS_SPIFORMAT_SHIFT)
495 | ((opt_header_bytes & CS_SPIOPT_MASK) << CS_SPIOPT_SHIFT)
497 payload = (uint32_t *) (d_payload + p_len);
498 *payload = host_to_usrp_u32(word);
502 // The third word contains the number of bytes
505 ((n_bytes & CS_SPINBYTES_MASK) << CS_SPINBYTES_SHIFT)
507 payload = (uint32_t *) (d_payload + p_len);
508 *payload = host_to_usrp_u32(word);
510 // Update payload length
511 int h_flags = flags();
514 int h_payload_len = payload_len() + CS_FIXED_LEN + CS_SPIREAD_LEN;
516 set_header(h_flags, h_chan, h_tag, h_payload_len);
521 bool usrp_inband_usb_packet::cs_spi_read_reply(long rid, uint8_t *spi_data, long spi_data_len)
526 int p_len = payload_len();
528 int spi_len = spi_data_len + 2;
530 if((MAX_PAYLOAD - p_len) < (spi_len + CS_FIXED_LEN))
535 // First word contains the opcode, length, and RID
537 ((OP_SPI_READ_REPLY & CS_OPCODE_MASK) << CS_OPCODE_SHIFT)
538 | ((spi_len & CS_LEN_MASK) << CS_LEN_SHIFT)
539 | ((rid & CS_RID_MASK) << CS_RID_SHIFT)
541 uint32_t *payload = (uint32_t *) (d_payload + p_len);
542 *payload = host_to_usrp_u32(word);
544 // Jump a word and write the actual data
546 memcpy(payload, spi_data, spi_data_len);
548 // Update payload length
549 int h_flags = flags();
552 int h_payload_len = payload_len() + CS_FIXED_LEN + spi_len;
554 set_header(h_flags, h_chan, h_tag, h_payload_len);
559 // Takes an offset to the beginning of a subpacket and extracts the
560 // length of the subpacket
561 int usrp_inband_usb_packet::cs_len(int payload_offset) {
562 uint32_t subpkt = usrp_to_host_u32(*((uint32_t *)(d_payload + payload_offset)));
563 return (subpkt >> CS_LEN_SHIFT) & CS_LEN_MASK;
566 // The following method takes an offset within the packet payload to extract
567 // a control/status subpacket and construct a pmt response which includes the
568 // proper signal and arguments specified by usrp-low-level-cs. The USRP
569 // server could therefore use this to read subpackets and pass them responses
570 // back up to the application. It's arguable that only reply packets should
571 // be parsed here, however we parse others for use in debugging or failure
572 // reporting on the transmit side of packets.
573 pmt_t usrp_inband_usb_packet::read_subpacket(int payload_offset) {
575 uint32_t subpkt = usrp_to_host_u32(*((uint32_t *)(d_payload + payload_offset)));
576 uint32_t opcode = (subpkt >> CS_OPCODE_SHIFT) & CS_OPCODE_MASK;
577 uint32_t len = (subpkt >> CS_LEN_SHIFT) & CS_LEN_MASK;
581 case OP_PING_FIXED_REPLY:
583 pmt_t rid = pmt_from_long((subpkt >> CS_RID_SHIFT) & CS_RID_MASK);
584 pmt_t pingval = pmt_from_long((subpkt >> CS_PINGVAL_SHIFT) & CS_PINGVAL_MASK);
585 return pmt_list3(s_op_ping_fixed_reply, rid, pingval);
588 case OP_READ_REG_REPLY:
590 pmt_t rid = pmt_from_long((subpkt >> CS_RID_SHIFT) & CS_RID_MASK);
591 pmt_t reg_num = pmt_from_long((subpkt >> CS_REGNUM_SHIFT) & CS_REGNUM_MASK);
593 // To get the register value we just read the next 32 bits
594 uint32_t val = usrp_to_host_u32(*((uint32_t *)(d_payload + payload_offset + 4)));
595 pmt_t reg_val = pmt_from_long(val);
597 return pmt_list4(s_op_read_reg_reply, rid, reg_num, reg_val);
600 case OP_I2C_READ_REPLY:
602 pmt_t rid = pmt_from_long((subpkt >> CS_RID_SHIFT) & CS_RID_MASK);
603 pmt_t i2c_addr = pmt_from_long((subpkt >> CS_I2CADDR_SHIFT) & CS_I2CADDR_MASK);
605 // Make a u8 vector to dump the data from the packet into
607 pmt_t i2c_data = pmt_make_u8vector(len - 2, 0); // skip rid+mbz+addr = 2 bytes
609 (uint8_t *) pmt_u8vector_writeable_elements(i2c_data, i2c_data_len);
611 memcpy(w_data, d_payload + payload_offset + 4, i2c_data_len); // skip first word
613 return pmt_list4(s_op_i2c_read_reply, rid, i2c_addr, i2c_data);
616 case OP_SPI_READ_REPLY:
618 pmt_t rid = pmt_from_long((subpkt >> CS_RID_SHIFT) & CS_RID_MASK);
620 // Make a u8 vector to dump the data from the packet into
622 pmt_t spi_data = pmt_make_u8vector(len - 2, 0); // skip rid+mbz+addr = 2 bytes
624 (uint8_t *) pmt_u8vector_writeable_elements(spi_data, spi_data_len);
626 memcpy(w_data, d_payload + payload_offset + 4, spi_data_len); // skip first word
628 return pmt_list3(s_op_spi_read_reply, rid, spi_data);
633 pmt_t rid = pmt_from_long((subpkt >> CS_RID_SHIFT) & CS_RID_MASK);
634 pmt_t pingval = pmt_from_long((subpkt >> CS_PINGVAL_SHIFT) & CS_PINGVAL_MASK);
635 return pmt_list3(s_op_ping_fixed, rid, pingval);
640 pmt_t reg_num = pmt_from_long((subpkt >> CS_REGNUM_SHIFT) & CS_REGNUM_MASK);
642 // To get the register value we just read the next 32 bits
643 uint32_t val = usrp_to_host_u32(*((uint32_t *)(d_payload + payload_offset + 4)));
644 pmt_t reg_val = pmt_from_long(val);
646 return pmt_list3(s_op_write_reg, reg_num, reg_val);
649 case OP_WRITE_REG_MASKED:
651 pmt_t reg_num = pmt_from_long((subpkt >> CS_REGNUM_SHIFT) & CS_REGNUM_MASK);
653 // To get the register value we just read the next 32 bits
654 uint32_t val = usrp_to_host_u32(*((uint32_t *)(d_payload + payload_offset + 4)));
655 pmt_t reg_val = pmt_from_long(val);
657 // The mask is the next 32 bits
658 uint32_t mask = usrp_to_host_u32(*((uint32_t *)(d_payload + payload_offset + 8)));
659 pmt_t reg_mask = pmt_from_long(mask);
661 return pmt_list4(s_op_write_reg_masked, reg_num, reg_val, reg_mask);
666 pmt_t rid = pmt_from_long((subpkt >> CS_RID_SHIFT) & CS_RID_MASK);
667 pmt_t reg_num = pmt_from_long((subpkt >> CS_REGNUM_SHIFT) & CS_REGNUM_MASK);
669 return pmt_list3(s_op_read_reg, rid, reg_num);
674 pmt_t i2c_addr = pmt_from_long((subpkt >> CS_I2CADDR_SHIFT) & CS_I2CADDR_MASK);
676 // The length includes an extra 2 bytes for storing the mbz and addr
677 pmt_t i2c_data = pmt_make_u8vector(len-2, 0);
679 // Get a writeable address to copy the data from the packet
681 uint8_t *w_data = (uint8_t *) pmt_u8vector_writeable_elements(i2c_data, ignore);
682 memcpy(w_data, d_payload + payload_offset + 4, len-2);
685 return pmt_list3(s_op_i2c_write, i2c_addr, i2c_data);
690 pmt_t rid = pmt_from_long((subpkt >> CS_RID_SHIFT) & CS_RID_MASK);
691 pmt_t i2c_addr = pmt_from_long((subpkt >> CS_I2CADDR_SHIFT) & CS_I2CADDR_MASK);
693 // The number of bytes is in the next word
694 uint32_t bytes = usrp_to_host_u32(*((uint32_t *)(d_payload + payload_offset + 4)));
695 bytes = (bytes >> CS_I2CREADBYTES_SHIFT) & CS_I2CREADBYTES_MASK;
696 pmt_t i2c_bytes = pmt_from_long(bytes);
698 return pmt_list4(s_op_i2c_read, rid, i2c_addr, i2c_bytes);
703 // Nothing interesting in the first word, skip to the next
704 uint32_t word = usrp_to_host_u32(*((uint32_t *)(d_payload + payload_offset + 4)));
705 pmt_t enables = pmt_from_long((word >> CS_SPIENABLES_SHIFT) & CS_SPIENABLES_MASK);
706 pmt_t format = pmt_from_long((word >> CS_SPIFORMAT_SHIFT) & CS_SPIFORMAT_MASK);
707 pmt_t opt = pmt_from_long((word >> CS_SPIOPT_SHIFT) & CS_SPIOPT_MASK);
709 // From the next word and on is data
711 pmt_t spi_data = pmt_make_u8vector(len - 6, 0); // skip rid+mbz+addr = 2 bytes
713 (uint8_t *) pmt_u8vector_writeable_elements(spi_data, spi_data_len);
715 memcpy(w_data, d_payload + payload_offset + 8, spi_data_len); // skip first 2 words
717 return pmt_list5(s_op_spi_write, enables, format, opt, spi_data);
722 // Read the RID from the first word, the rest is mbz
723 pmt_t rid = pmt_from_long((subpkt >> CS_RID_SHIFT) & CS_RID_MASK);
725 // Continue at the next word...
726 uint32_t word = usrp_to_host_u32(*((uint32_t *)(d_payload + payload_offset + 4)));
727 pmt_t enables = pmt_from_long((word >> CS_SPIENABLES_SHIFT) & CS_SPIENABLES_MASK);
728 pmt_t format = pmt_from_long((word >> CS_SPIFORMAT_SHIFT) & CS_SPIFORMAT_MASK);
729 pmt_t opt = pmt_from_long((word >> CS_SPIOPT_SHIFT) & CS_SPIOPT_MASK);
731 // The number of bytes is the only thing to read in the next word
732 word = usrp_to_host_u32(*((uint32_t *)(d_payload + payload_offset + 8)));
733 pmt_t n_bytes = pmt_from_long((word >> CS_SPINBYTES_SHIFT) & CS_SPINBYTES_MASK);
735 return pmt_list6(s_op_spi_read, rid, enables, format, opt, n_bytes);
740 pmt_t ticks = pmt_from_long((subpkt >> CS_DELAY_SHIFT) & CS_DELAY_MASK);
742 return pmt_list2(s_op_delay, ticks);