Skip to content

Commit 494ecaf

Browse files
author
Eric Poulsen
committed
*/*spi.c
*/*spi.h Support for odd bit lengths in SPI, per discussion at micropython#5225
1 parent 3032ae1 commit 494ecaf

File tree

15 files changed

+143
-132
lines changed

15 files changed

+143
-132
lines changed

drivers/bus/softspi.c

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* The MIT License (MIT)
55
*
66
* Copyright (c) 2016-2018 Damien P. George
7+
* Copyright (c) 2019 "Eric Poulsen" <eric@zyxod.com>
78
*
89
* Permission is hereby granted, free of charge, to any person obtaining a copy
910
* of this software and associated documentation files (the "Software"), to deal
@@ -25,6 +26,7 @@
2526
*/
2627

2728
#include "drivers/bus/spi.h"
29+
#include "py/runtime.h"
2830

2931
int mp_soft_spi_ioctl(void *self_in, uint32_t cmd) {
3032
mp_soft_spi_obj_t *self = (mp_soft_spi_obj_t*)self_in;
@@ -44,38 +46,63 @@ int mp_soft_spi_ioctl(void *self_in, uint32_t cmd) {
4446
return 0;
4547
}
4648

47-
void mp_soft_spi_transfer(void *self_in, size_t len, const uint8_t *src, uint8_t *dest) {
49+
void mp_soft_spi_transfer(void *self_in, size_t len, const uint8_t *src, uint8_t *dest, uint8_t bits) {
4850
mp_soft_spi_obj_t *self = (mp_soft_spi_obj_t*)self_in;
4951
uint32_t delay_half = self->delay_half;
5052

53+
bits = bits ? bits : self->bits;
54+
if (bits == 0) {
55+
// Shoudln't be possible with soft SPI, but
56+
// just in case.
57+
mp_raise_ValueError("bits cannot be 0");
58+
}
59+
60+
int bytesPerChunk = (bits + 7) / 8;
61+
// round length down as needed (possibly to zero)
62+
len = len / bytesPerChunk * bytesPerChunk;
63+
if(len == 0) {
64+
return;
65+
}
66+
67+
uint8_t remBits = bits;
68+
5169
// only MSB transfer is implemented
5270

5371
// If a port defines MICROPY_HW_SOFTSPI_MIN_DELAY, and the configured
5472
// delay_half is equal to this value, then the software SPI implementation
5573
// will run as fast as possible, limited only by CPU speed and GPIO time.
5674
#ifdef MICROPY_HW_SOFTSPI_MIN_DELAY
57-
if (delay_half == MICROPY_HW_SOFTSPI_MIN_DELAY) {
58-
for (size_t i = 0; i < len; ++i) {
59-
uint8_t data_out = src[i];
75+
if (delay_half == MICROPY_HW_SOFTSPI_MIN_DELAY)
76+
{
77+
while (len--) {
78+
uint8_t bitsThisByte = remBits < 8 ? remBits : 8;
79+
uint8_t data_out = *src++;
6080
uint8_t data_in = 0;
61-
for (int j = 0; j < 8; ++j, data_out <<= 1) {
81+
82+
remBits -= bitsThisByte;
83+
for (; bitsThisByte; --bitsThisByte, data_out <<= 1) {
6284
mp_hal_pin_write(self->mosi, (data_out >> 7) & 1);
6385
mp_hal_pin_write(self->sck, 1 - self->polarity);
6486
data_in = (data_in << 1) | mp_hal_pin_read(self->miso);
6587
mp_hal_pin_write(self->sck, self->polarity);
6688
}
89+
remBits = remBits ? remBits : bits;
6790
if (dest != NULL) {
68-
dest[i] = data_in;
91+
*dest++ = data_in;
6992
}
7093
}
7194
return;
7295
}
73-
#endif
96+
#endif
7497

75-
for (size_t i = 0; i < len; ++i) {
76-
uint8_t data_out = src[i];
98+
99+
while(len--) {
100+
uint8_t bitsThisByte = remBits < 8 ? remBits : 8;
101+
uint8_t data_out = *src++;
77102
uint8_t data_in = 0;
78-
for (int j = 0; j < 8; ++j, data_out <<= 1) {
103+
104+
remBits -= bitsThisByte;
105+
for(;bitsThisByte; --bitsThisByte, data_out <<= 1) {
79106
mp_hal_pin_write(self->mosi, (data_out >> 7) & 1);
80107
if (self->phase == 0) {
81108
mp_hal_delay_us_fast(delay_half);
@@ -93,8 +120,9 @@ void mp_soft_spi_transfer(void *self_in, size_t len, const uint8_t *src, uint8_t
93120
mp_hal_delay_us_fast(delay_half);
94121
}
95122
}
123+
remBits = remBits ? remBits : bits;
96124
if (dest != NULL) {
97-
dest[i] = data_in;
125+
*dest++ = data_in;
98126
}
99127
}
100128
}

drivers/bus/spi.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,14 @@ enum {
3535

3636
typedef struct _mp_spi_proto_t {
3737
int (*ioctl)(void *self, uint32_t cmd);
38-
void (*transfer)(void *self, size_t len, const uint8_t *src, uint8_t *dest);
38+
void (*transfer)(void *self, size_t len, const uint8_t *src, uint8_t *dest, uint8_t bits);
3939
} mp_spi_proto_t;
4040

4141
typedef struct _mp_soft_spi_obj_t {
4242
uint32_t delay_half; // microsecond delay for half SCK period
4343
uint8_t polarity;
4444
uint8_t phase;
45+
uint8_t bits;
4546
mp_hal_pin_obj_t sck;
4647
mp_hal_pin_obj_t mosi;
4748
mp_hal_pin_obj_t miso;
@@ -50,6 +51,6 @@ typedef struct _mp_soft_spi_obj_t {
5051
extern const mp_spi_proto_t mp_soft_spi_proto;
5152

5253
int mp_soft_spi_ioctl(void *self, uint32_t cmd);
53-
void mp_soft_spi_transfer(void *self, size_t len, const uint8_t *src, uint8_t *dest);
54+
void mp_soft_spi_transfer(void *self, size_t len, const uint8_t *src, uint8_t *dest, uint8_t bits);
5455

5556
#endif // MICROPY_INCLUDED_DRIVERS_BUS_SPI_H

drivers/memory/spiflash.c

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ STATIC void mp_spiflash_write_cmd_data(mp_spiflash_t *self, uint8_t cmd, size_t
6969
if (c->bus_kind == MP_SPIFLASH_BUS_SPI) {
7070
// Note: len/data are unused for standard SPI
7171
mp_hal_pin_write(c->bus.u_spi.cs, 0);
72-
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, 1, &cmd, NULL);
72+
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, 1, &cmd, NULL, 8);
7373
mp_hal_pin_write(c->bus.u_spi.cs, 1);
7474
} else {
7575
c->bus.u_qspi.proto->write_cmd_data(c->bus.u_qspi.data, cmd, len, data);
@@ -81,9 +81,9 @@ STATIC void mp_spiflash_write_cmd_addr_data(mp_spiflash_t *self, uint8_t cmd, ui
8181
if (c->bus_kind == MP_SPIFLASH_BUS_SPI) {
8282
uint8_t buf[4] = {cmd, addr >> 16, addr >> 8, addr};
8383
mp_hal_pin_write(c->bus.u_spi.cs, 0);
84-
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, 4, buf, NULL);
84+
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, 4, buf, NULL, 8);
8585
if (len) {
86-
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, len, src, NULL);
86+
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, len, src, NULL, 8);
8787
}
8888
mp_hal_pin_write(c->bus.u_spi.cs, 1);
8989
} else {
@@ -96,8 +96,8 @@ STATIC uint32_t mp_spiflash_read_cmd(mp_spiflash_t *self, uint8_t cmd, size_t le
9696
if (c->bus_kind == MP_SPIFLASH_BUS_SPI) {
9797
uint32_t buf;
9898
mp_hal_pin_write(c->bus.u_spi.cs, 0);
99-
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, 1, &cmd, NULL);
100-
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, len, (void*)&buf, (void*)&buf);
99+
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, 1, &cmd, NULL, 8);
100+
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, len, (void*)&buf, (void*)&buf, 8);
101101
mp_hal_pin_write(c->bus.u_spi.cs, 1);
102102
return buf;
103103
} else {
@@ -110,8 +110,8 @@ STATIC void mp_spiflash_read_data(mp_spiflash_t *self, uint32_t addr, size_t len
110110
if (c->bus_kind == MP_SPIFLASH_BUS_SPI) {
111111
uint8_t buf[4] = {CMD_READ, addr >> 16, addr >> 8, addr};
112112
mp_hal_pin_write(c->bus.u_spi.cs, 0);
113-
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, 4, buf, NULL);
114-
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, len, dest, dest);
113+
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, 4, buf, NULL, 8);
114+
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, len, dest, dest, 8);
115115
mp_hal_pin_write(c->bus.u_spi.cs, 1);
116116
} else {
117117
c->bus.u_qspi.proto->read_cmd_qaddr_qdata(c->bus.u_qspi.data, CMD_C4READ, addr, len, dest);

extmod/machine_spi.c

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -81,50 +81,59 @@ STATIC mp_obj_t machine_spi_deinit(mp_obj_t self) {
8181
}
8282
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_spi_deinit_obj, machine_spi_deinit);
8383

84-
STATIC void mp_machine_spi_transfer(mp_obj_t self, size_t len, const void *src, void *dest) {
84+
STATIC void mp_machine_spi_transfer(mp_obj_t self, size_t len, const void *src, void *dest, uint8_t bits) {
8585
mp_obj_base_t *s = (mp_obj_base_t*)MP_OBJ_TO_PTR(self);
8686
mp_machine_spi_p_t *spi_p = (mp_machine_spi_p_t*)s->type->protocol;
87-
spi_p->transfer(s, len, src, dest);
87+
spi_p->transfer(s, len, src, dest, bits);
8888
}
8989

9090
STATIC mp_obj_t mp_machine_spi_read(size_t n_args, const mp_obj_t *args) {
9191
vstr_t vstr;
9292
vstr_init_len(&vstr, mp_obj_get_int(args[1]));
93-
memset(vstr.buf, n_args == 3 ? mp_obj_get_int(args[2]) : 0, vstr.len);
94-
mp_machine_spi_transfer(args[0], vstr.len, vstr.buf, vstr.buf);
93+
memset(vstr.buf, n_args >= 3 ? mp_obj_get_int(args[2]) : 0, vstr.len);
94+
uint8_t bits = n_args == 4 ? mp_obj_get_int(args[3]) : 0;
95+
mp_machine_spi_transfer(args[0], vstr.len, vstr.buf, vstr.buf, bits);
9596
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
9697
}
97-
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_read_obj, 2, 3, mp_machine_spi_read);
98+
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_read_obj, 2, 4, mp_machine_spi_read);
9899

99100
STATIC mp_obj_t mp_machine_spi_readinto(size_t n_args, const mp_obj_t *args) {
100101
mp_buffer_info_t bufinfo;
101102
mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE);
102-
memset(bufinfo.buf, n_args == 3 ? mp_obj_get_int(args[2]) : 0, bufinfo.len);
103-
mp_machine_spi_transfer(args[0], bufinfo.len, bufinfo.buf, bufinfo.buf);
103+
memset(bufinfo.buf, n_args >= 3 ? mp_obj_get_int(args[2]) : 0, bufinfo.len);
104+
uint8_t bits = n_args == 4 ? mp_obj_get_int(args[3]) : 0;
105+
mp_machine_spi_transfer(args[0], bufinfo.len, bufinfo.buf, bufinfo.buf, bits);
104106
return mp_const_none;
105107
}
106-
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_readinto_obj, 2, 3, mp_machine_spi_readinto);
108+
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_readinto_obj, 2, 4, mp_machine_spi_readinto);
107109

108-
STATIC mp_obj_t mp_machine_spi_write(mp_obj_t self, mp_obj_t wr_buf) {
110+
STATIC mp_obj_t mp_machine_spi_write(size_t n_args, const mp_obj_t *args) {
111+
mp_obj_t self = args[0];
112+
mp_obj_t wr_buf = args[1];
113+
uint8_t bits = n_args == 3 ? mp_obj_get_int(args[2]) : 0;
109114
mp_buffer_info_t src;
110115
mp_get_buffer_raise(wr_buf, &src, MP_BUFFER_READ);
111-
mp_machine_spi_transfer(self, src.len, (const uint8_t*)src.buf, NULL);
116+
mp_machine_spi_transfer(self, src.len, (const uint8_t*)src.buf, NULL, bits);
112117
return mp_const_none;
113118
}
114-
MP_DEFINE_CONST_FUN_OBJ_2(mp_machine_spi_write_obj, mp_machine_spi_write);
119+
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_write_obj, 2, 3, mp_machine_spi_write);
115120

116-
STATIC mp_obj_t mp_machine_spi_write_readinto(mp_obj_t self, mp_obj_t wr_buf, mp_obj_t rd_buf) {
121+
STATIC mp_obj_t mp_machine_spi_write_readinto(size_t n_args, const mp_obj_t *args) {
122+
mp_obj_t self = args[0];
123+
mp_obj_t wr_buf = args[1];
124+
mp_obj_t rd_buf = args[2];
125+
uint8_t bits = n_args == 4 ? mp_obj_get_int(args[3]) : 0;
117126
mp_buffer_info_t src;
118127
mp_get_buffer_raise(wr_buf, &src, MP_BUFFER_READ);
119128
mp_buffer_info_t dest;
120129
mp_get_buffer_raise(rd_buf, &dest, MP_BUFFER_WRITE);
121130
if (src.len != dest.len) {
122131
mp_raise_ValueError("buffers must be the same length");
123132
}
124-
mp_machine_spi_transfer(self, src.len, src.buf, dest.buf);
133+
mp_machine_spi_transfer(self, src.len, src.buf, dest.buf, bits);
125134
return mp_const_none;
126135
}
127-
MP_DEFINE_CONST_FUN_OBJ_3(mp_machine_spi_write_readinto_obj, mp_machine_spi_write_readinto);
136+
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_write_readinto_obj, 3, 4, mp_machine_spi_write_readinto);
128137

129138
STATIC const mp_rom_map_elem_t machine_spi_locals_dict_table[] = {
130139
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_spi_init_obj) },
@@ -201,8 +210,9 @@ STATIC mp_obj_t mp_machine_soft_spi_make_new(const mp_obj_type_t *type, size_t n
201210
self->spi.delay_half = baudrate_to_delay_half(args[ARG_baudrate].u_int);
202211
self->spi.polarity = args[ARG_polarity].u_int;
203212
self->spi.phase = args[ARG_phase].u_int;
204-
if (args[ARG_bits].u_int != 8) {
205-
mp_raise_ValueError("bits must be 8");
213+
self->spi.bits = args[ARG_bits].u_int;
214+
if (self->spi.bits == 0) {
215+
mp_raise_ValueError("bits must be > 0");
206216
}
207217
if (args[ARG_firstbit].u_int != MICROPY_PY_MACHINE_SPI_MSB) {
208218
mp_raise_ValueError("firstbit must be MSB");
@@ -260,9 +270,9 @@ STATIC void mp_machine_soft_spi_init(mp_obj_base_t *self_in, size_t n_args, cons
260270
mp_soft_spi_ioctl(&self->spi, MP_SPI_IOCTL_INIT);
261271
}
262272

263-
STATIC void mp_machine_soft_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) {
273+
STATIC void mp_machine_soft_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest, uint8_t bits) {
264274
mp_machine_soft_spi_obj_t *self = (mp_machine_soft_spi_obj_t*)self_in;
265-
mp_soft_spi_transfer(&self->spi, len, src, dest);
275+
mp_soft_spi_transfer(&self->spi, len, src, dest, bits);
266276
}
267277

268278
const mp_machine_spi_p_t mp_machine_soft_spi_p = {

extmod/machine_spi.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
typedef struct _mp_machine_spi_p_t {
3535
void (*init)(mp_obj_base_t *obj, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
3636
void (*deinit)(mp_obj_base_t *obj); // can be NULL
37-
void (*transfer)(mp_obj_base_t *obj, size_t len, const uint8_t *src, uint8_t *dest);
37+
void (*transfer)(mp_obj_base_t *obj, size_t len, const uint8_t *src, uint8_t *dest, uint8_t bits);
3838
} mp_machine_spi_p_t;
3939

4040
typedef struct _mp_machine_soft_spi_obj_t {
@@ -47,10 +47,9 @@ extern const mp_obj_type_t mp_machine_soft_spi_type;
4747
extern const mp_obj_dict_t mp_machine_spi_locals_dict;
4848

4949
mp_obj_t mp_machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args);
50-
5150
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_read_obj);
5251
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_readinto_obj);
53-
MP_DECLARE_CONST_FUN_OBJ_2(mp_machine_spi_write_obj);
54-
MP_DECLARE_CONST_FUN_OBJ_3(mp_machine_spi_write_readinto_obj);
52+
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_write_obj);
53+
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_write_readinto_obj);
5554

5655
#endif // MICROPY_INCLUDED_EXTMOD_MACHINE_SPI_H

ports/esp32/machine_hw_spi.c

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -237,54 +237,68 @@ STATIC void machine_hw_spi_deinit(mp_obj_base_t *self_in) {
237237
}
238238
}
239239

240-
STATIC void machine_hw_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) {
240+
STATIC void machine_hw_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest, uint8_t bits) {
241241
machine_hw_spi_obj_t *self = MP_OBJ_TO_PTR(self_in);
242242

243243
if (self->state == MACHINE_HW_SPI_STATE_DEINIT) {
244244
mp_raise_msg(&mp_type_OSError, "transfer on deinitialized SPI");
245245
return;
246246
}
247247

248-
struct spi_transaction_t transaction = { 0 };
249-
250-
// Round to nearest whole set of bits
251-
int bits_to_send = len * 8 / self->bits * self->bits;
248+
bits = bits ? bits : self->bits;
249+
bool wholeBytes = bits % 8 == 0;
250+
if (bits == 0) {
251+
mp_raise_ValueError("bits cannot be 0");
252+
}
252253

253254

254-
if (len <= 4) {
255-
if (src != NULL) {
256-
memcpy(&transaction.tx_data, src, len);
257-
}
255+
int bytesPerChunk = (bits + 7) / 8;
256+
// round length down as needed (possibly to zero)
257+
len = len / bytesPerChunk * bytesPerChunk;
258+
if(len == 0) {
259+
return;
260+
}
258261

259-
transaction.flags = SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA;
260-
transaction.length = bits_to_send;
261-
spi_device_transmit(self->spi, &transaction);
262+
struct spi_transaction_t transaction;
263+
while(len) {
262264

263-
if (dest != NULL) {
264-
memcpy(dest, &transaction.rx_data, len);
265-
}
266-
} else {
267-
int offset = 0;
268-
int bits_remaining = bits_to_send;
265+
memset(&transaction, 0, sizeof(transaction));
266+
if ((bits <= 32 && !wholeBytes) || len <= 4) {
267+
if (src != NULL) {
268+
memcpy(&transaction.tx_data, src, bytesPerChunk);
269+
}
269270

270-
while (bits_remaining) {
271-
memset(&transaction, 0, sizeof(transaction));
271+
transaction.flags = SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA;
272+
transaction.length = bits;
273+
spi_device_transmit(self->spi, &transaction);
272274

275+
if (dest != NULL) {
276+
memcpy(dest, &transaction.rx_data, bytesPerChunk);
277+
}
278+
src += bytesPerChunk;
279+
len -= bytesPerChunk;
280+
} else {
281+
if (wholeBytes) {
282+
transaction.length = len * 8 / bits * bits;
283+
} else {
284+
transaction.length = bits;
285+
}
273286
transaction.length =
274-
bits_remaining > MP_HW_SPI_MAX_XFER_BITS ? MP_HW_SPI_MAX_XFER_BITS : bits_remaining;
287+
transaction.length > MP_HW_SPI_MAX_XFER_BITS
288+
? MP_HW_SPI_MAX_XFER_BITS
289+
: transaction.length;
275290

291+
size_t bytesSent = (transaction.length + 7) / 8;
276292
if (src != NULL) {
277-
transaction.tx_buffer = src + offset;
293+
transaction.tx_buffer = src;
294+
src += bytesSent;
278295
}
279296
if (dest != NULL) {
280-
transaction.rx_buffer = dest + offset;
297+
transaction.rx_buffer = dest;
298+
dest += bytesSent;
281299
}
282-
300+
len -= bytesSent;
283301
spi_device_transmit(self->spi, &transaction);
284-
bits_remaining -= transaction.length;
285-
286-
// doesn't need ceil(); loop ends when bits_remaining is 0
287-
offset += transaction.length / 8;
288302
}
289303
}
290304
}

ports/esp8266/machine_hspi.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ typedef struct _machine_hspi_obj_t {
4848
uint8_t phase;
4949
} machine_hspi_obj_t;
5050

51-
STATIC void machine_hspi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) {
51+
STATIC void machine_hspi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest, uint8_t bits) {
5252
(void)self_in;
5353

5454
if (dest == NULL) {

0 commit comments

Comments
 (0)