feat(Ubiquitous/RT_Thread): port micropython on RT-Thread for aiit-board

This commit is contained in:
yangtuo250
2022-03-10 16:22:44 +08:00
parent 73d10380ea
commit f83f073fa0
434 changed files with 94105 additions and 111 deletions
@@ -0,0 +1,117 @@
/*
* Automatically generated header file: don't edit
*/
#define HAVE_DOT_CONFIG 1
#define CONFIG_PLATFORM_LINUX 1
#undef CONFIG_PLATFORM_CYGWIN
#undef CONFIG_PLATFORM_WIN32
/*
* General Configuration
*/
#define PREFIX "/usr/local"
#undef CONFIG_DEBUG
#undef CONFIG_STRIP_UNWANTED_SECTIONS
#undef CONFIG_VISUAL_STUDIO_7_0
#undef CONFIG_VISUAL_STUDIO_8_0
#undef CONFIG_VISUAL_STUDIO_10_0
#define CONFIG_VISUAL_STUDIO_7_0_BASE ""
#define CONFIG_VISUAL_STUDIO_8_0_BASE ""
#define CONFIG_VISUAL_STUDIO_10_0_BASE ""
#define CONFIG_EXTRA_CFLAGS_OPTIONS ""
#define CONFIG_EXTRA_LDFLAGS_OPTIONS ""
/*
* SSL Library
*/
#undef CONFIG_SSL_SERVER_ONLY
#undef CONFIG_SSL_CERT_VERIFICATION
#undef CONFIG_SSL_FULL_MODE
#define CONFIG_SSL_SKELETON_MODE 1
#define CONFIG_SSL_ENABLE_SERVER 1
#define CONFIG_SSL_ENABLE_CLIENT 1
#undef CONFIG_SSL_DIAGNOSTICS
#define CONFIG_SSL_PROT_LOW 1
#undef CONFIG_SSL_PROT_MEDIUM
#undef CONFIG_SSL_PROT_HIGH
#define CONFIG_SSL_AES 1
#define CONFIG_SSL_USE_DEFAULT_KEY 1
#define CONFIG_SSL_PRIVATE_KEY_LOCATION ""
#define CONFIG_SSL_PRIVATE_KEY_PASSWORD ""
#define CONFIG_SSL_X509_CERT_LOCATION ""
#undef CONFIG_SSL_GENERATE_X509_CERT
#define CONFIG_SSL_X509_COMMON_NAME ""
#define CONFIG_SSL_X509_ORGANIZATION_NAME ""
#define CONFIG_SSL_X509_ORGANIZATION_UNIT_NAME ""
#undef CONFIG_SSL_HAS_PEM
#undef CONFIG_SSL_USE_PKCS12
#define CONFIG_SSL_EXPIRY_TIME
#define CONFIG_X509_MAX_CA_CERTS 0
#define CONFIG_SSL_MAX_CERTS 3
#undef CONFIG_SSL_CTX_MUTEXING
#undef CONFIG_USE_DEV_URANDOM
#undef CONFIG_WIN32_USE_CRYPTO_LIB
#undef CONFIG_OPENSSL_COMPATIBLE
#undef CONFIG_PERFORMANCE_TESTING
#undef CONFIG_SSL_TEST
#undef CONFIG_AXTLSWRAP
#undef CONFIG_AXHTTPD
#undef CONFIG_HTTP_STATIC_BUILD
#define CONFIG_HTTP_PORT
#define CONFIG_HTTP_HTTPS_PORT
#define CONFIG_HTTP_SESSION_CACHE_SIZE
#define CONFIG_HTTP_WEBROOT ""
#define CONFIG_HTTP_TIMEOUT
#undef CONFIG_HTTP_HAS_CGI
#define CONFIG_HTTP_CGI_EXTENSIONS ""
#undef CONFIG_HTTP_ENABLE_LUA
#define CONFIG_HTTP_LUA_PREFIX ""
#undef CONFIG_HTTP_BUILD_LUA
#define CONFIG_HTTP_CGI_LAUNCHER ""
#undef CONFIG_HTTP_DIRECTORIES
#undef CONFIG_HTTP_HAS_AUTHORIZATION
#undef CONFIG_HTTP_HAS_IPV6
#undef CONFIG_HTTP_ENABLE_DIFFERENT_USER
#define CONFIG_HTTP_USER ""
#undef CONFIG_HTTP_VERBOSE
#undef CONFIG_HTTP_IS_DAEMON
/*
* Language Bindings
*/
#undef CONFIG_BINDINGS
#undef CONFIG_CSHARP_BINDINGS
#undef CONFIG_VBNET_BINDINGS
#define CONFIG_DOT_NET_FRAMEWORK_BASE ""
#undef CONFIG_JAVA_BINDINGS
#define CONFIG_JAVA_HOME ""
#undef CONFIG_PERL_BINDINGS
#define CONFIG_PERL_CORE ""
#define CONFIG_PERL_LIB ""
#undef CONFIG_LUA_BINDINGS
#define CONFIG_LUA_CORE ""
/*
* Samples
*/
#undef CONFIG_SAMPLES
#undef CONFIG_C_SAMPLES
#undef CONFIG_CSHARP_SAMPLES
#undef CONFIG_VBNET_SAMPLES
#undef CONFIG_JAVA_SAMPLES
#undef CONFIG_PERL_SAMPLES
#undef CONFIG_LUA_SAMPLES
#undef CONFIG_BIGINT_CLASSICAL
#undef CONFIG_BIGINT_MONTGOMERY
#undef CONFIG_BIGINT_BARRETT
#undef CONFIG_BIGINT_CRT
#undef CONFIG_BIGINT_KARATSUBA
#define MUL_KARATSUBA_THRESH
#define SQU_KARATSUBA_THRESH
#undef CONFIG_BIGINT_SLIDING_WINDOW
#undef CONFIG_BIGINT_SQUARE
#undef CONFIG_BIGINT_CHECK_ON
#undef CONFIG_INTEGER_32BIT
#undef CONFIG_INTEGER_16BIT
#undef CONFIG_INTEGER_8BIT
@@ -0,0 +1 @@
#define AXTLS_VERSION "(no version)"
@@ -0,0 +1,158 @@
/*********************************************************************
* Source: https://github.com/B-Con/crypto-algorithms
* Filename: sha256.c
* Author: Brad Conte (brad AT bradconte.com)
* Copyright: This code is released into the public domain.
* Disclaimer: This code is presented "as is" without any guarantees.
* Details: Implementation of the SHA-256 hashing algorithm.
SHA-256 is one of the three algorithms in the SHA2
specification. The others, SHA-384 and SHA-512, are not
offered in this implementation.
Algorithm specification can be found here:
* http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf
This implementation uses little endian byte order.
*********************************************************************/
/*************************** HEADER FILES ***************************/
#include <stdlib.h>
#include "sha256.h"
/****************************** MACROS ******************************/
#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b))))
#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b))))
#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))
#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22))
#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25))
#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3))
#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10))
/**************************** VARIABLES *****************************/
static const WORD k[64] = {
0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
};
/*********************** FUNCTION DEFINITIONS ***********************/
static void sha256_transform(CRYAL_SHA256_CTX *ctx, const BYTE data[])
{
WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];
for (i = 0, j = 0; i < 16; ++i, j += 4)
m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);
for ( ; i < 64; ++i)
m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];
a = ctx->state[0];
b = ctx->state[1];
c = ctx->state[2];
d = ctx->state[3];
e = ctx->state[4];
f = ctx->state[5];
g = ctx->state[6];
h = ctx->state[7];
for (i = 0; i < 64; ++i) {
t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i];
t2 = EP0(a) + MAJ(a,b,c);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
ctx->state[0] += a;
ctx->state[1] += b;
ctx->state[2] += c;
ctx->state[3] += d;
ctx->state[4] += e;
ctx->state[5] += f;
ctx->state[6] += g;
ctx->state[7] += h;
}
void sha256_init(CRYAL_SHA256_CTX *ctx)
{
ctx->datalen = 0;
ctx->bitlen = 0;
ctx->state[0] = 0x6a09e667;
ctx->state[1] = 0xbb67ae85;
ctx->state[2] = 0x3c6ef372;
ctx->state[3] = 0xa54ff53a;
ctx->state[4] = 0x510e527f;
ctx->state[5] = 0x9b05688c;
ctx->state[6] = 0x1f83d9ab;
ctx->state[7] = 0x5be0cd19;
}
void sha256_update(CRYAL_SHA256_CTX *ctx, const BYTE data[], size_t len)
{
WORD i;
for (i = 0; i < len; ++i) {
ctx->data[ctx->datalen] = data[i];
ctx->datalen++;
if (ctx->datalen == 64) {
sha256_transform(ctx, ctx->data);
ctx->bitlen += 512;
ctx->datalen = 0;
}
}
}
void sha256_final(CRYAL_SHA256_CTX *ctx, BYTE hash[])
{
WORD i;
i = ctx->datalen;
// Pad whatever data is left in the buffer.
if (ctx->datalen < 56) {
ctx->data[i++] = 0x80;
while (i < 56)
ctx->data[i++] = 0x00;
}
else {
ctx->data[i++] = 0x80;
while (i < 64)
ctx->data[i++] = 0x00;
sha256_transform(ctx, ctx->data);
memset(ctx->data, 0, 56);
}
// Append to the padding the total message's length in bits and transform.
ctx->bitlen += ctx->datalen * 8;
ctx->data[63] = ctx->bitlen;
ctx->data[62] = ctx->bitlen >> 8;
ctx->data[61] = ctx->bitlen >> 16;
ctx->data[60] = ctx->bitlen >> 24;
ctx->data[59] = ctx->bitlen >> 32;
ctx->data[58] = ctx->bitlen >> 40;
ctx->data[57] = ctx->bitlen >> 48;
ctx->data[56] = ctx->bitlen >> 56;
sha256_transform(ctx, ctx->data);
// Since this implementation uses little endian byte ordering and SHA uses big endian,
// reverse all the bytes when copying the final state to the output hash.
for (i = 0; i < 4; ++i) {
hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;
hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;
hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;
hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;
hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;
hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;
hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;
}
}
@@ -0,0 +1,35 @@
/*********************************************************************
* Source: https://github.com/B-Con/crypto-algorithms
* Filename: sha256.h
* Author: Brad Conte (brad AT bradconte.com)
* Copyright: This code is released into the public domain.
* Disclaimer: This code is presented "as is" without any guarantees.
* Details: Defines the API for the corresponding SHA1 implementation.
*********************************************************************/
#ifndef SHA256_H
#define SHA256_H
/*************************** HEADER FILES ***************************/
#include <stddef.h>
/****************************** MACROS ******************************/
#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest
/**************************** DATA TYPES ****************************/
typedef unsigned char BYTE; // 8-bit byte
typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines
typedef struct {
BYTE data[64];
WORD datalen;
unsigned long long bitlen;
WORD state[8];
} CRYAL_SHA256_CTX;
/*********************** FUNCTION DECLARATIONS **********************/
void sha256_init(CRYAL_SHA256_CTX *ctx);
void sha256_update(CRYAL_SHA256_CTX *ctx, const BYTE data[], size_t len);
void sha256_final(CRYAL_SHA256_CTX *ctx, BYTE hash[]);
#endif // SHA256_H
@@ -0,0 +1,41 @@
#ifndef MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_CC_H
#define MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_CC_H
#include <stdint.h>
// Generate lwip's internal types from stdint
typedef uint8_t u8_t;
typedef int8_t s8_t;
typedef uint16_t u16_t;
typedef int16_t s16_t;
typedef uint32_t u32_t;
typedef int32_t s32_t;
typedef u32_t mem_ptr_t;
#define U16_F "hu"
#define S16_F "hd"
#define X16_F "hx"
#define U32_F "u"
#define S32_F "d"
#define X32_F "x"
#define X8_F "02x"
#define SZT_F "u"
#define BYTE_ORDER LITTLE_ENDIAN
#define LWIP_CHKSUM_ALGORITHM 2
#include <assert.h>
#define LWIP_PLATFORM_DIAG(x)
#define LWIP_PLATFORM_ASSERT(x) { assert(1); }
//#define PACK_STRUCT_FIELD(x) x __attribute__((packed))
#define PACK_STRUCT_FIELD(x) x
#define PACK_STRUCT_STRUCT __attribute__((packed))
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_END
#endif // MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_CC_H
@@ -0,0 +1,7 @@
#ifndef MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_PERF_H
#define MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_PERF_H
#define PERF_START /* null definition */
#define PERF_STOP(x) /* null definition */
#endif // MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_PERF_H
@@ -0,0 +1,35 @@
#ifndef MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_LWIPOPTS_H
#define MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_LWIPOPTS_H
#include <py/mpconfig.h>
#include <py/misc.h>
#include <py/mphal.h>
// We're running without an OS for this port. We don't provide any services except light protection.
#define NO_SYS 1
#define SYS_LIGHTWEIGHT_PROT 1
#include <stdint.h>
typedef uint32_t sys_prot_t;
#define TCP_LISTEN_BACKLOG 1
// We'll put these into a proper ifdef once somebody implements an ethernet driver
#define LWIP_ARP 0
#define LWIP_ETHERNET 0
#define LWIP_DNS 1
#define LWIP_NETCONN 0
#define LWIP_SOCKET 0
#ifdef MICROPY_PY_LWIP_SLIP
#define LWIP_HAVE_SLIPIF 1
#endif
// For now, we can simply define this as a macro for the timer code. But this function isn't
// universal and other ports will need to do something else. It may be necessary to move
// things like this into a port-provided header file.
#define sys_now mp_hal_ticks_ms
#endif // MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_LWIPOPTS_H
@@ -0,0 +1,703 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "py/mperrno.h"
#include "py/mphal.h"
#include "py/runtime.h"
#include "extmod/machine_i2c.h"
#if MICROPY_PY_MACHINE_I2C
typedef mp_machine_soft_i2c_obj_t machine_i2c_obj_t;
STATIC void mp_hal_i2c_delay(machine_i2c_obj_t *self) {
// We need to use an accurate delay to get acceptable I2C
// speeds (eg 1us should be not much more than 1us).
mp_hal_delay_us_fast(self->us_delay);
}
STATIC void mp_hal_i2c_scl_low(machine_i2c_obj_t *self) {
mp_hal_pin_od_low(self->scl);
}
STATIC int mp_hal_i2c_scl_release(machine_i2c_obj_t *self) {
uint32_t count = self->us_timeout;
mp_hal_pin_od_high(self->scl);
mp_hal_i2c_delay(self);
// For clock stretching, wait for the SCL pin to be released, with timeout.
for (; mp_hal_pin_read(self->scl) == 0 && count; --count) {
mp_hal_delay_us_fast(1);
}
if (count == 0) {
return -MP_ETIMEDOUT;
}
return 0; // success
}
STATIC void mp_hal_i2c_sda_low(machine_i2c_obj_t *self) {
mp_hal_pin_od_low(self->sda);
}
STATIC void mp_hal_i2c_sda_release(machine_i2c_obj_t *self) {
mp_hal_pin_od_high(self->sda);
}
STATIC int mp_hal_i2c_sda_read(machine_i2c_obj_t *self) {
return mp_hal_pin_read(self->sda);
}
STATIC int mp_hal_i2c_start(machine_i2c_obj_t *self) {
mp_hal_i2c_sda_release(self);
mp_hal_i2c_delay(self);
int ret = mp_hal_i2c_scl_release(self);
if (ret != 0) {
return ret;
}
mp_hal_i2c_sda_low(self);
mp_hal_i2c_delay(self);
return 0; // success
}
STATIC int mp_hal_i2c_stop(machine_i2c_obj_t *self) {
mp_hal_i2c_delay(self);
mp_hal_i2c_sda_low(self);
mp_hal_i2c_delay(self);
int ret = mp_hal_i2c_scl_release(self);
mp_hal_i2c_sda_release(self);
mp_hal_i2c_delay(self);
return ret;
}
STATIC void mp_hal_i2c_init(machine_i2c_obj_t *self, uint32_t freq) {
self->us_delay = 500000 / freq;
if (self->us_delay == 0) {
self->us_delay = 1;
}
mp_hal_pin_open_drain(self->scl);
mp_hal_pin_open_drain(self->sda);
mp_hal_i2c_stop(self); // ignore error
}
// return value:
// 0 - byte written and ack received
// 1 - byte written and nack received
// <0 - error, with errno being the negative of the return value
STATIC int mp_hal_i2c_write_byte(machine_i2c_obj_t *self, uint8_t val) {
mp_hal_i2c_delay(self);
mp_hal_i2c_scl_low(self);
for (int i = 7; i >= 0; i--) {
if ((val >> i) & 1) {
mp_hal_i2c_sda_release(self);
} else {
mp_hal_i2c_sda_low(self);
}
mp_hal_i2c_delay(self);
int ret = mp_hal_i2c_scl_release(self);
if (ret != 0) {
mp_hal_i2c_sda_release(self);
return ret;
}
mp_hal_i2c_scl_low(self);
}
mp_hal_i2c_sda_release(self);
mp_hal_i2c_delay(self);
int ret = mp_hal_i2c_scl_release(self);
if (ret != 0) {
return ret;
}
int ack = mp_hal_i2c_sda_read(self);
mp_hal_i2c_delay(self);
mp_hal_i2c_scl_low(self);
return ack;
}
// return value:
// 0 - success
// <0 - error, with errno being the negative of the return value
STATIC int mp_hal_i2c_read_byte(machine_i2c_obj_t *self, uint8_t *val, int nack) {
mp_hal_i2c_delay(self);
mp_hal_i2c_scl_low(self);
mp_hal_i2c_delay(self);
uint8_t data = 0;
for (int i = 7; i >= 0; i--) {
int ret = mp_hal_i2c_scl_release(self);
if (ret != 0) {
return ret;
}
data = (data << 1) | mp_hal_i2c_sda_read(self);
mp_hal_i2c_scl_low(self);
mp_hal_i2c_delay(self);
}
*val = data;
// send ack/nack bit
if (!nack) {
mp_hal_i2c_sda_low(self);
}
mp_hal_i2c_delay(self);
int ret = mp_hal_i2c_scl_release(self);
if (ret != 0) {
mp_hal_i2c_sda_release(self);
return ret;
}
mp_hal_i2c_scl_low(self);
mp_hal_i2c_sda_release(self);
return 0; // success
}
// return value:
// >=0 - success; for read it's 0, for write it's number of acks received
// <0 - error, with errno being the negative of the return value
int mp_machine_soft_i2c_transfer(mp_obj_base_t *self_in, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags) {
machine_i2c_obj_t *self = (machine_i2c_obj_t*)self_in;
// start the I2C transaction
int ret = mp_hal_i2c_start(self);
if (ret != 0) {
return ret;
}
// write the slave address
ret = mp_hal_i2c_write_byte(self, (addr << 1) | (flags & MP_MACHINE_I2C_FLAG_READ));
if (ret < 0) {
return ret;
} else if (ret != 0) {
// nack received, release the bus cleanly
mp_hal_i2c_stop(self);
return -MP_ENODEV;
}
int transfer_ret = 0;
for (; n--; ++bufs) {
size_t len = bufs->len;
uint8_t *buf = bufs->buf;
if (flags & MP_MACHINE_I2C_FLAG_READ) {
// read bytes from the slave into the given buffer(s)
while (len--) {
ret = mp_hal_i2c_read_byte(self, buf++, (n | len) == 0);
if (ret != 0) {
return ret;
}
}
} else {
// write bytes from the given buffer(s) to the slave
while (len--) {
ret = mp_hal_i2c_write_byte(self, *buf++);
if (ret < 0) {
return ret;
} else if (ret != 0) {
// nack received, stop sending
n = 0;
break;
}
++transfer_ret; // count the number of acks
}
}
}
// finish the I2C transaction
if (flags & MP_MACHINE_I2C_FLAG_STOP) {
ret = mp_hal_i2c_stop(self);
if (ret != 0) {
return ret;
}
}
return transfer_ret;
}
/******************************************************************************/
// Generic helper functions
// For use by ports that require a single buffer of data for a read/write transfer
int mp_machine_i2c_transfer_adaptor(mp_obj_base_t *self, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags) {
size_t len;
uint8_t *buf;
if (n == 1) {
// Use given single buffer
len = bufs[0].len;
buf = bufs[0].buf;
} else {
// Combine buffers into a single one
len = 0;
for (size_t i = 0; i < n; ++i) {
len += bufs[i].len;
}
buf = m_new(uint8_t, len);
if (!(flags & MP_MACHINE_I2C_FLAG_READ)) {
len = 0;
for (size_t i = 0; i < n; ++i) {
memcpy(buf + len, bufs[i].buf, bufs[i].len);
len += bufs[i].len;
}
}
}
mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol;
int ret = i2c_p->transfer_single(self, addr, len, buf, flags);
if (n > 1) {
if (flags & MP_MACHINE_I2C_FLAG_READ) {
// Copy data from single buffer to individual ones
len = 0;
for (size_t i = 0; i < n; ++i) {
memcpy(bufs[i].buf, buf + len, bufs[i].len);
len += bufs[i].len;
}
}
m_del(uint8_t, buf, len);
}
return ret;
}
STATIC int mp_machine_i2c_readfrom(mp_obj_base_t *self, uint16_t addr, uint8_t *dest, size_t len, bool stop) {
mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol;
mp_machine_i2c_buf_t buf = {.len = len, .buf = dest};
unsigned int flags = MP_MACHINE_I2C_FLAG_READ | (stop ? MP_MACHINE_I2C_FLAG_STOP : 0);
return i2c_p->transfer(self, addr, 1, &buf, flags);
}
STATIC int mp_machine_i2c_writeto(mp_obj_base_t *self, uint16_t addr, const uint8_t *src, size_t len, bool stop) {
mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol;
mp_machine_i2c_buf_t buf = {.len = len, .buf = (uint8_t*)src};
unsigned int flags = stop ? MP_MACHINE_I2C_FLAG_STOP : 0;
return i2c_p->transfer(self, addr, 1, &buf, flags);
}
/******************************************************************************/
// MicroPython bindings for I2C
STATIC void machine_i2c_obj_init_helper(machine_i2c_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_scl, ARG_sda, ARG_freq, ARG_timeout };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_scl, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_sda, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 400000} },
{ MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 255} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
self->scl = mp_hal_get_pin_obj(args[ARG_scl].u_obj);
self->sda = mp_hal_get_pin_obj(args[ARG_sda].u_obj);
self->us_timeout = args[ARG_timeout].u_int;
mp_hal_i2c_init(self, args[ARG_freq].u_int);
}
STATIC mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
// check the id argument, if given
if (n_args > 0) {
if (args[0] != MP_OBJ_NEW_SMALL_INT(-1)) {
#if defined(MICROPY_PY_MACHINE_I2C_MAKE_NEW)
// dispatch to port-specific constructor
extern mp_obj_t MICROPY_PY_MACHINE_I2C_MAKE_NEW(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args);
return MICROPY_PY_MACHINE_I2C_MAKE_NEW(type, n_args, n_kw, args);
#else
mp_raise_ValueError("invalid I2C peripheral");
#endif
}
--n_args;
++args;
}
// create new soft I2C object
machine_i2c_obj_t *self = m_new_obj(machine_i2c_obj_t);
self->base.type = &machine_i2c_type;
mp_map_t kw_args;
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
machine_i2c_obj_init_helper(self, n_args, args, &kw_args);
return MP_OBJ_FROM_PTR(self);
}
STATIC mp_obj_t machine_i2c_obj_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
machine_i2c_obj_init_helper(MP_OBJ_TO_PTR(args[0]), n_args - 1, args + 1, kw_args);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_init_obj, 1, machine_i2c_obj_init);
STATIC mp_obj_t machine_i2c_scan(mp_obj_t self_in) {
mp_obj_base_t *self = MP_OBJ_TO_PTR(self_in);
mp_obj_t list = mp_obj_new_list(0, NULL);
// 7-bit addresses 0b0000xxx and 0b1111xxx are reserved
for (int addr = 0x08; addr < 0x78; ++addr) {
int ret = mp_machine_i2c_writeto(self, addr, NULL, 0, true);
if (ret == 0) {
mp_obj_list_append(list, MP_OBJ_NEW_SMALL_INT(addr));
}
}
return list;
}
MP_DEFINE_CONST_FUN_OBJ_1(machine_i2c_scan_obj, machine_i2c_scan);
STATIC mp_obj_t machine_i2c_start(mp_obj_t self_in) {
mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(self_in);
mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol;
if (i2c_p->start == NULL) {
mp_raise_msg(&mp_type_OSError, "I2C operation not supported");
}
int ret = i2c_p->start(self);
if (ret != 0) {
mp_raise_OSError(-ret);
}
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(machine_i2c_start_obj, machine_i2c_start);
STATIC mp_obj_t machine_i2c_stop(mp_obj_t self_in) {
mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(self_in);
mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol;
if (i2c_p->stop == NULL) {
mp_raise_msg(&mp_type_OSError, "I2C operation not supported");
}
int ret = i2c_p->stop(self);
if (ret != 0) {
mp_raise_OSError(-ret);
}
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(machine_i2c_stop_obj, machine_i2c_stop);
STATIC mp_obj_t machine_i2c_readinto(size_t n_args, const mp_obj_t *args) {
mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]);
mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol;
if (i2c_p->read == NULL) {
mp_raise_msg(&mp_type_OSError, "I2C operation not supported");
}
// get the buffer to read into
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE);
// work out if we want to send a nack at the end
bool nack = (n_args == 2) ? true : mp_obj_is_true(args[2]);
// do the read
int ret = i2c_p->read(self, bufinfo.buf, bufinfo.len, nack);
if (ret != 0) {
mp_raise_OSError(-ret);
}
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_i2c_readinto_obj, 2, 3, machine_i2c_readinto);
STATIC mp_obj_t machine_i2c_write(mp_obj_t self_in, mp_obj_t buf_in) {
mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(self_in);
mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol;
if (i2c_p->write == NULL) {
mp_raise_msg(&mp_type_OSError, "I2C operation not supported");
}
// get the buffer to write from
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ);
// do the write
int ret = i2c_p->write(self, bufinfo.buf, bufinfo.len);
if (ret < 0) {
mp_raise_OSError(-ret);
}
// return number of acks received
return MP_OBJ_NEW_SMALL_INT(ret);
}
MP_DEFINE_CONST_FUN_OBJ_2(machine_i2c_write_obj, machine_i2c_write);
STATIC mp_obj_t machine_i2c_readfrom(size_t n_args, const mp_obj_t *args) {
mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]);
mp_int_t addr = mp_obj_get_int(args[1]);
vstr_t vstr;
vstr_init_len(&vstr, mp_obj_get_int(args[2]));
bool stop = (n_args == 3) ? true : mp_obj_is_true(args[3]);
int ret = mp_machine_i2c_readfrom(self, addr, (uint8_t*)vstr.buf, vstr.len, stop);
if (ret < 0) {
mp_raise_OSError(-ret);
}
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_i2c_readfrom_obj, 3, 4, machine_i2c_readfrom);
STATIC mp_obj_t machine_i2c_readfrom_into(size_t n_args, const mp_obj_t *args) {
mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]);
mp_int_t addr = mp_obj_get_int(args[1]);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_WRITE);
bool stop = (n_args == 3) ? true : mp_obj_is_true(args[3]);
int ret = mp_machine_i2c_readfrom(self, addr, bufinfo.buf, bufinfo.len, stop);
if (ret < 0) {
mp_raise_OSError(-ret);
}
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_i2c_readfrom_into_obj, 3, 4, machine_i2c_readfrom_into);
STATIC mp_obj_t machine_i2c_writeto(size_t n_args, const mp_obj_t *args) {
mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]);
mp_int_t addr = mp_obj_get_int(args[1]);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ);
bool stop = (n_args == 3) ? true : mp_obj_is_true(args[3]);
int ret = mp_machine_i2c_writeto(self, addr, bufinfo.buf, bufinfo.len, stop);
if (ret < 0) {
mp_raise_OSError(-ret);
}
// return number of acks received
return MP_OBJ_NEW_SMALL_INT(ret);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_i2c_writeto_obj, 3, 4, machine_i2c_writeto);
STATIC mp_obj_t machine_i2c_writevto(size_t n_args, const mp_obj_t *args) {
mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]);
mp_int_t addr = mp_obj_get_int(args[1]);
// Get the list of data buffer(s) to write
size_t nitems;
const mp_obj_t *items;
mp_obj_get_array(args[2], &nitems, (mp_obj_t**)&items);
// Get the stop argument
bool stop = (n_args == 3) ? true : mp_obj_is_true(args[3]);
// Extract all buffer data, skipping zero-length buffers
size_t alloc = nitems == 0 ? 1 : nitems;
size_t nbufs = 0;
mp_machine_i2c_buf_t *bufs = mp_local_alloc(alloc * sizeof(mp_machine_i2c_buf_t));
for (; nitems--; ++items) {
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(*items, &bufinfo, MP_BUFFER_READ);
if (bufinfo.len > 0) {
bufs[nbufs].len = bufinfo.len;
bufs[nbufs++].buf = bufinfo.buf;
}
}
// Make sure there is at least one buffer, empty if needed
if (nbufs == 0) {
bufs[0].len = 0;
bufs[0].buf = NULL;
nbufs = 1;
}
// Do the I2C transfer
mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol;
int ret = i2c_p->transfer(self, addr, nbufs, bufs, stop ? MP_MACHINE_I2C_FLAG_STOP : 0);
mp_local_free(bufs);
if (ret < 0) {
mp_raise_OSError(-ret);
}
// Return number of acks received
return MP_OBJ_NEW_SMALL_INT(ret);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_i2c_writevto_obj, 3, 4, machine_i2c_writevto);
STATIC int read_mem(mp_obj_t self_in, uint16_t addr, uint32_t memaddr, uint8_t addrsize, uint8_t *buf, size_t len) {
mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(self_in);
uint8_t memaddr_buf[4];
size_t memaddr_len = 0;
for (int16_t i = addrsize - 8; i >= 0; i -= 8) {
memaddr_buf[memaddr_len++] = memaddr >> i;
}
int ret = mp_machine_i2c_writeto(self, addr, memaddr_buf, memaddr_len, false);
if (ret != memaddr_len) {
// must generate STOP
mp_machine_i2c_writeto(self, addr, NULL, 0, true);
return ret;
}
return mp_machine_i2c_readfrom(self, addr, buf, len, true);
}
STATIC int write_mem(mp_obj_t self_in, uint16_t addr, uint32_t memaddr, uint8_t addrsize, const uint8_t *buf, size_t len) {
mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(self_in);
// Create buffer with memory address
size_t memaddr_len = 0;
uint8_t memaddr_buf[4];
for (int16_t i = addrsize - 8; i >= 0; i -= 8) {
memaddr_buf[memaddr_len++] = memaddr >> i;
}
// Create partial write buffers
mp_machine_i2c_buf_t bufs[2] = {
{.len = memaddr_len, .buf = memaddr_buf},
{.len = len, .buf = (uint8_t*)buf},
};
// Do I2C transfer
mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol;
return i2c_p->transfer(self, addr, 2, bufs, MP_MACHINE_I2C_FLAG_STOP);
}
STATIC const mp_arg_t machine_i2c_mem_allowed_args[] = {
{ MP_QSTR_addr, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_memaddr, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_arg, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_addrsize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} },
};
STATIC mp_obj_t machine_i2c_readfrom_mem(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_addr, ARG_memaddr, ARG_n, ARG_addrsize };
mp_arg_val_t args[MP_ARRAY_SIZE(machine_i2c_mem_allowed_args)];
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args,
MP_ARRAY_SIZE(machine_i2c_mem_allowed_args), machine_i2c_mem_allowed_args, args);
// create the buffer to store data into
vstr_t vstr;
vstr_init_len(&vstr, mp_obj_get_int(args[ARG_n].u_obj));
// do the transfer
int ret = read_mem(pos_args[0], args[ARG_addr].u_int, args[ARG_memaddr].u_int,
args[ARG_addrsize].u_int, (uint8_t*)vstr.buf, vstr.len);
if (ret < 0) {
mp_raise_OSError(-ret);
}
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
}
MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_readfrom_mem_obj, 1, machine_i2c_readfrom_mem);
STATIC mp_obj_t machine_i2c_readfrom_mem_into(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_addr, ARG_memaddr, ARG_buf, ARG_addrsize };
mp_arg_val_t args[MP_ARRAY_SIZE(machine_i2c_mem_allowed_args)];
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args,
MP_ARRAY_SIZE(machine_i2c_mem_allowed_args), machine_i2c_mem_allowed_args, args);
// get the buffer to store data into
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[ARG_buf].u_obj, &bufinfo, MP_BUFFER_WRITE);
// do the transfer
int ret = read_mem(pos_args[0], args[ARG_addr].u_int, args[ARG_memaddr].u_int,
args[ARG_addrsize].u_int, bufinfo.buf, bufinfo.len);
if (ret < 0) {
mp_raise_OSError(-ret);
}
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_readfrom_mem_into_obj, 1, machine_i2c_readfrom_mem_into);
STATIC mp_obj_t machine_i2c_writeto_mem(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_addr, ARG_memaddr, ARG_buf, ARG_addrsize };
mp_arg_val_t args[MP_ARRAY_SIZE(machine_i2c_mem_allowed_args)];
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args,
MP_ARRAY_SIZE(machine_i2c_mem_allowed_args), machine_i2c_mem_allowed_args, args);
// get the buffer to write the data from
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[ARG_buf].u_obj, &bufinfo, MP_BUFFER_READ);
// do the transfer
int ret = write_mem(pos_args[0], args[ARG_addr].u_int, args[ARG_memaddr].u_int,
args[ARG_addrsize].u_int, bufinfo.buf, bufinfo.len);
if (ret < 0) {
mp_raise_OSError(-ret);
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_writeto_mem_obj, 1, machine_i2c_writeto_mem);
STATIC const mp_rom_map_elem_t machine_i2c_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_i2c_init_obj) },
{ MP_ROM_QSTR(MP_QSTR_scan), MP_ROM_PTR(&machine_i2c_scan_obj) },
// primitive I2C operations
{ MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&machine_i2c_start_obj) },
{ MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&machine_i2c_stop_obj) },
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&machine_i2c_readinto_obj) },
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&machine_i2c_write_obj) },
// standard bus operations
{ MP_ROM_QSTR(MP_QSTR_readfrom), MP_ROM_PTR(&machine_i2c_readfrom_obj) },
{ MP_ROM_QSTR(MP_QSTR_readfrom_into), MP_ROM_PTR(&machine_i2c_readfrom_into_obj) },
{ MP_ROM_QSTR(MP_QSTR_writeto), MP_ROM_PTR(&machine_i2c_writeto_obj) },
{ MP_ROM_QSTR(MP_QSTR_writevto), MP_ROM_PTR(&machine_i2c_writevto_obj) },
// memory operations
{ MP_ROM_QSTR(MP_QSTR_readfrom_mem), MP_ROM_PTR(&machine_i2c_readfrom_mem_obj) },
{ MP_ROM_QSTR(MP_QSTR_readfrom_mem_into), MP_ROM_PTR(&machine_i2c_readfrom_mem_into_obj) },
{ MP_ROM_QSTR(MP_QSTR_writeto_mem), MP_ROM_PTR(&machine_i2c_writeto_mem_obj) },
};
MP_DEFINE_CONST_DICT(mp_machine_soft_i2c_locals_dict, machine_i2c_locals_dict_table);
int mp_machine_soft_i2c_read(mp_obj_base_t *self_in, uint8_t *dest, size_t len, bool nack) {
machine_i2c_obj_t *self = (machine_i2c_obj_t*)self_in;
while (len--) {
int ret = mp_hal_i2c_read_byte(self, dest++, nack && (len == 0));
if (ret != 0) {
return ret;
}
}
return 0; // success
}
int mp_machine_soft_i2c_write(mp_obj_base_t *self_in, const uint8_t *src, size_t len) {
machine_i2c_obj_t *self = (machine_i2c_obj_t*)self_in;
int num_acks = 0;
while (len--) {
int ret = mp_hal_i2c_write_byte(self, *src++);
if (ret < 0) {
return ret;
} else if (ret != 0) {
// nack received, stop sending
break;
}
++num_acks;
}
return num_acks;
}
STATIC const mp_machine_i2c_p_t mp_machine_soft_i2c_p = {
.start = (int(*)(mp_obj_base_t*))mp_hal_i2c_start,
.stop = (int(*)(mp_obj_base_t*))mp_hal_i2c_stop,
.read = mp_machine_soft_i2c_read,
.write = mp_machine_soft_i2c_write,
.transfer = mp_machine_soft_i2c_transfer,
};
const mp_obj_type_t machine_i2c_type = {
{ &mp_type_type },
.name = MP_QSTR_I2C,
.make_new = machine_i2c_make_new,
.protocol = &mp_machine_soft_i2c_p,
.locals_dict = (mp_obj_dict_t*)&mp_machine_soft_i2c_locals_dict,
};
#endif // MICROPY_PY_MACHINE_I2C
@@ -0,0 +1,65 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_EXTMOD_MACHINE_I2C_H
#define MICROPY_INCLUDED_EXTMOD_MACHINE_I2C_H
#include "py/obj.h"
#define MP_MACHINE_I2C_FLAG_READ (0x01) // if not set then it's a write
#define MP_MACHINE_I2C_FLAG_STOP (0x02)
typedef struct _mp_machine_i2c_buf_t {
size_t len;
uint8_t *buf;
} mp_machine_i2c_buf_t;
// I2C protocol
// the first 4 methods can be NULL, meaning operation is not supported
// transfer_single only needs to be set if transfer=mp_machine_i2c_transfer_adaptor
typedef struct _mp_machine_i2c_p_t {
int (*start)(mp_obj_base_t *obj);
int (*stop)(mp_obj_base_t *obj);
int (*read)(mp_obj_base_t *obj, uint8_t *dest, size_t len, bool nack);
int (*write)(mp_obj_base_t *obj, const uint8_t *src, size_t len);
int (*transfer)(mp_obj_base_t *obj, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags);
int (*transfer_single)(mp_obj_base_t *obj, uint16_t addr, size_t len, uint8_t *buf, unsigned int flags);
} mp_machine_i2c_p_t;
typedef struct _mp_machine_soft_i2c_obj_t {
mp_obj_base_t base;
uint32_t us_delay;
uint32_t us_timeout;
mp_hal_pin_obj_t scl;
mp_hal_pin_obj_t sda;
} mp_machine_soft_i2c_obj_t;
extern const mp_obj_type_t machine_i2c_type;
extern const mp_obj_dict_t mp_machine_soft_i2c_locals_dict;
int mp_machine_i2c_transfer_adaptor(mp_obj_base_t *self, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags);
int mp_machine_soft_i2c_transfer(mp_obj_base_t *self, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags);
#endif // MICROPY_INCLUDED_EXTMOD_MACHINE_I2C_H
@@ -0,0 +1,103 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2013, 2014 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/runtime.h"
#include "extmod/machine_mem.h"
#if MICROPY_PY_MACHINE
// If you wish to override the functions for mapping the machine_mem read/write
// address, then add a #define for MICROPY_MACHINE_MEM_GET_READ_ADDR and/or
// MICROPY_MACHINE_MEM_GET_WRITE_ADDR in your mpconfigport.h. Since the
// prototypes are identical, it is allowable for both of the macros to evaluate
// the to same function.
//
// It is expected that the modmachine.c file for a given port will provide the
// implementations, if the default implementation isn't used.
#if !defined(MICROPY_MACHINE_MEM_GET_READ_ADDR) || !defined(MICROPY_MACHINE_MEM_GET_WRITE_ADDR)
STATIC uintptr_t machine_mem_get_addr(mp_obj_t addr_o, uint align) {
uintptr_t addr = mp_obj_int_get_truncated(addr_o);
if ((addr & (align - 1)) != 0) {
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "address %08x is not aligned to %d bytes", addr, align));
}
return addr;
}
#if !defined(MICROPY_MACHINE_MEM_GET_READ_ADDR)
#define MICROPY_MACHINE_MEM_GET_READ_ADDR machine_mem_get_addr
#endif
#if !defined(MICROPY_MACHINE_MEM_GET_WRITE_ADDR)
#define MICROPY_MACHINE_MEM_GET_WRITE_ADDR machine_mem_get_addr
#endif
#endif
STATIC void machine_mem_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
(void)kind;
machine_mem_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_printf(print, "<%u-bit memory>", 8 * self->elem_size);
}
STATIC mp_obj_t machine_mem_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
// TODO support slice index to read/write multiple values at once
machine_mem_obj_t *self = MP_OBJ_TO_PTR(self_in);
if (value == MP_OBJ_NULL) {
// delete
return MP_OBJ_NULL; // op not supported
} else if (value == MP_OBJ_SENTINEL) {
// load
uintptr_t addr = MICROPY_MACHINE_MEM_GET_READ_ADDR(index, self->elem_size);
uint32_t val;
switch (self->elem_size) {
case 1: val = (*(uint8_t*)addr); break;
case 2: val = (*(uint16_t*)addr); break;
default: val = (*(uint32_t*)addr); break;
}
return mp_obj_new_int(val);
} else {
// store
uintptr_t addr = MICROPY_MACHINE_MEM_GET_WRITE_ADDR(index, self->elem_size);
uint32_t val = mp_obj_get_int_truncated(value);
switch (self->elem_size) {
case 1: (*(uint8_t*)addr) = val; break;
case 2: (*(uint16_t*)addr) = val; break;
default: (*(uint32_t*)addr) = val; break;
}
return mp_const_none;
}
}
const mp_obj_type_t machine_mem_type = {
{ &mp_type_type },
.name = MP_QSTR_mem,
.print = machine_mem_print,
.subscr = machine_mem_subscr,
};
const machine_mem_obj_t machine_mem8_obj = {{&machine_mem_type}, 1};
const machine_mem_obj_t machine_mem16_obj = {{&machine_mem_type}, 2};
const machine_mem_obj_t machine_mem32_obj = {{&machine_mem_type}, 4};
#endif // MICROPY_PY_MACHINE
@@ -0,0 +1,49 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2015 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_EXTMOD_MACHINE_MEM_H
#define MICROPY_INCLUDED_EXTMOD_MACHINE_MEM_H
#include "py/obj.h"
typedef struct _machine_mem_obj_t {
mp_obj_base_t base;
unsigned elem_size; // in bytes
} machine_mem_obj_t;
extern const mp_obj_type_t machine_mem_type;
extern const machine_mem_obj_t machine_mem8_obj;
extern const machine_mem_obj_t machine_mem16_obj;
extern const machine_mem_obj_t machine_mem32_obj;
#if defined(MICROPY_MACHINE_MEM_GET_READ_ADDR)
uintptr_t MICROPY_MACHINE_MEM_GET_READ_ADDR(mp_obj_t addr_o, uint align);
#endif
#if defined(MICROPY_MACHINE_MEM_GET_WRITE_ADDR)
uintptr_t MICROPY_MACHINE_MEM_GET_WRITE_ADDR(mp_obj_t addr_o, uint align);
#endif
#endif // MICROPY_INCLUDED_EXTMOD_MACHINE_MEM_H
@@ -0,0 +1,87 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Paul Sokolovsky
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/mpconfig.h"
#if MICROPY_PY_MACHINE
#include "py/obj.h"
#include "py/runtime.h"
#include "extmod/virtpin.h"
#include "extmod/machine_pinbase.h"
// PinBase class
// As this is abstract class, its instance is null.
// But there should be an instance, as the rest of instance code
// expects that there will be concrete object for inheritance.
typedef struct _mp_pinbase_t {
mp_obj_base_t base;
} mp_pinbase_t;
STATIC const mp_pinbase_t pinbase_singleton = {
.base = { &machine_pinbase_type },
};
STATIC mp_obj_t pinbase_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
(void)type;
(void)n_args;
(void)n_kw;
(void)args;
return MP_OBJ_FROM_PTR(&pinbase_singleton);
}
mp_uint_t pinbase_ioctl(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode);
mp_uint_t pinbase_ioctl(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode) {
(void)errcode;
switch (request) {
case MP_PIN_READ: {
mp_obj_t dest[2];
mp_load_method(obj, MP_QSTR_value, dest);
return mp_obj_get_int(mp_call_method_n_kw(0, 0, dest));
}
case MP_PIN_WRITE: {
mp_obj_t dest[3];
mp_load_method(obj, MP_QSTR_value, dest);
dest[2] = (arg == 0 ? mp_const_false : mp_const_true);
mp_call_method_n_kw(1, 0, dest);
return 0;
}
}
return -1;
}
STATIC const mp_pin_p_t pinbase_pin_p = {
.ioctl = pinbase_ioctl,
};
const mp_obj_type_t machine_pinbase_type = {
{ &mp_type_type },
.name = MP_QSTR_PinBase,
.make_new = pinbase_make_new,
.protocol = &pinbase_pin_p,
};
#endif // MICROPY_PY_MACHINE
@@ -0,0 +1,33 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Paul Sokolovsky
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_EXTMOD_MACHINE_PINBASE_H
#define MICROPY_INCLUDED_EXTMOD_MACHINE_PINBASE_H
#include "py/obj.h"
extern const mp_obj_type_t machine_pinbase_type;
#endif // MICROPY_INCLUDED_EXTMOD_MACHINE_PINBASE_H
@@ -0,0 +1,65 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/runtime.h"
#include "py/mperrno.h"
#include "extmod/machine_pulse.h"
#if MICROPY_PY_MACHINE_PULSE
MP_WEAK mp_uint_t machine_time_pulse_us(mp_hal_pin_obj_t pin, int pulse_level, mp_uint_t timeout_us) {
mp_uint_t start = mp_hal_ticks_us();
while (mp_hal_pin_read(pin) != pulse_level) {
if ((mp_uint_t)(mp_hal_ticks_us() - start) >= timeout_us) {
return (mp_uint_t)-2;
}
}
start = mp_hal_ticks_us();
while (mp_hal_pin_read(pin) == pulse_level) {
if ((mp_uint_t)(mp_hal_ticks_us() - start) >= timeout_us) {
return (mp_uint_t)-1;
}
}
return mp_hal_ticks_us() - start;
}
STATIC mp_obj_t machine_time_pulse_us_(size_t n_args, const mp_obj_t *args) {
mp_hal_pin_obj_t pin = mp_hal_get_pin_obj(args[0]);
int level = 0;
if (mp_obj_is_true(args[1])) {
level = 1;
}
mp_uint_t timeout_us = 1000000;
if (n_args > 2) {
timeout_us = mp_obj_get_int(args[2]);
}
mp_uint_t us = machine_time_pulse_us(pin, level, timeout_us);
// May return -1 or -2 in case of timeout
return mp_obj_new_int(us);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_time_pulse_us_obj, 2, 3, machine_time_pulse_us_);
#endif
@@ -0,0 +1,36 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_EXTMOD_MACHINE_PULSE_H
#define MICROPY_INCLUDED_EXTMOD_MACHINE_PULSE_H
#include "py/obj.h"
#include "py/mphal.h"
mp_uint_t machine_time_pulse_us(mp_hal_pin_obj_t pin, int pulse_level, mp_uint_t timeout_us);
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(machine_time_pulse_us_obj);
#endif // MICROPY_INCLUDED_EXTMOD_MACHINE_PULSE_H
@@ -0,0 +1,186 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2017 Paul Sokolovsky
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/mpconfig.h"
#if MICROPY_PY_MACHINE
#include <string.h>
#include "py/obj.h"
#include "py/runtime.h"
#include "extmod/virtpin.h"
#include "extmod/machine_signal.h"
// Signal class
typedef struct _machine_signal_t {
mp_obj_base_t base;
mp_obj_t pin;
bool invert;
} machine_signal_t;
STATIC mp_obj_t signal_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_obj_t pin;
bool invert = false;
#if defined(MICROPY_PY_MACHINE_PIN_MAKE_NEW)
mp_pin_p_t *pin_p = NULL;
if (n_args > 0 && mp_obj_is_obj(args[0])) {
mp_obj_base_t *pin_base = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]);
pin_p = (mp_pin_p_t*)pin_base->type->protocol;
}
if (pin_p == NULL) {
// If first argument isn't a Pin-like object, we filter out "invert"
// from keyword arguments and pass them all to the exported Pin
// constructor to create one.
mp_obj_t *pin_args = mp_local_alloc((n_args + n_kw * 2) * sizeof(mp_obj_t));
memcpy(pin_args, args, n_args * sizeof(mp_obj_t));
const mp_obj_t *src = args + n_args;
mp_obj_t *dst = pin_args + n_args;
mp_obj_t *sig_value = NULL;
for (size_t cnt = n_kw; cnt; cnt--) {
if (*src == MP_OBJ_NEW_QSTR(MP_QSTR_invert)) {
invert = mp_obj_is_true(src[1]);
n_kw--;
} else {
*dst++ = *src;
*dst++ = src[1];
}
if (*src == MP_OBJ_NEW_QSTR(MP_QSTR_value)) {
// Value is pertained to Signal, so we should invert
// it for Pin if needed, and we should do it only when
// inversion status is guaranteedly known.
sig_value = dst - 1;
}
src += 2;
}
if (invert && sig_value != NULL) {
*sig_value = mp_obj_is_true(*sig_value) ? MP_OBJ_NEW_SMALL_INT(0) : MP_OBJ_NEW_SMALL_INT(1);
}
// Here we pass NULL as a type, hoping that mp_pin_make_new()
// will just ignore it as set a concrete type. If not, we'd need
// to expose port's "default" pin type too.
pin = MICROPY_PY_MACHINE_PIN_MAKE_NEW(NULL, n_args, n_kw, pin_args);
mp_local_free(pin_args);
}
else
#endif
// Otherwise there should be 1 or 2 args
{
if (n_args == 1) {
pin = args[0];
if (n_kw == 0) {
} else if (n_kw == 1 && args[1] == MP_OBJ_NEW_QSTR(MP_QSTR_invert)) {
invert = mp_obj_is_true(args[2]);
} else {
goto error;
}
} else {
error:
mp_raise_TypeError(NULL);
}
}
machine_signal_t *o = m_new_obj(machine_signal_t);
o->base.type = type;
o->pin = pin;
o->invert = invert;
return MP_OBJ_FROM_PTR(o);
}
STATIC mp_uint_t signal_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) {
(void)errcode;
machine_signal_t *self = MP_OBJ_TO_PTR(self_in);
switch (request) {
case MP_PIN_READ: {
return mp_virtual_pin_read(self->pin) ^ self->invert;
}
case MP_PIN_WRITE: {
mp_virtual_pin_write(self->pin, arg ^ self->invert);
return 0;
}
}
return -1;
}
// fast method for getting/setting signal value
STATIC mp_obj_t signal_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 0, 1, false);
if (n_args == 0) {
// get pin
return MP_OBJ_NEW_SMALL_INT(mp_virtual_pin_read(self_in));
} else {
// set pin
mp_virtual_pin_write(self_in, mp_obj_is_true(args[0]));
return mp_const_none;
}
}
STATIC mp_obj_t signal_value(size_t n_args, const mp_obj_t *args) {
return signal_call(args[0], n_args - 1, 0, args + 1);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(signal_value_obj, 1, 2, signal_value);
STATIC mp_obj_t signal_on(mp_obj_t self_in) {
mp_virtual_pin_write(self_in, 1);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(signal_on_obj, signal_on);
STATIC mp_obj_t signal_off(mp_obj_t self_in) {
mp_virtual_pin_write(self_in, 0);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(signal_off_obj, signal_off);
STATIC const mp_rom_map_elem_t signal_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&signal_value_obj) },
{ MP_ROM_QSTR(MP_QSTR_on), MP_ROM_PTR(&signal_on_obj) },
{ MP_ROM_QSTR(MP_QSTR_off), MP_ROM_PTR(&signal_off_obj) },
};
STATIC MP_DEFINE_CONST_DICT(signal_locals_dict, signal_locals_dict_table);
STATIC const mp_pin_p_t signal_pin_p = {
.ioctl = signal_ioctl,
};
const mp_obj_type_t machine_signal_type = {
{ &mp_type_type },
.name = MP_QSTR_Signal,
.make_new = signal_make_new,
.call = signal_call,
.protocol = &signal_pin_p,
.locals_dict = (void*)&signal_locals_dict,
};
#endif // MICROPY_PY_MACHINE
@@ -0,0 +1,33 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2017 Paul Sokolovsky
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_EXTMOD_MACHINE_SIGNAL_H
#define MICROPY_INCLUDED_EXTMOD_MACHINE_SIGNAL_H
#include "py/obj.h"
extern const mp_obj_type_t machine_signal_type;
#endif // MICROPY_INCLUDED_EXTMOD_MACHINE_SIGNAL_H
@@ -0,0 +1,283 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdio.h>
#include <string.h>
#include "py/runtime.h"
#include "extmod/machine_spi.h"
#if MICROPY_PY_MACHINE_SPI
// if a port didn't define MSB/LSB constants then provide them
#ifndef MICROPY_PY_MACHINE_SPI_MSB
#define MICROPY_PY_MACHINE_SPI_MSB (0)
#define MICROPY_PY_MACHINE_SPI_LSB (1)
#endif
/******************************************************************************/
// MicroPython bindings for generic machine.SPI
STATIC mp_obj_t mp_machine_soft_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args);
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) {
// check the id argument, if given
if (n_args > 0) {
if (args[0] != MP_OBJ_NEW_SMALL_INT(-1)) {
#if defined(MICROPY_PY_MACHINE_SPI_MAKE_NEW)
// dispatch to port-specific constructor
extern mp_obj_t MICROPY_PY_MACHINE_SPI_MAKE_NEW(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args);
return MICROPY_PY_MACHINE_SPI_MAKE_NEW(type, n_args, n_kw, args);
#else
mp_raise_ValueError("invalid SPI peripheral");
#endif
}
--n_args;
++args;
}
// software SPI
return mp_machine_soft_spi_make_new(type, n_args, n_kw, args);
}
STATIC mp_obj_t machine_spi_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
mp_obj_base_t *s = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]);
mp_machine_spi_p_t *spi_p = (mp_machine_spi_p_t*)s->type->protocol;
spi_p->init(s, n_args - 1, args + 1, kw_args);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_spi_init_obj, 1, machine_spi_init);
STATIC mp_obj_t machine_spi_deinit(mp_obj_t self) {
mp_obj_base_t *s = (mp_obj_base_t*)MP_OBJ_TO_PTR(self);
mp_machine_spi_p_t *spi_p = (mp_machine_spi_p_t*)s->type->protocol;
if (spi_p->deinit != NULL) {
spi_p->deinit(s);
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_spi_deinit_obj, machine_spi_deinit);
STATIC void mp_machine_spi_transfer(mp_obj_t self, size_t len, const void *src, void *dest) {
mp_obj_base_t *s = (mp_obj_base_t*)MP_OBJ_TO_PTR(self);
mp_machine_spi_p_t *spi_p = (mp_machine_spi_p_t*)s->type->protocol;
spi_p->transfer(s, len, src, dest);
}
STATIC mp_obj_t mp_machine_spi_read(size_t n_args, const mp_obj_t *args) {
vstr_t vstr;
vstr_init_len(&vstr, mp_obj_get_int(args[1]));
memset(vstr.buf, n_args == 3 ? mp_obj_get_int(args[2]) : 0, vstr.len);
mp_machine_spi_transfer(args[0], vstr.len, vstr.buf, vstr.buf);
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_read_obj, 2, 3, mp_machine_spi_read);
STATIC mp_obj_t mp_machine_spi_readinto(size_t n_args, const mp_obj_t *args) {
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE);
memset(bufinfo.buf, n_args == 3 ? mp_obj_get_int(args[2]) : 0, bufinfo.len);
mp_machine_spi_transfer(args[0], bufinfo.len, bufinfo.buf, bufinfo.buf);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_readinto_obj, 2, 3, mp_machine_spi_readinto);
STATIC mp_obj_t mp_machine_spi_write(mp_obj_t self, mp_obj_t wr_buf) {
mp_buffer_info_t src;
mp_get_buffer_raise(wr_buf, &src, MP_BUFFER_READ);
mp_machine_spi_transfer(self, src.len, (const uint8_t*)src.buf, NULL);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_2(mp_machine_spi_write_obj, mp_machine_spi_write);
STATIC mp_obj_t mp_machine_spi_write_readinto(mp_obj_t self, mp_obj_t wr_buf, mp_obj_t rd_buf) {
mp_buffer_info_t src;
mp_get_buffer_raise(wr_buf, &src, MP_BUFFER_READ);
mp_buffer_info_t dest;
mp_get_buffer_raise(rd_buf, &dest, MP_BUFFER_WRITE);
if (src.len != dest.len) {
mp_raise_ValueError("buffers must be the same length");
}
mp_machine_spi_transfer(self, src.len, src.buf, dest.buf);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_3(mp_machine_spi_write_readinto_obj, mp_machine_spi_write_readinto);
STATIC const mp_rom_map_elem_t machine_spi_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_spi_init_obj) },
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_spi_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_machine_spi_read_obj) },
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_machine_spi_readinto_obj) },
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_machine_spi_write_obj) },
{ MP_ROM_QSTR(MP_QSTR_write_readinto), MP_ROM_PTR(&mp_machine_spi_write_readinto_obj) },
{ MP_ROM_QSTR(MP_QSTR_MSB), MP_ROM_INT(MICROPY_PY_MACHINE_SPI_MSB) },
{ MP_ROM_QSTR(MP_QSTR_LSB), MP_ROM_INT(MICROPY_PY_MACHINE_SPI_LSB) },
};
MP_DEFINE_CONST_DICT(mp_machine_spi_locals_dict, machine_spi_locals_dict_table);
/******************************************************************************/
// Implementation of soft SPI
STATIC uint32_t baudrate_from_delay_half(uint32_t delay_half) {
#ifdef MICROPY_HW_SOFTSPI_MIN_DELAY
if (delay_half == MICROPY_HW_SOFTSPI_MIN_DELAY) {
return MICROPY_HW_SOFTSPI_MAX_BAUDRATE;
} else
#endif
{
return 500000 / delay_half;
}
}
STATIC uint32_t baudrate_to_delay_half(uint32_t baudrate) {
#ifdef MICROPY_HW_SOFTSPI_MIN_DELAY
if (baudrate >= MICROPY_HW_SOFTSPI_MAX_BAUDRATE) {
return MICROPY_HW_SOFTSPI_MIN_DELAY;
} else
#endif
{
uint32_t delay_half = 500000 / baudrate;
// round delay_half up so that: actual_baudrate <= requested_baudrate
if (500000 % baudrate != 0) {
delay_half += 1;
}
return delay_half;
}
}
STATIC void mp_machine_soft_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
mp_machine_soft_spi_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_printf(print, "SoftSPI(baudrate=%u, polarity=%u, phase=%u,"
" sck=" MP_HAL_PIN_FMT ", mosi=" MP_HAL_PIN_FMT ", miso=" MP_HAL_PIN_FMT ")",
baudrate_from_delay_half(self->spi.delay_half), self->spi.polarity, self->spi.phase,
mp_hal_pin_name(self->spi.sck), mp_hal_pin_name(self->spi.mosi), mp_hal_pin_name(self->spi.miso));
}
STATIC mp_obj_t mp_machine_soft_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
enum { ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_sck, ARG_mosi, ARG_miso };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 500000} },
{ MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} },
{ MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = MICROPY_PY_MACHINE_SPI_MSB} },
{ MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_miso, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
// create new object
mp_machine_soft_spi_obj_t *self = m_new_obj(mp_machine_soft_spi_obj_t);
self->base.type = &mp_machine_soft_spi_type;
// set parameters
self->spi.delay_half = baudrate_to_delay_half(args[ARG_baudrate].u_int);
self->spi.polarity = args[ARG_polarity].u_int;
self->spi.phase = args[ARG_phase].u_int;
if (args[ARG_bits].u_int != 8) {
mp_raise_ValueError("bits must be 8");
}
if (args[ARG_firstbit].u_int != MICROPY_PY_MACHINE_SPI_MSB) {
mp_raise_ValueError("firstbit must be MSB");
}
if (args[ARG_sck].u_obj == MP_OBJ_NULL
|| args[ARG_mosi].u_obj == MP_OBJ_NULL
|| args[ARG_miso].u_obj == MP_OBJ_NULL) {
mp_raise_ValueError("must specify all of sck/mosi/miso");
}
self->spi.sck = mp_hal_get_pin_obj(args[ARG_sck].u_obj);
self->spi.mosi = mp_hal_get_pin_obj(args[ARG_mosi].u_obj);
self->spi.miso = mp_hal_get_pin_obj(args[ARG_miso].u_obj);
// configure bus
mp_soft_spi_ioctl(&self->spi, MP_SPI_IOCTL_INIT);
return MP_OBJ_FROM_PTR(self);
}
STATIC void mp_machine_soft_spi_init(mp_obj_base_t *self_in, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
mp_machine_soft_spi_obj_t *self = (mp_machine_soft_spi_obj_t*)self_in;
enum { ARG_baudrate, ARG_polarity, ARG_phase, ARG_sck, ARG_mosi, ARG_miso };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_baudrate, MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_polarity, MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_phase, MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_miso, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if (args[ARG_baudrate].u_int != -1) {
self->spi.delay_half = baudrate_to_delay_half(args[ARG_baudrate].u_int);
}
if (args[ARG_polarity].u_int != -1) {
self->spi.polarity = args[ARG_polarity].u_int;
}
if (args[ARG_phase].u_int != -1) {
self->spi.phase = args[ARG_phase].u_int;
}
if (args[ARG_sck].u_obj != MP_OBJ_NULL) {
self->spi.sck = mp_hal_get_pin_obj(args[ARG_sck].u_obj);
}
if (args[ARG_mosi].u_obj != MP_OBJ_NULL) {
self->spi.mosi = mp_hal_get_pin_obj(args[ARG_mosi].u_obj);
}
if (args[ARG_miso].u_obj != MP_OBJ_NULL) {
self->spi.miso = mp_hal_get_pin_obj(args[ARG_miso].u_obj);
}
// configure bus
mp_soft_spi_ioctl(&self->spi, MP_SPI_IOCTL_INIT);
}
STATIC void mp_machine_soft_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) {
mp_machine_soft_spi_obj_t *self = (mp_machine_soft_spi_obj_t*)self_in;
mp_soft_spi_transfer(&self->spi, len, src, dest);
}
const mp_machine_spi_p_t mp_machine_soft_spi_p = {
.init = mp_machine_soft_spi_init,
.deinit = NULL,
.transfer = mp_machine_soft_spi_transfer,
};
const mp_obj_type_t mp_machine_soft_spi_type = {
{ &mp_type_type },
.name = MP_QSTR_SoftSPI,
.print = mp_machine_soft_spi_print,
.make_new = mp_machine_spi_make_new, // delegate to master constructor
.protocol = &mp_machine_soft_spi_p,
.locals_dict = (mp_obj_dict_t*)&mp_machine_spi_locals_dict,
};
#endif // MICROPY_PY_MACHINE_SPI
@@ -0,0 +1,56 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_EXTMOD_MACHINE_SPI_H
#define MICROPY_INCLUDED_EXTMOD_MACHINE_SPI_H
#include "py/obj.h"
#include "py/mphal.h"
#include "drivers/bus/spi.h"
// SPI protocol
typedef struct _mp_machine_spi_p_t {
void (*init)(mp_obj_base_t *obj, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
void (*deinit)(mp_obj_base_t *obj); // can be NULL
void (*transfer)(mp_obj_base_t *obj, size_t len, const uint8_t *src, uint8_t *dest);
} mp_machine_spi_p_t;
typedef struct _mp_machine_soft_spi_obj_t {
mp_obj_base_t base;
mp_soft_spi_obj_t spi;
} mp_machine_soft_spi_obj_t;
extern const mp_machine_spi_p_t mp_machine_soft_spi_p;
extern const mp_obj_type_t mp_machine_soft_spi_type;
extern const mp_obj_dict_t mp_machine_spi_locals_dict;
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);
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_read_obj);
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_readinto_obj);
MP_DECLARE_CONST_FUN_OBJ_2(mp_machine_spi_write_obj);
MP_DECLARE_CONST_FUN_OBJ_3(mp_machine_spi_write_readinto_obj);
#endif // MICROPY_INCLUDED_EXTMOD_MACHINE_SPI_H
@@ -0,0 +1,47 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2014-2016 Damien P. George
* Copyright (c) 2016 Paul Sokolovsky
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_EXTMOD_MISC_H
#define MICROPY_INCLUDED_EXTMOD_MISC_H
// This file contains cumulative declarations for extmod/ .
#include <stddef.h>
#include "py/runtime.h"
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_uos_dupterm_obj);
#if MICROPY_PY_OS_DUPTERM
bool mp_uos_dupterm_is_builtin_stream(mp_const_obj_t stream);
uintptr_t mp_uos_dupterm_poll(uintptr_t poll_flags);
int mp_uos_dupterm_rx_chr(void);
void mp_uos_dupterm_tx_strn(const char *str, size_t len);
void mp_uos_deactivate(size_t dupterm_idx, const char *msg, mp_obj_t exc);
#else
#define mp_uos_dupterm_tx_strn(s, l)
#endif
#endif // MICROPY_INCLUDED_EXTMOD_MISC_H
@@ -0,0 +1,383 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Paul Sokolovsky
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdio.h>
#include <string.h>
#include <errno.h> // for declaration of global errno variable
#include <fcntl.h>
#include "py/runtime.h"
#include "py/stream.h"
#if MICROPY_PY_BTREE
#include <db.h>
#include <../../btree/btree.h>
typedef struct _mp_obj_btree_t {
mp_obj_base_t base;
DB *db;
mp_obj_t start_key;
mp_obj_t end_key;
#define FLAG_END_KEY_INCL 1
#define FLAG_DESC 2
#define FLAG_ITER_TYPE_MASK 0xc0
#define FLAG_ITER_KEYS 0x40
#define FLAG_ITER_VALUES 0x80
#define FLAG_ITER_ITEMS 0xc0
byte flags;
byte next_flags;
} mp_obj_btree_t;
#if !MICROPY_ENABLE_DYNRUNTIME
STATIC const mp_obj_type_t btree_type;
#endif
#define CHECK_ERROR(res) \
if (res == RET_ERROR) { \
mp_raise_OSError(errno); \
}
void __dbpanic(DB *db) {
mp_printf(&mp_plat_print, "__dbpanic(%p)\n", db);
}
STATIC mp_obj_btree_t *btree_new(DB *db) {
mp_obj_btree_t *o = m_new_obj(mp_obj_btree_t);
o->base.type = &btree_type;
o->db = db;
o->start_key = mp_const_none;
o->end_key = mp_const_none;
o->next_flags = 0;
return o;
}
STATIC void btree_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
(void)kind;
mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in);
mp_printf(print, "<btree %p>", self->db);
}
STATIC mp_obj_t btree_flush(mp_obj_t self_in) {
mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in);
return MP_OBJ_NEW_SMALL_INT(__bt_sync(self->db, 0));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(btree_flush_obj, btree_flush);
STATIC mp_obj_t btree_close(mp_obj_t self_in) {
mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in);
return MP_OBJ_NEW_SMALL_INT(__bt_close(self->db));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(btree_close_obj, btree_close);
STATIC mp_obj_t btree_put(size_t n_args, const mp_obj_t *args) {
(void)n_args;
mp_obj_btree_t *self = MP_OBJ_TO_PTR(args[0]);
DBT key, val;
key.data = (void*)mp_obj_str_get_data(args[1], &key.size);
val.data = (void*)mp_obj_str_get_data(args[2], &val.size);
return MP_OBJ_NEW_SMALL_INT(__bt_put(self->db, &key, &val, 0));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(btree_put_obj, 3, 4, btree_put);
STATIC mp_obj_t btree_get(size_t n_args, const mp_obj_t *args) {
mp_obj_btree_t *self = MP_OBJ_TO_PTR(args[0]);
DBT key, val;
key.data = (void*)mp_obj_str_get_data(args[1], &key.size);
int res = __bt_get(self->db, &key, &val, 0);
if (res == RET_SPECIAL) {
if (n_args > 2) {
return args[2];
} else {
return mp_const_none;
}
}
CHECK_ERROR(res);
return mp_obj_new_bytes(val.data, val.size);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(btree_get_obj, 2, 3, btree_get);
STATIC mp_obj_t btree_seq(size_t n_args, const mp_obj_t *args) {
mp_obj_btree_t *self = MP_OBJ_TO_PTR(args[0]);
int flags = MP_OBJ_SMALL_INT_VALUE(args[1]);
DBT key, val;
if (n_args > 2) {
key.data = (void*)mp_obj_str_get_data(args[2], &key.size);
}
int res = __bt_seq(self->db, &key, &val, flags);
CHECK_ERROR(res);
if (res == RET_SPECIAL) {
return mp_const_none;
}
mp_obj_t pair_o = mp_obj_new_tuple(2, NULL);
mp_obj_tuple_t *pair = MP_OBJ_TO_PTR(pair_o);
pair->items[0] = mp_obj_new_bytes(key.data, key.size);
pair->items[1] = mp_obj_new_bytes(val.data, val.size);
return pair_o;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(btree_seq_obj, 2, 4, btree_seq);
STATIC mp_obj_t btree_init_iter(size_t n_args, const mp_obj_t *args, byte type) {
mp_obj_btree_t *self = MP_OBJ_TO_PTR(args[0]);
self->next_flags = type;
self->start_key = mp_const_none;
self->end_key = mp_const_none;
if (n_args > 1) {
self->start_key = args[1];
if (n_args > 2) {
self->end_key = args[2];
if (n_args > 3) {
self->next_flags = type | MP_OBJ_SMALL_INT_VALUE(args[3]);
}
}
}
return args[0];
}
STATIC mp_obj_t btree_keys(size_t n_args, const mp_obj_t *args) {
return btree_init_iter(n_args, args, FLAG_ITER_KEYS);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(btree_keys_obj, 1, 4, btree_keys);
STATIC mp_obj_t btree_values(size_t n_args, const mp_obj_t *args) {
return btree_init_iter(n_args, args, FLAG_ITER_VALUES);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(btree_values_obj, 1, 4, btree_values);
STATIC mp_obj_t btree_items(size_t n_args, const mp_obj_t *args) {
return btree_init_iter(n_args, args, FLAG_ITER_ITEMS);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(btree_items_obj, 1, 4, btree_items);
STATIC mp_obj_t btree_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) {
(void)iter_buf;
mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in);
if (self->next_flags != 0) {
// If we're called immediately after keys(), values(), or items(),
// use their setup for iteration.
self->flags = self->next_flags;
self->next_flags = 0;
} else {
// Otherwise, iterate over all keys.
self->flags = FLAG_ITER_KEYS;
self->start_key = mp_const_none;
self->end_key = mp_const_none;
}
return self_in;
}
STATIC mp_obj_t btree_iternext(mp_obj_t self_in) {
mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in);
DBT key, val;
int res;
bool desc = self->flags & FLAG_DESC;
if (self->start_key != MP_OBJ_NULL) {
int flags = R_FIRST;
if (self->start_key != mp_const_none) {
key.data = (void*)mp_obj_str_get_data(self->start_key, &key.size);
flags = R_CURSOR;
} else if (desc) {
flags = R_LAST;
}
res = __bt_seq(self->db, &key, &val, flags);
self->start_key = MP_OBJ_NULL;
} else {
res = __bt_seq(self->db, &key, &val, desc ? R_PREV : R_NEXT);
}
if (res == RET_SPECIAL) {
return MP_OBJ_STOP_ITERATION;
}
CHECK_ERROR(res);
if (self->end_key != mp_const_none) {
DBT end_key;
end_key.data = (void*)mp_obj_str_get_data(self->end_key, &end_key.size);
BTREE *t = self->db->internal;
int cmp = t->bt_cmp(&key, &end_key);
if (desc) {
cmp = -cmp;
}
if (self->flags & FLAG_END_KEY_INCL) {
cmp--;
}
if (cmp >= 0) {
self->end_key = MP_OBJ_NULL;
return MP_OBJ_STOP_ITERATION;
}
}
switch (self->flags & FLAG_ITER_TYPE_MASK) {
case FLAG_ITER_KEYS:
return mp_obj_new_bytes(key.data, key.size);
case FLAG_ITER_VALUES:
return mp_obj_new_bytes(val.data, val.size);
default: {
mp_obj_t pair_o = mp_obj_new_tuple(2, NULL);
mp_obj_tuple_t *pair = MP_OBJ_TO_PTR(pair_o);
pair->items[0] = mp_obj_new_bytes(key.data, key.size);
pair->items[1] = mp_obj_new_bytes(val.data, val.size);
return pair_o;
}
}
}
STATIC mp_obj_t btree_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in);
if (value == MP_OBJ_NULL) {
// delete
DBT key;
key.data = (void*)mp_obj_str_get_data(index, &key.size);
int res = __bt_delete(self->db, &key, 0);
if (res == RET_SPECIAL) {
nlr_raise(mp_obj_new_exception(&mp_type_KeyError));
}
CHECK_ERROR(res);
return mp_const_none;
} else if (value == MP_OBJ_SENTINEL) {
// load
DBT key, val;
key.data = (void*)mp_obj_str_get_data(index, &key.size);
int res = __bt_get(self->db, &key, &val, 0);
if (res == RET_SPECIAL) {
nlr_raise(mp_obj_new_exception(&mp_type_KeyError));
}
CHECK_ERROR(res);
return mp_obj_new_bytes(val.data, val.size);
} else {
// store
DBT key, val;
key.data = (void*)mp_obj_str_get_data(index, &key.size);
val.data = (void*)mp_obj_str_get_data(value, &val.size);
int res = __bt_put(self->db, &key, &val, 0);
CHECK_ERROR(res);
return mp_const_none;
}
}
STATIC mp_obj_t btree_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
mp_obj_btree_t *self = MP_OBJ_TO_PTR(lhs_in);
switch (op) {
case MP_BINARY_OP_CONTAINS: {
DBT key, val;
key.data = (void*)mp_obj_str_get_data(rhs_in, &key.size);
int res = __bt_get(self->db, &key, &val, 0);
CHECK_ERROR(res);
return mp_obj_new_bool(res != RET_SPECIAL);
}
default:
// op not supported
return MP_OBJ_NULL;
}
}
#if !MICROPY_ENABLE_DYNRUNTIME
STATIC const mp_rom_map_elem_t btree_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&btree_close_obj) },
{ MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&btree_flush_obj) },
{ MP_ROM_QSTR(MP_QSTR_get), MP_ROM_PTR(&btree_get_obj) },
{ MP_ROM_QSTR(MP_QSTR_put), MP_ROM_PTR(&btree_put_obj) },
{ MP_ROM_QSTR(MP_QSTR_seq), MP_ROM_PTR(&btree_seq_obj) },
{ MP_ROM_QSTR(MP_QSTR_keys), MP_ROM_PTR(&btree_keys_obj) },
{ MP_ROM_QSTR(MP_QSTR_values), MP_ROM_PTR(&btree_values_obj) },
{ MP_ROM_QSTR(MP_QSTR_items), MP_ROM_PTR(&btree_items_obj) },
};
STATIC MP_DEFINE_CONST_DICT(btree_locals_dict, btree_locals_dict_table);
STATIC const mp_obj_type_t btree_type = {
{ &mp_type_type },
// Save on qstr's, reuse same as for module
.name = MP_QSTR_btree,
.print = btree_print,
.getiter = btree_getiter,
.iternext = btree_iternext,
.binary_op = btree_binary_op,
.subscr = btree_subscr,
.locals_dict = (void*)&btree_locals_dict,
};
#endif
STATIC const FILEVTABLE btree_stream_fvtable = {
mp_stream_posix_read,
mp_stream_posix_write,
mp_stream_posix_lseek,
mp_stream_posix_fsync
};
#if !MICROPY_ENABLE_DYNRUNTIME
STATIC mp_obj_t mod_btree_open(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_flags, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_cachesize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_pagesize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_minkeypage, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
};
// Make sure we got a stream object
mp_get_stream_raise(pos_args[0], MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL);
struct {
mp_arg_val_t flags;
mp_arg_val_t cachesize;
mp_arg_val_t pagesize;
mp_arg_val_t minkeypage;
} args;
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args,
MP_ARRAY_SIZE(allowed_args), allowed_args, (mp_arg_val_t*)&args);
BTREEINFO openinfo = {0};
openinfo.flags = args.flags.u_int;
openinfo.cachesize = args.cachesize.u_int;
openinfo.psize = args.pagesize.u_int;
openinfo.minkeypage = args.minkeypage.u_int;
DB *db = __bt_open(MP_OBJ_TO_PTR(pos_args[0]), &btree_stream_fvtable, &openinfo, /*dflags*/0);
if (db == NULL) {
mp_raise_OSError(errno);
}
return MP_OBJ_FROM_PTR(btree_new(db));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_btree_open_obj, 1, mod_btree_open);
STATIC const mp_rom_map_elem_t mp_module_btree_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_btree) },
{ MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mod_btree_open_obj) },
{ MP_ROM_QSTR(MP_QSTR_INCL), MP_ROM_INT(FLAG_END_KEY_INCL) },
{ MP_ROM_QSTR(MP_QSTR_DESC), MP_ROM_INT(FLAG_DESC) },
};
STATIC MP_DEFINE_CONST_DICT(mp_module_btree_globals, mp_module_btree_globals_table);
const mp_obj_module_t mp_module_btree = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_btree_globals,
};
#endif
#endif // MICROPY_PY_BTREE
@@ -0,0 +1,652 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdio.h>
#include <string.h>
#include "py/runtime.h"
#if MICROPY_PY_FRAMEBUF
#include "ports/stm32/font_petme128_8x8.h"
typedef struct _mp_obj_framebuf_t {
mp_obj_base_t base;
mp_obj_t buf_obj; // need to store this to prevent GC from reclaiming buf
void *buf;
uint16_t width, height, stride;
uint8_t format;
} mp_obj_framebuf_t;
typedef void (*setpixel_t)(const mp_obj_framebuf_t*, int, int, uint32_t);
typedef uint32_t (*getpixel_t)(const mp_obj_framebuf_t*, int, int);
typedef void (*fill_rect_t)(const mp_obj_framebuf_t *, int, int, int, int, uint32_t);
typedef struct _mp_framebuf_p_t {
setpixel_t setpixel;
getpixel_t getpixel;
fill_rect_t fill_rect;
} mp_framebuf_p_t;
// constants for formats
#define FRAMEBUF_MVLSB (0)
#define FRAMEBUF_RGB565 (1)
#define FRAMEBUF_GS2_HMSB (5)
#define FRAMEBUF_GS4_HMSB (2)
#define FRAMEBUF_GS8 (6)
#define FRAMEBUF_MHLSB (3)
#define FRAMEBUF_MHMSB (4)
// Functions for MHLSB and MHMSB
STATIC void mono_horiz_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) {
size_t index = (x + y * fb->stride) >> 3;
int offset = fb->format == FRAMEBUF_MHMSB ? x & 0x07 : 7 - (x & 0x07);
((uint8_t*)fb->buf)[index] = (((uint8_t*)fb->buf)[index] & ~(0x01 << offset)) | ((col != 0) << offset);
}
STATIC uint32_t mono_horiz_getpixel(const mp_obj_framebuf_t *fb, int x, int y) {
size_t index = (x + y * fb->stride) >> 3;
int offset = fb->format == FRAMEBUF_MHMSB ? x & 0x07 : 7 - (x & 0x07);
return (((uint8_t*)fb->buf)[index] >> (offset)) & 0x01;
}
STATIC void mono_horiz_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) {
int reverse = fb->format == FRAMEBUF_MHMSB;
int advance = fb->stride >> 3;
while (w--) {
uint8_t *b = &((uint8_t*)fb->buf)[(x >> 3) + y * advance];
int offset = reverse ? x & 7 : 7 - (x & 7);
for (int hh = h; hh; --hh) {
*b = (*b & ~(0x01 << offset)) | ((col != 0) << offset);
b += advance;
}
++x;
}
}
// Functions for MVLSB format
STATIC void mvlsb_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) {
size_t index = (y >> 3) * fb->stride + x;
uint8_t offset = y & 0x07;
((uint8_t*)fb->buf)[index] = (((uint8_t*)fb->buf)[index] & ~(0x01 << offset)) | ((col != 0) << offset);
}
STATIC uint32_t mvlsb_getpixel(const mp_obj_framebuf_t *fb, int x, int y) {
return (((uint8_t*)fb->buf)[(y >> 3) * fb->stride + x] >> (y & 0x07)) & 0x01;
}
STATIC void mvlsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) {
while (h--) {
uint8_t *b = &((uint8_t*)fb->buf)[(y >> 3) * fb->stride + x];
uint8_t offset = y & 0x07;
for (int ww = w; ww; --ww) {
*b = (*b & ~(0x01 << offset)) | ((col != 0) << offset);
++b;
}
++y;
}
}
// Functions for RGB565 format
STATIC void rgb565_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) {
((uint16_t*)fb->buf)[x + y * fb->stride] = col;
}
STATIC uint32_t rgb565_getpixel(const mp_obj_framebuf_t *fb, int x, int y) {
return ((uint16_t*)fb->buf)[x + y * fb->stride];
}
STATIC void rgb565_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) {
uint16_t *b = &((uint16_t*)fb->buf)[x + y * fb->stride];
while (h--) {
for (int ww = w; ww; --ww) {
*b++ = col;
}
b += fb->stride - w;
}
}
// Functions for GS2_HMSB format
STATIC void gs2_hmsb_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) {
uint8_t *pixel = &((uint8_t*)fb->buf)[(x + y * fb->stride) >> 2];
uint8_t shift = (x & 0x3) << 1;
uint8_t mask = 0x3 << shift;
uint8_t color = (col & 0x3) << shift;
*pixel = color | (*pixel & (~mask));
}
STATIC uint32_t gs2_hmsb_getpixel(const mp_obj_framebuf_t *fb, int x, int y) {
uint8_t pixel = ((uint8_t*)fb->buf)[(x + y * fb->stride) >> 2];
uint8_t shift = (x & 0x3) << 1;
return (pixel >> shift) & 0x3;
}
STATIC void gs2_hmsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) {
for (int xx=x; xx < x+w; xx++) {
for (int yy=y; yy < y+h; yy++) {
gs2_hmsb_setpixel(fb, xx, yy, col);
}
}
}
// Functions for GS4_HMSB format
STATIC void gs4_hmsb_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) {
uint8_t *pixel = &((uint8_t*)fb->buf)[(x + y * fb->stride) >> 1];
if (x % 2) {
*pixel = ((uint8_t)col & 0x0f) | (*pixel & 0xf0);
} else {
*pixel = ((uint8_t)col << 4) | (*pixel & 0x0f);
}
}
STATIC uint32_t gs4_hmsb_getpixel(const mp_obj_framebuf_t *fb, int x, int y) {
if (x % 2) {
return ((uint8_t*)fb->buf)[(x + y * fb->stride) >> 1] & 0x0f;
}
return ((uint8_t*)fb->buf)[(x + y * fb->stride) >> 1] >> 4;
}
STATIC void gs4_hmsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) {
col &= 0x0f;
uint8_t *pixel_pair = &((uint8_t*)fb->buf)[(x + y * fb->stride) >> 1];
uint8_t col_shifted_left = col << 4;
uint8_t col_pixel_pair = col_shifted_left | col;
int pixel_count_till_next_line = (fb->stride - w) >> 1;
bool odd_x = (x % 2 == 1);
while (h--) {
int ww = w;
if (odd_x && ww > 0) {
*pixel_pair = (*pixel_pair & 0xf0) | col;
pixel_pair++;
ww--;
}
memset(pixel_pair, col_pixel_pair, ww >> 1);
pixel_pair += ww >> 1;
if (ww % 2) {
*pixel_pair = col_shifted_left | (*pixel_pair & 0x0f);
if (!odd_x) {
pixel_pair++;
}
}
pixel_pair += pixel_count_till_next_line;
}
}
// Functions for GS8 format
STATIC void gs8_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) {
uint8_t *pixel = &((uint8_t*)fb->buf)[(x + y * fb->stride)];
*pixel = col & 0xff;
}
STATIC uint32_t gs8_getpixel(const mp_obj_framebuf_t *fb, int x, int y) {
return ((uint8_t*)fb->buf)[(x + y * fb->stride)];
}
STATIC void gs8_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) {
uint8_t *pixel = &((uint8_t*)fb->buf)[(x + y * fb->stride)];
while (h--) {
memset(pixel, col, w);
pixel += fb->stride;
}
}
STATIC mp_framebuf_p_t formats[] = {
[FRAMEBUF_MVLSB] = {mvlsb_setpixel, mvlsb_getpixel, mvlsb_fill_rect},
[FRAMEBUF_RGB565] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect},
[FRAMEBUF_GS2_HMSB] = {gs2_hmsb_setpixel, gs2_hmsb_getpixel, gs2_hmsb_fill_rect},
[FRAMEBUF_GS4_HMSB] = {gs4_hmsb_setpixel, gs4_hmsb_getpixel, gs4_hmsb_fill_rect},
[FRAMEBUF_GS8] = {gs8_setpixel, gs8_getpixel, gs8_fill_rect},
[FRAMEBUF_MHLSB] = {mono_horiz_setpixel, mono_horiz_getpixel, mono_horiz_fill_rect},
[FRAMEBUF_MHMSB] = {mono_horiz_setpixel, mono_horiz_getpixel, mono_horiz_fill_rect},
};
static inline void setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) {
formats[fb->format].setpixel(fb, x, y, col);
}
static inline uint32_t getpixel(const mp_obj_framebuf_t *fb, int x, int y) {
return formats[fb->format].getpixel(fb, x, y);
}
STATIC void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) {
if (h < 1 || w < 1 || x + w <= 0 || y + h <= 0 || y >= fb->height || x >= fb->width) {
// No operation needed.
return;
}
// clip to the framebuffer
int xend = MIN(fb->width, x + w);
int yend = MIN(fb->height, y + h);
x = MAX(x, 0);
y = MAX(y, 0);
formats[fb->format].fill_rect(fb, x, y, xend - x, yend - y, col);
}
STATIC mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 4, 5, false);
mp_obj_framebuf_t *o = m_new_obj(mp_obj_framebuf_t);
o->base.type = type;
o->buf_obj = args[0];
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_WRITE);
o->buf = bufinfo.buf;
o->width = mp_obj_get_int(args[1]);
o->height = mp_obj_get_int(args[2]);
o->format = mp_obj_get_int(args[3]);
if (n_args >= 5) {
o->stride = mp_obj_get_int(args[4]);
} else {
o->stride = o->width;
}
switch (o->format) {
case FRAMEBUF_MVLSB:
case FRAMEBUF_RGB565:
break;
case FRAMEBUF_MHLSB:
case FRAMEBUF_MHMSB:
o->stride = (o->stride + 7) & ~7;
break;
case FRAMEBUF_GS2_HMSB:
o->stride = (o->stride + 3) & ~3;
break;
case FRAMEBUF_GS4_HMSB:
o->stride = (o->stride + 1) & ~1;
break;
case FRAMEBUF_GS8:
break;
default:
mp_raise_ValueError("invalid format");
}
return MP_OBJ_FROM_PTR(o);
}
STATIC mp_int_t framebuf_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) {
(void)flags;
mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in);
bufinfo->buf = self->buf;
bufinfo->len = self->stride * self->height * (self->format == FRAMEBUF_RGB565 ? 2 : 1);
bufinfo->typecode = 'B'; // view framebuf as bytes
return 0;
}
STATIC mp_obj_t framebuf_fill(mp_obj_t self_in, mp_obj_t col_in) {
mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in);
mp_int_t col = mp_obj_get_int(col_in);
formats[self->format].fill_rect(self, 0, 0, self->width, self->height, col);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(framebuf_fill_obj, framebuf_fill);
STATIC mp_obj_t framebuf_fill_rect(size_t n_args, const mp_obj_t *args) {
(void)n_args;
mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
mp_int_t x = mp_obj_get_int(args[1]);
mp_int_t y = mp_obj_get_int(args[2]);
mp_int_t width = mp_obj_get_int(args[3]);
mp_int_t height = mp_obj_get_int(args[4]);
mp_int_t col = mp_obj_get_int(args[5]);
fill_rect(self, x, y, width, height, col);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_fill_rect_obj, 6, 6, framebuf_fill_rect);
STATIC mp_obj_t framebuf_pixel(size_t n_args, const mp_obj_t *args) {
mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
mp_int_t x = mp_obj_get_int(args[1]);
mp_int_t y = mp_obj_get_int(args[2]);
if (0 <= x && x < self->width && 0 <= y && y < self->height) {
if (n_args == 3) {
// get
return MP_OBJ_NEW_SMALL_INT(getpixel(self, x, y));
} else {
// set
setpixel(self, x, y, mp_obj_get_int(args[3]));
}
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_pixel_obj, 3, 4, framebuf_pixel);
STATIC mp_obj_t framebuf_hline(size_t n_args, const mp_obj_t *args) {
(void)n_args;
mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
mp_int_t x = mp_obj_get_int(args[1]);
mp_int_t y = mp_obj_get_int(args[2]);
mp_int_t w = mp_obj_get_int(args[3]);
mp_int_t col = mp_obj_get_int(args[4]);
fill_rect(self, x, y, w, 1, col);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_hline_obj, 5, 5, framebuf_hline);
STATIC mp_obj_t framebuf_vline(size_t n_args, const mp_obj_t *args) {
(void)n_args;
mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
mp_int_t x = mp_obj_get_int(args[1]);
mp_int_t y = mp_obj_get_int(args[2]);
mp_int_t h = mp_obj_get_int(args[3]);
mp_int_t col = mp_obj_get_int(args[4]);
fill_rect(self, x, y, 1, h, col);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_vline_obj, 5, 5, framebuf_vline);
STATIC mp_obj_t framebuf_rect(size_t n_args, const mp_obj_t *args) {
(void)n_args;
mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
mp_int_t x = mp_obj_get_int(args[1]);
mp_int_t y = mp_obj_get_int(args[2]);
mp_int_t w = mp_obj_get_int(args[3]);
mp_int_t h = mp_obj_get_int(args[4]);
mp_int_t col = mp_obj_get_int(args[5]);
fill_rect(self, x, y, w, 1, col);
fill_rect(self, x, y + h- 1, w, 1, col);
fill_rect(self, x, y, 1, h, col);
fill_rect(self, x + w- 1, y, 1, h, col);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_rect_obj, 6, 6, framebuf_rect);
STATIC mp_obj_t framebuf_line(size_t n_args, const mp_obj_t *args) {
(void)n_args;
mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
mp_int_t x1 = mp_obj_get_int(args[1]);
mp_int_t y1 = mp_obj_get_int(args[2]);
mp_int_t x2 = mp_obj_get_int(args[3]);
mp_int_t y2 = mp_obj_get_int(args[4]);
mp_int_t col = mp_obj_get_int(args[5]);
mp_int_t dx = x2 - x1;
mp_int_t sx;
if (dx > 0) {
sx = 1;
} else {
dx = -dx;
sx = -1;
}
mp_int_t dy = y2 - y1;
mp_int_t sy;
if (dy > 0) {
sy = 1;
} else {
dy = -dy;
sy = -1;
}
bool steep;
if (dy > dx) {
mp_int_t temp;
temp = x1; x1 = y1; y1 = temp;
temp = dx; dx = dy; dy = temp;
temp = sx; sx = sy; sy = temp;
steep = true;
} else {
steep = false;
}
mp_int_t e = 2 * dy - dx;
for (mp_int_t i = 0; i < dx; ++i) {
if (steep) {
if (0 <= y1 && y1 < self->width && 0 <= x1 && x1 < self->height) {
setpixel(self, y1, x1, col);
}
} else {
if (0 <= x1 && x1 < self->width && 0 <= y1 && y1 < self->height) {
setpixel(self, x1, y1, col);
}
}
while (e >= 0) {
y1 += sy;
e -= 2 * dx;
}
x1 += sx;
e += 2 * dy;
}
if (0 <= x2 && x2 < self->width && 0 <= y2 && y2 < self->height) {
setpixel(self, x2, y2, col);
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_line_obj, 6, 6, framebuf_line);
STATIC mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args) {
mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
mp_obj_framebuf_t *source = MP_OBJ_TO_PTR(args[1]);
mp_int_t x = mp_obj_get_int(args[2]);
mp_int_t y = mp_obj_get_int(args[3]);
mp_int_t key = -1;
if (n_args > 4) {
key = mp_obj_get_int(args[4]);
}
if (
(x >= self->width) ||
(y >= self->height) ||
(-x >= source->width) ||
(-y >= source->height)
) {
// Out of bounds, no-op.
return mp_const_none;
}
// Clip.
int x0 = MAX(0, x);
int y0 = MAX(0, y);
int x1 = MAX(0, -x);
int y1 = MAX(0, -y);
int x0end = MIN(self->width, x + source->width);
int y0end = MIN(self->height, y + source->height);
for (; y0 < y0end; ++y0) {
int cx1 = x1;
for (int cx0 = x0; cx0 < x0end; ++cx0) {
uint32_t col = getpixel(source, cx1, y1);
if (col != (uint32_t)key) {
setpixel(self, cx0, y0, col);
}
++cx1;
}
++y1;
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_blit_obj, 4, 5, framebuf_blit);
STATIC mp_obj_t framebuf_scroll(mp_obj_t self_in, mp_obj_t xstep_in, mp_obj_t ystep_in) {
mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in);
mp_int_t xstep = mp_obj_get_int(xstep_in);
mp_int_t ystep = mp_obj_get_int(ystep_in);
int sx, y, xend, yend, dx, dy;
if (xstep < 0) {
sx = 0;
xend = self->width + xstep;
dx = 1;
} else {
sx = self->width - 1;
xend = xstep - 1;
dx = -1;
}
if (ystep < 0) {
y = 0;
yend = self->height + ystep;
dy = 1;
} else {
y = self->height - 1;
yend = ystep - 1;
dy = -1;
}
for (; y != yend; y += dy) {
for (int x = sx; x != xend; x += dx) {
setpixel(self, x, y, getpixel(self, x - xstep, y - ystep));
}
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_3(framebuf_scroll_obj, framebuf_scroll);
STATIC mp_obj_t framebuf_text(size_t n_args, const mp_obj_t *args) {
// extract arguments
mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
const char *str = mp_obj_str_get_str(args[1]);
mp_int_t x0 = mp_obj_get_int(args[2]);
mp_int_t y0 = mp_obj_get_int(args[3]);
mp_int_t col = 1;
if (n_args >= 5) {
col = mp_obj_get_int(args[4]);
}
// loop over chars
for (; *str; ++str) {
// get char and make sure its in range of font
int chr = *(uint8_t*)str;
if (chr < 32 || chr > 127) {
chr = 127;
}
// get char data
const uint8_t *chr_data = &font_petme128_8x8[(chr - 32) * 8];
// loop over char data
for (int j = 0; j < 8; j++, x0++) {
if (0 <= x0 && x0 < self->width) { // clip x
uint vline_data = chr_data[j]; // each byte is a column of 8 pixels, LSB at top
for (int y = y0; vline_data; vline_data >>= 1, y++) { // scan over vertical column
if (vline_data & 1) { // only draw if pixel set
if (0 <= y && y < self->height) { // clip y
setpixel(self, x0, y, col);
}
}
}
}
}
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_text_obj, 4, 5, framebuf_text);
#if !MICROPY_ENABLE_DYNRUNTIME
STATIC const mp_rom_map_elem_t framebuf_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_fill), MP_ROM_PTR(&framebuf_fill_obj) },
{ MP_ROM_QSTR(MP_QSTR_fill_rect), MP_ROM_PTR(&framebuf_fill_rect_obj) },
{ MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&framebuf_pixel_obj) },
{ MP_ROM_QSTR(MP_QSTR_hline), MP_ROM_PTR(&framebuf_hline_obj) },
{ MP_ROM_QSTR(MP_QSTR_vline), MP_ROM_PTR(&framebuf_vline_obj) },
{ MP_ROM_QSTR(MP_QSTR_rect), MP_ROM_PTR(&framebuf_rect_obj) },
{ MP_ROM_QSTR(MP_QSTR_line), MP_ROM_PTR(&framebuf_line_obj) },
{ MP_ROM_QSTR(MP_QSTR_blit), MP_ROM_PTR(&framebuf_blit_obj) },
{ MP_ROM_QSTR(MP_QSTR_scroll), MP_ROM_PTR(&framebuf_scroll_obj) },
{ MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&framebuf_text_obj) },
};
STATIC MP_DEFINE_CONST_DICT(framebuf_locals_dict, framebuf_locals_dict_table);
STATIC const mp_obj_type_t mp_type_framebuf = {
{ &mp_type_type },
.name = MP_QSTR_FrameBuffer,
.make_new = framebuf_make_new,
.buffer_p = { .get_buffer = framebuf_get_buffer },
.locals_dict = (mp_obj_dict_t*)&framebuf_locals_dict,
};
#endif
// this factory function is provided for backwards compatibility with old FrameBuffer1 class
STATIC mp_obj_t legacy_framebuffer1(size_t n_args, const mp_obj_t *args) {
mp_obj_framebuf_t *o = m_new_obj(mp_obj_framebuf_t);
o->base.type = &mp_type_framebuf;
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_WRITE);
o->buf = bufinfo.buf;
o->width = mp_obj_get_int(args[1]);
o->height = mp_obj_get_int(args[2]);
o->format = FRAMEBUF_MVLSB;
if (n_args >= 4) {
o->stride = mp_obj_get_int(args[3]);
} else {
o->stride = o->width;
}
return MP_OBJ_FROM_PTR(o);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(legacy_framebuffer1_obj, 3, 4, legacy_framebuffer1);
#if !MICROPY_ENABLE_DYNRUNTIME
STATIC const mp_rom_map_elem_t framebuf_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_framebuf) },
{ MP_ROM_QSTR(MP_QSTR_FrameBuffer), MP_ROM_PTR(&mp_type_framebuf) },
{ MP_ROM_QSTR(MP_QSTR_FrameBuffer1), MP_ROM_PTR(&legacy_framebuffer1_obj) },
{ MP_ROM_QSTR(MP_QSTR_MVLSB), MP_ROM_INT(FRAMEBUF_MVLSB) },
{ MP_ROM_QSTR(MP_QSTR_MONO_VLSB), MP_ROM_INT(FRAMEBUF_MVLSB) },
{ MP_ROM_QSTR(MP_QSTR_RGB565), MP_ROM_INT(FRAMEBUF_RGB565) },
{ MP_ROM_QSTR(MP_QSTR_GS2_HMSB), MP_ROM_INT(FRAMEBUF_GS2_HMSB) },
{ MP_ROM_QSTR(MP_QSTR_GS4_HMSB), MP_ROM_INT(FRAMEBUF_GS4_HMSB) },
{ MP_ROM_QSTR(MP_QSTR_GS8), MP_ROM_INT(FRAMEBUF_GS8) },
{ MP_ROM_QSTR(MP_QSTR_MONO_HLSB), MP_ROM_INT(FRAMEBUF_MHLSB) },
{ MP_ROM_QSTR(MP_QSTR_MONO_HMSB), MP_ROM_INT(FRAMEBUF_MHMSB) },
};
STATIC MP_DEFINE_CONST_DICT(framebuf_module_globals, framebuf_module_globals_table);
const mp_obj_module_t mp_module_framebuf = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&framebuf_module_globals,
};
#endif
#endif // MICROPY_PY_FRAMEBUF
@@ -0,0 +1,162 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2017 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include "py/obj.h"
#include "py/mphal.h"
/******************************************************************************/
// Low-level 1-Wire routines
#define TIMING_RESET1 (480)
#define TIMING_RESET2 (70)
#define TIMING_RESET3 (410)
#define TIMING_READ1 (5)
#define TIMING_READ2 (5)
#define TIMING_READ3 (40)
#define TIMING_WRITE1 (10)
#define TIMING_WRITE2 (50)
#define TIMING_WRITE3 (10)
STATIC int onewire_bus_reset(mp_hal_pin_obj_t pin) {
mp_hal_pin_write(pin, 0);
mp_hal_delay_us(TIMING_RESET1);
uint32_t i = mp_hal_quiet_timing_enter();
mp_hal_pin_write(pin, 1);
mp_hal_delay_us_fast(TIMING_RESET2);
int status = !mp_hal_pin_read(pin);
mp_hal_quiet_timing_exit(i);
mp_hal_delay_us(TIMING_RESET3);
return status;
}
STATIC int onewire_bus_readbit(mp_hal_pin_obj_t pin) {
mp_hal_pin_write(pin, 1);
uint32_t i = mp_hal_quiet_timing_enter();
mp_hal_pin_write(pin, 0);
mp_hal_delay_us_fast(TIMING_READ1);
mp_hal_pin_write(pin, 1);
mp_hal_delay_us_fast(TIMING_READ2);
int value = mp_hal_pin_read(pin);
mp_hal_quiet_timing_exit(i);
mp_hal_delay_us_fast(TIMING_READ3);
return value;
}
STATIC void onewire_bus_writebit(mp_hal_pin_obj_t pin, int value) {
uint32_t i = mp_hal_quiet_timing_enter();
mp_hal_pin_write(pin, 0);
mp_hal_delay_us_fast(TIMING_WRITE1);
if (value) {
mp_hal_pin_write(pin, 1);
}
mp_hal_delay_us_fast(TIMING_WRITE2);
mp_hal_pin_write(pin, 1);
mp_hal_delay_us_fast(TIMING_WRITE3);
mp_hal_quiet_timing_exit(i);
}
/******************************************************************************/
// MicroPython bindings
STATIC mp_obj_t onewire_reset(mp_obj_t pin_in) {
return mp_obj_new_bool(onewire_bus_reset(mp_hal_get_pin_obj(pin_in)));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(onewire_reset_obj, onewire_reset);
STATIC mp_obj_t onewire_readbit(mp_obj_t pin_in) {
return MP_OBJ_NEW_SMALL_INT(onewire_bus_readbit(mp_hal_get_pin_obj(pin_in)));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(onewire_readbit_obj, onewire_readbit);
STATIC mp_obj_t onewire_readbyte(mp_obj_t pin_in) {
mp_hal_pin_obj_t pin = mp_hal_get_pin_obj(pin_in);
uint8_t value = 0;
for (int i = 0; i < 8; ++i) {
value |= onewire_bus_readbit(pin) << i;
}
return MP_OBJ_NEW_SMALL_INT(value);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(onewire_readbyte_obj, onewire_readbyte);
STATIC mp_obj_t onewire_writebit(mp_obj_t pin_in, mp_obj_t value_in) {
onewire_bus_writebit(mp_hal_get_pin_obj(pin_in), mp_obj_get_int(value_in));
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(onewire_writebit_obj, onewire_writebit);
STATIC mp_obj_t onewire_writebyte(mp_obj_t pin_in, mp_obj_t value_in) {
mp_hal_pin_obj_t pin = mp_hal_get_pin_obj(pin_in);
int value = mp_obj_get_int(value_in);
for (int i = 0; i < 8; ++i) {
onewire_bus_writebit(pin, value & 1);
value >>= 1;
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(onewire_writebyte_obj, onewire_writebyte);
STATIC mp_obj_t onewire_crc8(mp_obj_t data) {
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ);
uint8_t crc = 0;
for (size_t i = 0; i < bufinfo.len; ++i) {
uint8_t byte = ((uint8_t*)bufinfo.buf)[i];
for (int b = 0; b < 8; ++b) {
uint8_t fb_bit = (crc ^ byte) & 0x01;
if (fb_bit == 0x01) {
crc = crc ^ 0x18;
}
crc = (crc >> 1) & 0x7f;
if (fb_bit == 0x01) {
crc = crc | 0x80;
}
byte = byte >> 1;
}
}
return MP_OBJ_NEW_SMALL_INT(crc);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(onewire_crc8_obj, onewire_crc8);
STATIC const mp_rom_map_elem_t onewire_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_onewire) },
{ MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&onewire_reset_obj) },
{ MP_ROM_QSTR(MP_QSTR_readbit), MP_ROM_PTR(&onewire_readbit_obj) },
{ MP_ROM_QSTR(MP_QSTR_readbyte), MP_ROM_PTR(&onewire_readbyte_obj) },
{ MP_ROM_QSTR(MP_QSTR_writebit), MP_ROM_PTR(&onewire_writebit_obj) },
{ MP_ROM_QSTR(MP_QSTR_writebyte), MP_ROM_PTR(&onewire_writebyte_obj) },
{ MP_ROM_QSTR(MP_QSTR_crc8), MP_ROM_PTR(&onewire_crc8_obj) },
};
STATIC MP_DEFINE_CONST_DICT(onewire_module_globals, onewire_module_globals_table);
const mp_obj_module_t mp_module_onewire = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&onewire_module_globals,
};
@@ -0,0 +1,253 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2014 Paul Sokolovsky
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include "py/runtime.h"
#include "py/binary.h"
#include "extmod/modubinascii.h"
mp_obj_t mod_binascii_hexlify(size_t n_args, const mp_obj_t *args) {
// Second argument is for an extension to allow a separator to be used
// between values.
const char *sep = NULL;
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ);
// Code below assumes non-zero buffer length when computing size with
// separator, so handle the zero-length case here.
if (bufinfo.len == 0) {
return mp_const_empty_bytes;
}
vstr_t vstr;
size_t out_len = bufinfo.len * 2;
if (n_args > 1) {
// 1-char separator between hex numbers
out_len += bufinfo.len - 1;
sep = mp_obj_str_get_str(args[1]);
}
vstr_init_len(&vstr, out_len);
byte *in = bufinfo.buf, *out = (byte*)vstr.buf;
for (mp_uint_t i = bufinfo.len; i--;) {
byte d = (*in >> 4);
if (d > 9) {
d += 'a' - '9' - 1;
}
*out++ = d + '0';
d = (*in++ & 0xf);
if (d > 9) {
d += 'a' - '9' - 1;
}
*out++ = d + '0';
if (sep != NULL && i != 0) {
*out++ = *sep;
}
}
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_binascii_hexlify_obj, 1, 2, mod_binascii_hexlify);
mp_obj_t mod_binascii_unhexlify(mp_obj_t data) {
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ);
if ((bufinfo.len & 1) != 0) {
mp_raise_ValueError("odd-length string");
}
vstr_t vstr;
vstr_init_len(&vstr, bufinfo.len / 2);
byte *in = bufinfo.buf, *out = (byte*)vstr.buf;
byte hex_byte = 0;
for (mp_uint_t i = bufinfo.len; i--;) {
byte hex_ch = *in++;
if (unichar_isxdigit(hex_ch)) {
hex_byte += unichar_xdigit_value(hex_ch);
} else {
mp_raise_ValueError("non-hex digit found");
}
if (i & 1) {
hex_byte <<= 4;
} else {
*out++ = hex_byte;
hex_byte = 0;
}
}
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
}
MP_DEFINE_CONST_FUN_OBJ_1(mod_binascii_unhexlify_obj, mod_binascii_unhexlify);
// If ch is a character in the base64 alphabet, and is not a pad character, then
// the corresponding integer between 0 and 63, inclusively, is returned.
// Otherwise, -1 is returned.
static int mod_binascii_sextet(byte ch) {
if (ch >= 'A' && ch <= 'Z') {
return ch - 'A';
} else if (ch >= 'a' && ch <= 'z') {
return ch - 'a' + 26;
} else if (ch >= '0' && ch <= '9') {
return ch - '0' + 52;
} else if (ch == '+') {
return 62;
} else if (ch == '/') {
return 63;
} else {
return -1;
}
}
mp_obj_t mod_binascii_a2b_base64(mp_obj_t data) {
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ);
byte *in = bufinfo.buf;
vstr_t vstr;
vstr_init(&vstr, (bufinfo.len / 4) * 3 + 1); // Potentially over-allocate
byte *out = (byte *)vstr.buf;
uint shift = 0;
int nbits = 0; // Number of meaningful bits in shift
bool hadpad = false; // Had a pad character since last valid character
for (size_t i = 0; i < bufinfo.len; i++) {
if (in[i] == '=') {
if ((nbits == 2) || ((nbits == 4) && hadpad)) {
nbits = 0;
break;
}
hadpad = true;
}
int sextet = mod_binascii_sextet(in[i]);
if (sextet == -1) {
continue;
}
hadpad = false;
shift = (shift << 6) | sextet;
nbits += 6;
if (nbits >= 8) {
nbits -= 8;
out[vstr.len++] = (shift >> nbits) & 0xFF;
}
}
if (nbits) {
mp_raise_ValueError("incorrect padding");
}
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
}
MP_DEFINE_CONST_FUN_OBJ_1(mod_binascii_a2b_base64_obj, mod_binascii_a2b_base64);
mp_obj_t mod_binascii_b2a_base64(mp_obj_t data) {
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ);
vstr_t vstr;
vstr_init_len(&vstr, ((bufinfo.len != 0) ? (((bufinfo.len - 1) / 3) + 1) * 4 : 0) + 1);
// First pass, we convert input buffer to numeric base 64 values
byte *in = bufinfo.buf, *out = (byte*)vstr.buf;
mp_uint_t i;
for (i = bufinfo.len; i >= 3; i -= 3) {
*out++ = (in[0] & 0xFC) >> 2;
*out++ = (in[0] & 0x03) << 4 | (in[1] & 0xF0) >> 4;
*out++ = (in[1] & 0x0F) << 2 | (in[2] & 0xC0) >> 6;
*out++ = in[2] & 0x3F;
in += 3;
}
if (i != 0) {
*out++ = (in[0] & 0xFC) >> 2;
if (i == 2) {
*out++ = (in[0] & 0x03) << 4 | (in[1] & 0xF0) >> 4;
*out++ = (in[1] & 0x0F) << 2;
}
else {
*out++ = (in[0] & 0x03) << 4;
*out++ = 64;
}
*out = 64;
}
// Second pass, we convert number base 64 values to actual base64 ascii encoding
out = (byte*)vstr.buf;
for (mp_uint_t j = vstr.len - 1; j--;) {
if (*out < 26) {
*out += 'A';
} else if (*out < 52) {
*out += 'a' - 26;
} else if (*out < 62) {
*out += '0' - 52;
} else if (*out == 62) {
*out ='+';
} else if (*out == 63) {
*out = '/';
} else {
*out = '=';
}
out++;
}
*out = '\n';
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
}
MP_DEFINE_CONST_FUN_OBJ_1(mod_binascii_b2a_base64_obj, mod_binascii_b2a_base64);
#if MICROPY_PY_UBINASCII_CRC32
#include "uzlib/tinf.h"
mp_obj_t mod_binascii_crc32(size_t n_args, const mp_obj_t *args) {
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ);
uint32_t crc = (n_args > 1) ? mp_obj_get_int_truncated(args[1]) : 0;
crc = uzlib_crc32(bufinfo.buf, bufinfo.len, crc ^ 0xffffffff);
return mp_obj_new_int_from_uint(crc ^ 0xffffffff);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_binascii_crc32_obj, 1, 2, mod_binascii_crc32);
#endif
#if MICROPY_PY_UBINASCII
STATIC const mp_rom_map_elem_t mp_module_binascii_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ubinascii) },
{ MP_ROM_QSTR(MP_QSTR_hexlify), MP_ROM_PTR(&mod_binascii_hexlify_obj) },
{ MP_ROM_QSTR(MP_QSTR_unhexlify), MP_ROM_PTR(&mod_binascii_unhexlify_obj) },
{ MP_ROM_QSTR(MP_QSTR_a2b_base64), MP_ROM_PTR(&mod_binascii_a2b_base64_obj) },
{ MP_ROM_QSTR(MP_QSTR_b2a_base64), MP_ROM_PTR(&mod_binascii_b2a_base64_obj) },
#if MICROPY_PY_UBINASCII_CRC32
{ MP_ROM_QSTR(MP_QSTR_crc32), MP_ROM_PTR(&mod_binascii_crc32_obj) },
#endif
};
STATIC MP_DEFINE_CONST_DICT(mp_module_binascii_globals, mp_module_binascii_globals_table);
const mp_obj_module_t mp_module_ubinascii = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_binascii_globals,
};
#endif //MICROPY_PY_UBINASCII
@@ -0,0 +1,41 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2014 Paul Sokolovsky
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_EXTMOD_MODUBINASCII_H
#define MICROPY_INCLUDED_EXTMOD_MODUBINASCII_H
extern mp_obj_t mod_binascii_hexlify(size_t n_args, const mp_obj_t *args);
extern mp_obj_t mod_binascii_unhexlify(mp_obj_t data);
extern mp_obj_t mod_binascii_a2b_base64(mp_obj_t data);
extern mp_obj_t mod_binascii_b2a_base64(mp_obj_t data);
extern mp_obj_t mod_binascii_crc32(size_t n_args, const mp_obj_t *args);
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mod_binascii_hexlify_obj);
MP_DECLARE_CONST_FUN_OBJ_1(mod_binascii_unhexlify_obj);
MP_DECLARE_CONST_FUN_OBJ_1(mod_binascii_a2b_base64_obj);
MP_DECLARE_CONST_FUN_OBJ_1(mod_binascii_b2a_base64_obj);
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mod_binascii_crc32_obj);
#endif // MICROPY_INCLUDED_EXTMOD_MODUBINASCII_H
@@ -0,0 +1,378 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2017-2018 Paul Sokolovsky
* Copyright (c) 2018 Yonatan Goldschmidt
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/mpconfig.h"
#if MICROPY_PY_UCRYPTOLIB
#include <assert.h>
#include <string.h>
#include "py/runtime.h"
// This module implements crypto ciphers API, roughly following
// https://www.python.org/dev/peps/pep-0272/ . Exact implementation
// of PEP 272 can be made with a simple wrapper which adds all the
// needed boilerplate.
// values follow PEP 272
enum {
UCRYPTOLIB_MODE_ECB = 1,
UCRYPTOLIB_MODE_CBC = 2,
UCRYPTOLIB_MODE_CTR = 6,
};
struct ctr_params {
// counter is the IV of the AES context.
size_t offset; // in encrypted_counter
// encrypted counter
uint8_t encrypted_counter[16];
};
#if MICROPY_SSL_AXTLS
#include "lib/axtls/crypto/crypto.h"
#define AES_CTX_IMPL AES_CTX
#endif
#if MICROPY_SSL_MBEDTLS
#include <mbedtls/aes.h>
// we can't run mbedtls AES key schedule until we know whether we're used for encrypt or decrypt.
// therefore, we store the key & keysize and on the first call to encrypt/decrypt we override them
// with the mbedtls_aes_context, as they are not longer required. (this is done to save space)
struct mbedtls_aes_ctx_with_key {
union {
mbedtls_aes_context mbedtls_ctx;
struct {
uint8_t key[32];
uint8_t keysize;
} init_data;
} u;
unsigned char iv[16];
};
#define AES_CTX_IMPL struct mbedtls_aes_ctx_with_key
#endif
typedef struct _mp_obj_aes_t {
mp_obj_base_t base;
AES_CTX_IMPL ctx;
uint8_t block_mode: 6;
#define AES_KEYTYPE_NONE 0
#define AES_KEYTYPE_ENC 1
#define AES_KEYTYPE_DEC 2
uint8_t key_type: 2;
} mp_obj_aes_t;
static inline bool is_ctr_mode(int block_mode) {
#if MICROPY_PY_UCRYPTOLIB_CTR
return block_mode == UCRYPTOLIB_MODE_CTR;
#else
return false;
#endif
}
static inline struct ctr_params *ctr_params_from_aes(mp_obj_aes_t *o) {
// ctr_params follows aes object struct
return (struct ctr_params*)&o[1];
}
#if MICROPY_SSL_AXTLS
STATIC void aes_initial_set_key_impl(AES_CTX_IMPL *ctx, const uint8_t *key, size_t keysize, const uint8_t iv[16]) {
assert(16 == keysize || 32 == keysize);
AES_set_key(ctx, key, iv, (16 == keysize) ? AES_MODE_128 : AES_MODE_256);
}
STATIC void aes_final_set_key_impl(AES_CTX_IMPL *ctx, bool encrypt) {
if (!encrypt) {
AES_convert_key(ctx);
}
}
STATIC void aes_process_ecb_impl(AES_CTX_IMPL *ctx, const uint8_t in[16], uint8_t out[16], bool encrypt) {
memcpy(out, in, 16);
// We assume that out (vstr.buf or given output buffer) is uint32_t aligned
uint32_t *p = (uint32_t*)out;
// axTLS likes it weird and complicated with byteswaps
for (int i = 0; i < 4; i++) {
p[i] = MP_HTOBE32(p[i]);
}
if (encrypt) {
AES_encrypt(ctx, p);
} else {
AES_decrypt(ctx, p);
}
for (int i = 0; i < 4; i++) {
p[i] = MP_BE32TOH(p[i]);
}
}
STATIC void aes_process_cbc_impl(AES_CTX_IMPL *ctx, const uint8_t *in, uint8_t *out, size_t in_len, bool encrypt) {
if (encrypt) {
AES_cbc_encrypt(ctx, in, out, in_len);
} else {
AES_cbc_decrypt(ctx, in, out, in_len);
}
}
#if MICROPY_PY_UCRYPTOLIB_CTR
// axTLS doesn't have CTR support out of the box. This implements the counter part using the ECB primitive.
STATIC void aes_process_ctr_impl(AES_CTX_IMPL *ctx, const uint8_t *in, uint8_t *out, size_t in_len, struct ctr_params *ctr_params) {
size_t n = ctr_params->offset;
uint8_t *const counter = ctx->iv;
while (in_len--) {
if (n == 0) {
aes_process_ecb_impl(ctx, counter, ctr_params->encrypted_counter, true);
// increment the 128-bit counter
for (int i = 15; i >= 0; --i) {
if (++counter[i] != 0) {
break;
}
}
}
*out++ = *in++ ^ ctr_params->encrypted_counter[n];
n = (n + 1) & 0xf;
}
ctr_params->offset = n;
}
#endif
#endif
#if MICROPY_SSL_MBEDTLS
STATIC void aes_initial_set_key_impl(AES_CTX_IMPL *ctx, const uint8_t *key, size_t keysize, const uint8_t iv[16]) {
ctx->u.init_data.keysize = keysize;
memcpy(ctx->u.init_data.key, key, keysize);
if (NULL != iv) {
memcpy(ctx->iv, iv, sizeof(ctx->iv));
}
}
STATIC void aes_final_set_key_impl(AES_CTX_IMPL *ctx, bool encrypt) {
// first, copy key aside
uint8_t key[32];
uint8_t keysize = ctx->u.init_data.keysize;
memcpy(key, ctx->u.init_data.key, keysize);
// now, override key with the mbedtls context object
mbedtls_aes_init(&ctx->u.mbedtls_ctx);
// setkey call will succeed, we've already checked the keysize earlier.
assert(16 == keysize || 32 == keysize);
if (encrypt) {
mbedtls_aes_setkey_enc(&ctx->u.mbedtls_ctx, key, keysize * 8);
} else {
mbedtls_aes_setkey_dec(&ctx->u.mbedtls_ctx, key, keysize * 8);
}
}
STATIC void aes_process_ecb_impl(AES_CTX_IMPL *ctx, const uint8_t in[16], uint8_t out[16], bool encrypt) {
mbedtls_aes_crypt_ecb(&ctx->u.mbedtls_ctx, encrypt ? MBEDTLS_AES_ENCRYPT : MBEDTLS_AES_DECRYPT, in, out);
}
STATIC void aes_process_cbc_impl(AES_CTX_IMPL *ctx, const uint8_t *in, uint8_t *out, size_t in_len, bool encrypt) {
mbedtls_aes_crypt_cbc(&ctx->u.mbedtls_ctx, encrypt ? MBEDTLS_AES_ENCRYPT : MBEDTLS_AES_DECRYPT, in_len, ctx->iv, in, out);
}
#if MICROPY_PY_UCRYPTOLIB_CTR
STATIC void aes_process_ctr_impl(AES_CTX_IMPL *ctx, const uint8_t *in, uint8_t *out, size_t in_len, struct ctr_params *ctr_params) {
mbedtls_aes_crypt_ctr(&ctx->u.mbedtls_ctx, in_len, &ctr_params->offset, ctx->iv, ctr_params->encrypted_counter, in, out);
}
#endif
#endif
STATIC mp_obj_t ucryptolib_aes_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 2, 3, false);
const mp_int_t block_mode = mp_obj_get_int(args[1]);
switch (block_mode) {
case UCRYPTOLIB_MODE_ECB:
case UCRYPTOLIB_MODE_CBC:
#if MICROPY_PY_UCRYPTOLIB_CTR
case UCRYPTOLIB_MODE_CTR:
#endif
break;
default:
mp_raise_ValueError("mode");
}
mp_obj_aes_t *o = m_new_obj_var(mp_obj_aes_t, struct ctr_params, !!is_ctr_mode(block_mode));
o->base.type = type;
o->block_mode = block_mode;
o->key_type = AES_KEYTYPE_NONE;
mp_buffer_info_t keyinfo;
mp_get_buffer_raise(args[0], &keyinfo, MP_BUFFER_READ);
if (32 != keyinfo.len && 16 != keyinfo.len) {
mp_raise_ValueError("key");
}
mp_buffer_info_t ivinfo;
ivinfo.buf = NULL;
if (n_args > 2 && args[2] != mp_const_none) {
mp_get_buffer_raise(args[2], &ivinfo, MP_BUFFER_READ);
if (16 != ivinfo.len) {
mp_raise_ValueError("IV");
}
} else if (o->block_mode == UCRYPTOLIB_MODE_CBC || is_ctr_mode(o->block_mode)) {
mp_raise_ValueError("IV");
}
if (is_ctr_mode(block_mode)) {
ctr_params_from_aes(o)->offset = 0;
}
aes_initial_set_key_impl(&o->ctx, keyinfo.buf, keyinfo.len, ivinfo.buf);
return MP_OBJ_FROM_PTR(o);
}
STATIC mp_obj_t aes_process(size_t n_args, const mp_obj_t *args, bool encrypt) {
mp_obj_aes_t *self = MP_OBJ_TO_PTR(args[0]);
mp_obj_t in_buf = args[1];
mp_obj_t out_buf = MP_OBJ_NULL;
if (n_args > 2) {
out_buf = args[2];
}
mp_buffer_info_t in_bufinfo;
mp_get_buffer_raise(in_buf, &in_bufinfo, MP_BUFFER_READ);
if (!is_ctr_mode(self->block_mode) && in_bufinfo.len % 16 != 0) {
mp_raise_ValueError("blksize % 16");
}
vstr_t vstr;
mp_buffer_info_t out_bufinfo;
uint8_t *out_buf_ptr;
if (out_buf != MP_OBJ_NULL) {
mp_get_buffer_raise(out_buf, &out_bufinfo, MP_BUFFER_WRITE);
if (out_bufinfo.len < in_bufinfo.len) {
mp_raise_ValueError("output too small");
}
out_buf_ptr = out_bufinfo.buf;
} else {
vstr_init_len(&vstr, in_bufinfo.len);
out_buf_ptr = (uint8_t*)vstr.buf;
}
if (AES_KEYTYPE_NONE == self->key_type) {
// always set key for encryption if CTR mode.
const bool encrypt_mode = encrypt || is_ctr_mode(self->block_mode);
aes_final_set_key_impl(&self->ctx, encrypt_mode);
self->key_type = encrypt ? AES_KEYTYPE_ENC : AES_KEYTYPE_DEC;
} else {
if ((encrypt && self->key_type == AES_KEYTYPE_DEC) ||
(!encrypt && self->key_type == AES_KEYTYPE_ENC)) {
mp_raise_ValueError("can't encrypt & decrypt");
}
}
switch (self->block_mode) {
case UCRYPTOLIB_MODE_ECB: {
uint8_t *in = in_bufinfo.buf, *out = out_buf_ptr;
uint8_t *top = in + in_bufinfo.len;
for (; in < top; in += 16, out += 16) {
aes_process_ecb_impl(&self->ctx, in, out, encrypt);
}
break;
}
case UCRYPTOLIB_MODE_CBC:
aes_process_cbc_impl(&self->ctx, in_bufinfo.buf, out_buf_ptr, in_bufinfo.len, encrypt);
break;
#if MICROPY_PY_UCRYPTOLIB_CTR
case UCRYPTOLIB_MODE_CTR:
aes_process_ctr_impl(&self->ctx, in_bufinfo.buf, out_buf_ptr, in_bufinfo.len,
ctr_params_from_aes(self));
break;
#endif
}
if (out_buf != MP_OBJ_NULL) {
return out_buf;
}
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
}
STATIC mp_obj_t ucryptolib_aes_encrypt(size_t n_args, const mp_obj_t *args) {
return aes_process(n_args, args, true);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ucryptolib_aes_encrypt_obj, 2, 3, ucryptolib_aes_encrypt);
STATIC mp_obj_t ucryptolib_aes_decrypt(size_t n_args, const mp_obj_t *args) {
return aes_process(n_args, args, false);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ucryptolib_aes_decrypt_obj, 2, 3, ucryptolib_aes_decrypt);
STATIC const mp_rom_map_elem_t ucryptolib_aes_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_encrypt), MP_ROM_PTR(&ucryptolib_aes_encrypt_obj) },
{ MP_ROM_QSTR(MP_QSTR_decrypt), MP_ROM_PTR(&ucryptolib_aes_decrypt_obj) },
};
STATIC MP_DEFINE_CONST_DICT(ucryptolib_aes_locals_dict, ucryptolib_aes_locals_dict_table);
STATIC const mp_obj_type_t ucryptolib_aes_type = {
{ &mp_type_type },
.name = MP_QSTR_aes,
.make_new = ucryptolib_aes_make_new,
.locals_dict = (void*)&ucryptolib_aes_locals_dict,
};
STATIC const mp_rom_map_elem_t mp_module_ucryptolib_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ucryptolib) },
{ MP_ROM_QSTR(MP_QSTR_aes), MP_ROM_PTR(&ucryptolib_aes_type) },
#if MICROPY_PY_UCRYPTOLIB_CONSTS
{ MP_ROM_QSTR(MP_QSTR_MODE_ECB), MP_ROM_INT(UCRYPTOLIB_MODE_ECB) },
{ MP_ROM_QSTR(MP_QSTR_MODE_CBC), MP_ROM_INT(UCRYPTOLIB_MODE_CBC) },
#if MICROPY_PY_UCRYPTOLIB_CTR
{ MP_ROM_QSTR(MP_QSTR_MODE_CTR), MP_ROM_INT(UCRYPTOLIB_MODE_CTR) },
#endif
#endif
};
STATIC MP_DEFINE_CONST_DICT(mp_module_ucryptolib_globals, mp_module_ucryptolib_globals_table);
const mp_obj_module_t mp_module_ucryptolib = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_ucryptolib_globals,
};
#endif //MICROPY_PY_UCRYPTOLIB
@@ -0,0 +1,778 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2014-2018 Paul Sokolovsky
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <assert.h>
#include <string.h>
#include <stdint.h>
#include "py/runtime.h"
#include "py/objtuple.h"
#include "py/binary.h"
#if MICROPY_PY_UCTYPES
/// \module uctypes - Access data structures in memory
///
/// The module allows to define layout of raw data structure (using terms
/// of C language), and then access memory buffers using this definition.
/// The module also provides convenience functions to access memory buffers
/// contained in Python objects or wrap memory buffers in Python objects.
/// \constant UINT8_1 - uint8_t value type
/// \class struct - C-like structure
///
/// Encapsulalation of in-memory data structure. This class doesn't define
/// any methods, only attribute access (for structure fields) and
/// indexing (for pointer and array fields).
///
/// Usage:
///
/// # Define layout of a structure with 2 fields
/// # 0 and 4 are byte offsets of fields from the beginning of struct
/// # they are logically ORed with field type
/// FOO_STRUCT = {"a": 0 | uctypes.UINT32, "b": 4 | uctypes.UINT8}
///
/// # Example memory buffer to access (contained in bytes object)
/// buf = b"\x64\0\0\0\0x14"
///
/// # Create structure object referring to address of
/// # the data in the buffer above
/// s = uctypes.struct(FOO_STRUCT, uctypes.addressof(buf))
///
/// # Access fields
/// print(s.a, s.b)
/// # Result:
/// # 100, 20
#define LAYOUT_LITTLE_ENDIAN (0)
#define LAYOUT_BIG_ENDIAN (1)
#define LAYOUT_NATIVE (2)
#define VAL_TYPE_BITS 4
#define BITF_LEN_BITS 5
#define BITF_OFF_BITS 5
#define OFFSET_BITS 17
#if VAL_TYPE_BITS + BITF_LEN_BITS + BITF_OFF_BITS + OFFSET_BITS != 31
#error Invalid encoding field length
#endif
enum {
UINT8, INT8, UINT16, INT16,
UINT32, INT32, UINT64, INT64,
BFUINT8, BFINT8, BFUINT16, BFINT16,
BFUINT32, BFINT32,
FLOAT32, FLOAT64,
};
#define AGG_TYPE_BITS 2
enum {
STRUCT, PTR, ARRAY,
};
// Here we need to set sign bit right
#define TYPE2SMALLINT(x, nbits) ((((int)x) << (32 - nbits)) >> 1)
#define GET_TYPE(x, nbits) (((x) >> (31 - nbits)) & ((1 << nbits) - 1))
// Bit 0 is "is_signed"
#define GET_SCALAR_SIZE(val_type) (1 << ((val_type) >> 1))
#define VALUE_MASK(type_nbits) ~((int)0x80000000 >> type_nbits)
#define IS_SCALAR_ARRAY(tuple_desc) ((tuple_desc)->len == 2)
// We cannot apply the below to INT8, as their range [-128, 127]
#define IS_SCALAR_ARRAY_OF_BYTES(tuple_desc) (GET_TYPE(MP_OBJ_SMALL_INT_VALUE((tuple_desc)->items[1]), VAL_TYPE_BITS) == UINT8)
// "struct" in uctypes context means "structural", i.e. aggregate, type.
STATIC const mp_obj_type_t uctypes_struct_type;
typedef struct _mp_obj_uctypes_struct_t {
mp_obj_base_t base;
mp_obj_t desc;
byte *addr;
uint32_t flags;
} mp_obj_uctypes_struct_t;
STATIC NORETURN void syntax_error(void) {
mp_raise_TypeError("syntax error in uctypes descriptor");
}
STATIC mp_obj_t uctypes_struct_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 2, 3, false);
mp_obj_uctypes_struct_t *o = m_new_obj(mp_obj_uctypes_struct_t);
o->base.type = type;
o->addr = (void*)(uintptr_t)mp_obj_int_get_truncated(args[0]);
o->desc = args[1];
o->flags = LAYOUT_NATIVE;
if (n_args == 3) {
o->flags = mp_obj_get_int(args[2]);
}
return MP_OBJ_FROM_PTR(o);
}
STATIC void uctypes_struct_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
(void)kind;
mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in);
const char *typen = "unk";
if (mp_obj_is_type(self->desc, &mp_type_dict)
#if MICROPY_PY_COLLECTIONS_ORDEREDDICT
|| mp_obj_is_type(self->desc, &mp_type_ordereddict)
#endif
) {
typen = "STRUCT";
} else if (mp_obj_is_type(self->desc, &mp_type_tuple)) {
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(self->desc);
mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(t->items[0]);
uint agg_type = GET_TYPE(offset, AGG_TYPE_BITS);
switch (agg_type) {
case PTR: typen = "PTR"; break;
case ARRAY: typen = "ARRAY"; break;
}
} else {
typen = "ERROR";
}
mp_printf(print, "<struct %s %p>", typen, self->addr);
}
// Get size of any type descriptor
STATIC mp_uint_t uctypes_struct_size(mp_obj_t desc_in, int layout_type, mp_uint_t *max_field_size);
// Get size of scalar type descriptor
static inline mp_uint_t uctypes_struct_scalar_size(int val_type) {
if (val_type == FLOAT32) {
return 4;
} else {
return GET_SCALAR_SIZE(val_type & 7);
}
}
// Get size of aggregate type descriptor
STATIC mp_uint_t uctypes_struct_agg_size(mp_obj_tuple_t *t, int layout_type, mp_uint_t *max_field_size) {
mp_uint_t total_size = 0;
mp_int_t offset_ = MP_OBJ_SMALL_INT_VALUE(t->items[0]);
mp_uint_t agg_type = GET_TYPE(offset_, AGG_TYPE_BITS);
switch (agg_type) {
case STRUCT:
return uctypes_struct_size(t->items[1], layout_type, max_field_size);
case PTR:
if (sizeof(void*) > *max_field_size) {
*max_field_size = sizeof(void*);
}
return sizeof(void*);
case ARRAY: {
mp_int_t arr_sz = MP_OBJ_SMALL_INT_VALUE(t->items[1]);
uint val_type = GET_TYPE(arr_sz, VAL_TYPE_BITS);
arr_sz &= VALUE_MASK(VAL_TYPE_BITS);
mp_uint_t item_s;
if (t->len == 2) {
// Elements of array are scalar
item_s = GET_SCALAR_SIZE(val_type);
if (item_s > *max_field_size) {
*max_field_size = item_s;
}
} else {
// Elements of array are aggregates
item_s = uctypes_struct_size(t->items[2], layout_type, max_field_size);
}
return item_s * arr_sz;
}
default:
assert(0);
}
return total_size;
}
STATIC mp_uint_t uctypes_struct_size(mp_obj_t desc_in, int layout_type, mp_uint_t *max_field_size) {
if (!mp_obj_is_type(desc_in, &mp_type_dict)
#if MICROPY_PY_COLLECTIONS_ORDEREDDICT
&& !mp_obj_is_type(desc_in, &mp_type_ordereddict)
#endif
) {
if (mp_obj_is_type(desc_in, &mp_type_tuple)) {
return uctypes_struct_agg_size((mp_obj_tuple_t*)MP_OBJ_TO_PTR(desc_in), layout_type, max_field_size);
} else if (mp_obj_is_small_int(desc_in)) {
// We allow sizeof on both type definitions and structures/structure fields,
// but scalar structure field is lowered into native Python int, so all
// type info is lost. So, we cannot say if it's scalar type description,
// or such lowered scalar.
mp_raise_TypeError("Cannot unambiguously get sizeof scalar");
}
syntax_error();
}
mp_obj_dict_t *d = MP_OBJ_TO_PTR(desc_in);
mp_uint_t total_size = 0;
for (mp_uint_t i = 0; i < d->map.alloc; i++) {
if (mp_map_slot_is_filled(&d->map, i)) {
mp_obj_t v = d->map.table[i].value;
if (mp_obj_is_small_int(v)) {
mp_uint_t offset = MP_OBJ_SMALL_INT_VALUE(v);
mp_uint_t val_type = GET_TYPE(offset, VAL_TYPE_BITS);
offset &= VALUE_MASK(VAL_TYPE_BITS);
if (val_type >= BFUINT8 && val_type <= BFINT32) {
offset &= (1 << OFFSET_BITS) - 1;
}
mp_uint_t s = uctypes_struct_scalar_size(val_type);
if (s > *max_field_size) {
*max_field_size = s;
}
if (offset + s > total_size) {
total_size = offset + s;
}
} else {
if (!mp_obj_is_type(v, &mp_type_tuple)) {
syntax_error();
}
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(v);
mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(t->items[0]);
offset &= VALUE_MASK(AGG_TYPE_BITS);
mp_uint_t s = uctypes_struct_agg_size(t, layout_type, max_field_size);
if (offset + s > total_size) {
total_size = offset + s;
}
}
}
}
// Round size up to alignment of biggest field
if (layout_type == LAYOUT_NATIVE) {
total_size = (total_size + *max_field_size - 1) & ~(*max_field_size - 1);
}
return total_size;
}
STATIC mp_obj_t uctypes_struct_sizeof(size_t n_args, const mp_obj_t *args) {
mp_obj_t obj_in = args[0];
mp_uint_t max_field_size = 0;
if (mp_obj_is_type(obj_in, &mp_type_bytearray)) {
return mp_obj_len(obj_in);
}
int layout_type = LAYOUT_NATIVE;
// We can apply sizeof either to structure definition (a dict)
// or to instantiated structure
if (mp_obj_is_type(obj_in, &uctypes_struct_type)) {
if (n_args != 1) {
mp_raise_TypeError(NULL);
}
// Extract structure definition
mp_obj_uctypes_struct_t *obj = MP_OBJ_TO_PTR(obj_in);
obj_in = obj->desc;
layout_type = obj->flags;
} else {
if (n_args == 2) {
layout_type = mp_obj_get_int(args[1]);
}
}
mp_uint_t size = uctypes_struct_size(obj_in, layout_type, &max_field_size);
return MP_OBJ_NEW_SMALL_INT(size);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(uctypes_struct_sizeof_obj, 1, 2, uctypes_struct_sizeof);
static inline mp_obj_t get_unaligned(uint val_type, byte *p, int big_endian) {
char struct_type = big_endian ? '>' : '<';
static const char type2char[16] = "BbHhIiQq------fd";
return mp_binary_get_val(struct_type, type2char[val_type], p, &p);
}
static inline void set_unaligned(uint val_type, byte *p, int big_endian, mp_obj_t val) {
char struct_type = big_endian ? '>' : '<';
static const char type2char[16] = "BbHhIiQq------fd";
mp_binary_set_val(struct_type, type2char[val_type], val, p, &p);
}
static inline mp_uint_t get_aligned_basic(uint val_type, void *p) {
switch (val_type) {
case UINT8:
return *(uint8_t*)p;
case UINT16:
return *(uint16_t*)p;
case UINT32:
return *(uint32_t*)p;
}
assert(0);
return 0;
}
static inline void set_aligned_basic(uint val_type, void *p, mp_uint_t v) {
switch (val_type) {
case UINT8:
*(uint8_t*)p = (uint8_t)v; return;
case UINT16:
*(uint16_t*)p = (uint16_t)v; return;
case UINT32:
*(uint32_t*)p = (uint32_t)v; return;
}
assert(0);
}
STATIC mp_obj_t get_aligned(uint val_type, void *p, mp_int_t index) {
switch (val_type) {
case UINT8:
return MP_OBJ_NEW_SMALL_INT(((uint8_t*)p)[index]);
case INT8:
return MP_OBJ_NEW_SMALL_INT(((int8_t*)p)[index]);
case UINT16:
return MP_OBJ_NEW_SMALL_INT(((uint16_t*)p)[index]);
case INT16:
return MP_OBJ_NEW_SMALL_INT(((int16_t*)p)[index]);
case UINT32:
return mp_obj_new_int_from_uint(((uint32_t*)p)[index]);
case INT32:
return mp_obj_new_int(((int32_t*)p)[index]);
case UINT64:
return mp_obj_new_int_from_ull(((uint64_t*)p)[index]);
case INT64:
return mp_obj_new_int_from_ll(((int64_t*)p)[index]);
#if MICROPY_PY_BUILTINS_FLOAT
case FLOAT32:
return mp_obj_new_float(((float*)p)[index]);
case FLOAT64:
return mp_obj_new_float(((double*)p)[index]);
#endif
default:
assert(0);
return MP_OBJ_NULL;
}
}
STATIC void set_aligned(uint val_type, void *p, mp_int_t index, mp_obj_t val) {
#if MICROPY_PY_BUILTINS_FLOAT
if (val_type == FLOAT32 || val_type == FLOAT64) {
mp_float_t v = mp_obj_get_float(val);
if (val_type == FLOAT32) {
((float*)p)[index] = v;
} else {
((double*)p)[index] = v;
}
return;
}
#endif
mp_int_t v = mp_obj_get_int_truncated(val);
switch (val_type) {
case UINT8:
((uint8_t*)p)[index] = (uint8_t)v; return;
case INT8:
((int8_t*)p)[index] = (int8_t)v; return;
case UINT16:
((uint16_t*)p)[index] = (uint16_t)v; return;
case INT16:
((int16_t*)p)[index] = (int16_t)v; return;
case UINT32:
((uint32_t*)p)[index] = (uint32_t)v; return;
case INT32:
((int32_t*)p)[index] = (int32_t)v; return;
case INT64:
case UINT64:
if (sizeof(mp_int_t) == 8) {
((uint64_t*)p)[index] = (uint64_t)v;
} else {
// TODO: Doesn't offer atomic store semantics, but should at least try
set_unaligned(val_type, p, MP_ENDIANNESS_BIG, val);
}
return;
default:
assert(0);
}
}
STATIC mp_obj_t uctypes_struct_attr_op(mp_obj_t self_in, qstr attr, mp_obj_t set_val) {
mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in);
if (!mp_obj_is_type(self->desc, &mp_type_dict)
#if MICROPY_PY_COLLECTIONS_ORDEREDDICT
&& !mp_obj_is_type(self->desc, &mp_type_ordereddict)
#endif
) {
mp_raise_TypeError("struct: no fields");
}
mp_obj_t deref = mp_obj_dict_get(self->desc, MP_OBJ_NEW_QSTR(attr));
if (mp_obj_is_small_int(deref)) {
mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(deref);
mp_uint_t val_type = GET_TYPE(offset, VAL_TYPE_BITS);
offset &= VALUE_MASK(VAL_TYPE_BITS);
//printf("scalar type=%d offset=%x\n", val_type, offset);
if (val_type <= INT64 || val_type == FLOAT32 || val_type == FLOAT64) {
// printf("size=%d\n", GET_SCALAR_SIZE(val_type));
if (self->flags == LAYOUT_NATIVE) {
if (set_val == MP_OBJ_NULL) {
return get_aligned(val_type, self->addr + offset, 0);
} else {
set_aligned(val_type, self->addr + offset, 0, set_val);
return set_val; // just !MP_OBJ_NULL
}
} else {
if (set_val == MP_OBJ_NULL) {
return get_unaligned(val_type, self->addr + offset, self->flags);
} else {
set_unaligned(val_type, self->addr + offset, self->flags, set_val);
return set_val; // just !MP_OBJ_NULL
}
}
} else if (val_type >= BFUINT8 && val_type <= BFINT32) {
uint bit_offset = (offset >> 17) & 31;
uint bit_len = (offset >> 22) & 31;
offset &= (1 << 17) - 1;
mp_uint_t val;
if (self->flags == LAYOUT_NATIVE) {
val = get_aligned_basic(val_type & 6, self->addr + offset);
} else {
val = mp_binary_get_int(GET_SCALAR_SIZE(val_type & 7), val_type & 1, self->flags, self->addr + offset);
}
if (set_val == MP_OBJ_NULL) {
val >>= bit_offset;
val &= (1 << bit_len) - 1;
// TODO: signed
assert((val_type & 1) == 0);
return mp_obj_new_int(val);
} else {
mp_uint_t set_val_int = (mp_uint_t)mp_obj_get_int(set_val);
mp_uint_t mask = (1 << bit_len) - 1;
set_val_int &= mask;
set_val_int <<= bit_offset;
mask <<= bit_offset;
val = (val & ~mask) | set_val_int;
if (self->flags == LAYOUT_NATIVE) {
set_aligned_basic(val_type & 6, self->addr + offset, val);
} else {
mp_binary_set_int(GET_SCALAR_SIZE(val_type & 7), self->flags == LAYOUT_BIG_ENDIAN,
self->addr + offset, val);
}
return set_val; // just !MP_OBJ_NULL
}
}
assert(0);
return MP_OBJ_NULL;
}
if (!mp_obj_is_type(deref, &mp_type_tuple)) {
syntax_error();
}
if (set_val != MP_OBJ_NULL) {
// Cannot assign to aggregate
syntax_error();
}
mp_obj_tuple_t *sub = MP_OBJ_TO_PTR(deref);
mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(sub->items[0]);
mp_uint_t agg_type = GET_TYPE(offset, AGG_TYPE_BITS);
offset &= VALUE_MASK(AGG_TYPE_BITS);
//printf("agg type=%d offset=%x\n", agg_type, offset);
switch (agg_type) {
case STRUCT: {
mp_obj_uctypes_struct_t *o = m_new_obj(mp_obj_uctypes_struct_t);
o->base.type = &uctypes_struct_type;
o->desc = sub->items[1];
o->addr = self->addr + offset;
o->flags = self->flags;
return MP_OBJ_FROM_PTR(o);
}
case ARRAY: {
mp_uint_t dummy;
if (IS_SCALAR_ARRAY(sub) && IS_SCALAR_ARRAY_OF_BYTES(sub)) {
return mp_obj_new_bytearray_by_ref(uctypes_struct_agg_size(sub, self->flags, &dummy), self->addr + offset);
}
// Fall thru to return uctypes struct object
}
case PTR: {
mp_obj_uctypes_struct_t *o = m_new_obj(mp_obj_uctypes_struct_t);
o->base.type = &uctypes_struct_type;
o->desc = MP_OBJ_FROM_PTR(sub);
o->addr = self->addr + offset;
o->flags = self->flags;
//printf("PTR/ARR base addr=%p\n", o->addr);
return MP_OBJ_FROM_PTR(o);
}
}
// Should be unreachable once all cases are handled
return MP_OBJ_NULL;
}
STATIC void uctypes_struct_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
if (dest[0] == MP_OBJ_NULL) {
// load attribute
mp_obj_t val = uctypes_struct_attr_op(self_in, attr, MP_OBJ_NULL);
dest[0] = val;
} else {
// delete/store attribute
if (uctypes_struct_attr_op(self_in, attr, dest[1]) != MP_OBJ_NULL) {
dest[0] = MP_OBJ_NULL; // indicate success
}
}
}
STATIC mp_obj_t uctypes_struct_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value) {
mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in);
if (value == MP_OBJ_NULL) {
// delete
return MP_OBJ_NULL; // op not supported
} else {
// load / store
if (!mp_obj_is_type(self->desc, &mp_type_tuple)) {
mp_raise_TypeError("struct: cannot index");
}
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(self->desc);
mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(t->items[0]);
uint agg_type = GET_TYPE(offset, AGG_TYPE_BITS);
mp_int_t index = MP_OBJ_SMALL_INT_VALUE(index_in);
if (agg_type == ARRAY) {
mp_int_t arr_sz = MP_OBJ_SMALL_INT_VALUE(t->items[1]);
uint val_type = GET_TYPE(arr_sz, VAL_TYPE_BITS);
arr_sz &= VALUE_MASK(VAL_TYPE_BITS);
if (index >= arr_sz) {
mp_raise_msg(&mp_type_IndexError, "struct: index out of range");
}
if (t->len == 2) {
// array of scalars
if (self->flags == LAYOUT_NATIVE) {
if (value == MP_OBJ_SENTINEL) {
return get_aligned(val_type, self->addr, index);
} else {
set_aligned(val_type, self->addr, index, value);
return value; // just !MP_OBJ_NULL
}
} else {
byte *p = self->addr + GET_SCALAR_SIZE(val_type) * index;
if (value == MP_OBJ_SENTINEL) {
return get_unaligned(val_type, p, self->flags);
} else {
set_unaligned(val_type, p, self->flags, value);
return value; // just !MP_OBJ_NULL
}
}
} else if (value == MP_OBJ_SENTINEL) {
mp_uint_t dummy = 0;
mp_uint_t size = uctypes_struct_size(t->items[2], self->flags, &dummy);
mp_obj_uctypes_struct_t *o = m_new_obj(mp_obj_uctypes_struct_t);
o->base.type = &uctypes_struct_type;
o->desc = t->items[2];
o->addr = self->addr + size * index;
o->flags = self->flags;
return MP_OBJ_FROM_PTR(o);
} else {
return MP_OBJ_NULL; // op not supported
}
} else if (agg_type == PTR) {
byte *p = *(void**)self->addr;
if (mp_obj_is_small_int(t->items[1])) {
uint val_type = GET_TYPE(MP_OBJ_SMALL_INT_VALUE(t->items[1]), VAL_TYPE_BITS);
return get_aligned(val_type, p, index);
} else {
mp_uint_t dummy = 0;
mp_uint_t size = uctypes_struct_size(t->items[1], self->flags, &dummy);
mp_obj_uctypes_struct_t *o = m_new_obj(mp_obj_uctypes_struct_t);
o->base.type = &uctypes_struct_type;
o->desc = t->items[1];
o->addr = p + size * index;
o->flags = self->flags;
return MP_OBJ_FROM_PTR(o);
}
}
assert(0);
return MP_OBJ_NULL;
}
}
STATIC mp_obj_t uctypes_struct_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in);
switch (op) {
case MP_UNARY_OP_INT:
if (mp_obj_is_type(self->desc, &mp_type_tuple)) {
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(self->desc);
mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(t->items[0]);
uint agg_type = GET_TYPE(offset, AGG_TYPE_BITS);
if (agg_type == PTR) {
byte *p = *(void**)self->addr;
return mp_obj_new_int((mp_int_t)(uintptr_t)p);
}
}
/* fallthru */
default: return MP_OBJ_NULL; // op not supported
}
}
STATIC mp_int_t uctypes_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) {
(void)flags;
mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in);
mp_uint_t max_field_size = 0;
mp_uint_t size = uctypes_struct_size(self->desc, self->flags, &max_field_size);
bufinfo->buf = self->addr;
bufinfo->len = size;
bufinfo->typecode = BYTEARRAY_TYPECODE;
return 0;
}
/// \function addressof()
/// Return address of object's data (applies to object providing buffer
/// interface).
STATIC mp_obj_t uctypes_struct_addressof(mp_obj_t buf) {
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ);
return mp_obj_new_int((mp_int_t)(uintptr_t)bufinfo.buf);
}
MP_DEFINE_CONST_FUN_OBJ_1(uctypes_struct_addressof_obj, uctypes_struct_addressof);
/// \function bytearray_at()
/// Capture memory at given address of given size as bytearray. Memory is
/// captured by reference (and thus memory pointed by bytearray may change
/// or become invalid at later time). Use bytes_at() to capture by value.
STATIC mp_obj_t uctypes_struct_bytearray_at(mp_obj_t ptr, mp_obj_t size) {
return mp_obj_new_bytearray_by_ref(mp_obj_int_get_truncated(size), (void*)(uintptr_t)mp_obj_int_get_truncated(ptr));
}
MP_DEFINE_CONST_FUN_OBJ_2(uctypes_struct_bytearray_at_obj, uctypes_struct_bytearray_at);
/// \function bytes_at()
/// Capture memory at given address of given size as bytes. Memory is
/// captured by value, i.e. copied. Use bytearray_at() to capture by reference
/// ("zero copy").
STATIC mp_obj_t uctypes_struct_bytes_at(mp_obj_t ptr, mp_obj_t size) {
return mp_obj_new_bytes((void*)(uintptr_t)mp_obj_int_get_truncated(ptr), mp_obj_int_get_truncated(size));
}
MP_DEFINE_CONST_FUN_OBJ_2(uctypes_struct_bytes_at_obj, uctypes_struct_bytes_at);
STATIC const mp_obj_type_t uctypes_struct_type = {
{ &mp_type_type },
.name = MP_QSTR_struct,
.print = uctypes_struct_print,
.make_new = uctypes_struct_make_new,
.attr = uctypes_struct_attr,
.subscr = uctypes_struct_subscr,
.unary_op = uctypes_struct_unary_op,
.buffer_p = { .get_buffer = uctypes_get_buffer },
};
STATIC const mp_rom_map_elem_t mp_module_uctypes_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uctypes) },
{ MP_ROM_QSTR(MP_QSTR_struct), MP_ROM_PTR(&uctypes_struct_type) },
{ MP_ROM_QSTR(MP_QSTR_sizeof), MP_ROM_PTR(&uctypes_struct_sizeof_obj) },
{ MP_ROM_QSTR(MP_QSTR_addressof), MP_ROM_PTR(&uctypes_struct_addressof_obj) },
{ MP_ROM_QSTR(MP_QSTR_bytes_at), MP_ROM_PTR(&uctypes_struct_bytes_at_obj) },
{ MP_ROM_QSTR(MP_QSTR_bytearray_at), MP_ROM_PTR(&uctypes_struct_bytearray_at_obj) },
/// \moduleref uctypes
/// \constant NATIVE - Native structure layout - native endianness,
/// platform-specific field alignment
{ MP_ROM_QSTR(MP_QSTR_NATIVE), MP_ROM_INT(LAYOUT_NATIVE) },
/// \constant LITTLE_ENDIAN - Little-endian structure layout, tightly packed
/// (no alignment constraints)
{ MP_ROM_QSTR(MP_QSTR_LITTLE_ENDIAN), MP_ROM_INT(LAYOUT_LITTLE_ENDIAN) },
/// \constant BIG_ENDIAN - Big-endian structure layout, tightly packed
/// (no alignment constraints)
{ MP_ROM_QSTR(MP_QSTR_BIG_ENDIAN), MP_ROM_INT(LAYOUT_BIG_ENDIAN) },
/// \constant VOID - void value type, may be used only as pointer target type.
{ MP_ROM_QSTR(MP_QSTR_VOID), MP_ROM_INT(TYPE2SMALLINT(UINT8, VAL_TYPE_BITS)) },
/// \constant UINT8 - uint8_t value type
{ MP_ROM_QSTR(MP_QSTR_UINT8), MP_ROM_INT(TYPE2SMALLINT(UINT8, 4)) },
/// \constant INT8 - int8_t value type
{ MP_ROM_QSTR(MP_QSTR_INT8), MP_ROM_INT(TYPE2SMALLINT(INT8, 4)) },
/// \constant UINT16 - uint16_t value type
{ MP_ROM_QSTR(MP_QSTR_UINT16), MP_ROM_INT(TYPE2SMALLINT(UINT16, 4)) },
/// \constant INT16 - int16_t value type
{ MP_ROM_QSTR(MP_QSTR_INT16), MP_ROM_INT(TYPE2SMALLINT(INT16, 4)) },
/// \constant UINT32 - uint32_t value type
{ MP_ROM_QSTR(MP_QSTR_UINT32), MP_ROM_INT(TYPE2SMALLINT(UINT32, 4)) },
/// \constant INT32 - int32_t value type
{ MP_ROM_QSTR(MP_QSTR_INT32), MP_ROM_INT(TYPE2SMALLINT(INT32, 4)) },
/// \constant UINT64 - uint64_t value type
{ MP_ROM_QSTR(MP_QSTR_UINT64), MP_ROM_INT(TYPE2SMALLINT(UINT64, 4)) },
/// \constant INT64 - int64_t value type
{ MP_ROM_QSTR(MP_QSTR_INT64), MP_ROM_INT(TYPE2SMALLINT(INT64, 4)) },
{ MP_ROM_QSTR(MP_QSTR_BFUINT8), MP_ROM_INT(TYPE2SMALLINT(BFUINT8, 4)) },
{ MP_ROM_QSTR(MP_QSTR_BFINT8), MP_ROM_INT(TYPE2SMALLINT(BFINT8, 4)) },
{ MP_ROM_QSTR(MP_QSTR_BFUINT16), MP_ROM_INT(TYPE2SMALLINT(BFUINT16, 4)) },
{ MP_ROM_QSTR(MP_QSTR_BFINT16), MP_ROM_INT(TYPE2SMALLINT(BFINT16, 4)) },
{ MP_ROM_QSTR(MP_QSTR_BFUINT32), MP_ROM_INT(TYPE2SMALLINT(BFUINT32, 4)) },
{ MP_ROM_QSTR(MP_QSTR_BFINT32), MP_ROM_INT(TYPE2SMALLINT(BFINT32, 4)) },
{ MP_ROM_QSTR(MP_QSTR_BF_POS), MP_ROM_INT(17) },
{ MP_ROM_QSTR(MP_QSTR_BF_LEN), MP_ROM_INT(22) },
#if MICROPY_PY_BUILTINS_FLOAT
{ MP_ROM_QSTR(MP_QSTR_FLOAT32), MP_ROM_INT(TYPE2SMALLINT(FLOAT32, 4)) },
{ MP_ROM_QSTR(MP_QSTR_FLOAT64), MP_ROM_INT(TYPE2SMALLINT(FLOAT64, 4)) },
#endif
#if MICROPY_PY_UCTYPES_NATIVE_C_TYPES
// C native type aliases. These depend on GCC-compatible predefined
// preprocessor macros.
#if __SIZEOF_SHORT__ == 2
{ MP_ROM_QSTR(MP_QSTR_SHORT), MP_ROM_INT(TYPE2SMALLINT(INT16, 4)) },
{ MP_ROM_QSTR(MP_QSTR_USHORT), MP_ROM_INT(TYPE2SMALLINT(UINT16, 4)) },
#endif
#if __SIZEOF_INT__ == 4
{ MP_ROM_QSTR(MP_QSTR_INT), MP_ROM_INT(TYPE2SMALLINT(INT32, 4)) },
{ MP_ROM_QSTR(MP_QSTR_UINT), MP_ROM_INT(TYPE2SMALLINT(UINT32, 4)) },
#endif
#if __SIZEOF_LONG__ == 4
{ MP_ROM_QSTR(MP_QSTR_LONG), MP_ROM_INT(TYPE2SMALLINT(INT32, 4)) },
{ MP_ROM_QSTR(MP_QSTR_ULONG), MP_ROM_INT(TYPE2SMALLINT(UINT32, 4)) },
#elif __SIZEOF_LONG__ == 8
{ MP_ROM_QSTR(MP_QSTR_LONG), MP_ROM_INT(TYPE2SMALLINT(INT64, 4)) },
{ MP_ROM_QSTR(MP_QSTR_ULONG), MP_ROM_INT(TYPE2SMALLINT(UINT64, 4)) },
#endif
#if __SIZEOF_LONG_LONG__ == 8
{ MP_ROM_QSTR(MP_QSTR_LONGLONG), MP_ROM_INT(TYPE2SMALLINT(INT64, 4)) },
{ MP_ROM_QSTR(MP_QSTR_ULONGLONG), MP_ROM_INT(TYPE2SMALLINT(UINT64, 4)) },
#endif
#endif // MICROPY_PY_UCTYPES_NATIVE_C_TYPES
{ MP_ROM_QSTR(MP_QSTR_PTR), MP_ROM_INT(TYPE2SMALLINT(PTR, AGG_TYPE_BITS)) },
{ MP_ROM_QSTR(MP_QSTR_ARRAY), MP_ROM_INT(TYPE2SMALLINT(ARRAY, AGG_TYPE_BITS)) },
};
STATIC MP_DEFINE_CONST_DICT(mp_module_uctypes_globals, mp_module_uctypes_globals_table);
const mp_obj_module_t mp_module_uctypes = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_uctypes_globals,
};
#endif
@@ -0,0 +1,349 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2014 Paul Sokolovsky
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <assert.h>
#include <string.h>
#include "py/runtime.h"
#if MICROPY_PY_UHASHLIB
#if MICROPY_SSL_MBEDTLS
#include "mbedtls/version.h"
#endif
#if MICROPY_PY_UHASHLIB_SHA256
#if MICROPY_SSL_MBEDTLS
#include "mbedtls/sha256.h"
#else
#include "crypto-algorithms/sha256.h"
#endif
#endif
#if MICROPY_PY_UHASHLIB_SHA1 || MICROPY_PY_UHASHLIB_MD5
#if MICROPY_SSL_AXTLS
#include "lib/axtls/crypto/crypto.h"
#endif
#if MICROPY_SSL_MBEDTLS
#include "mbedtls/md5.h"
#include "mbedtls/sha1.h"
#endif
#endif
typedef struct _mp_obj_hash_t {
mp_obj_base_t base;
char state[0];
} mp_obj_hash_t;
#if MICROPY_PY_UHASHLIB_SHA256
STATIC mp_obj_t uhashlib_sha256_update(mp_obj_t self_in, mp_obj_t arg);
#if MICROPY_SSL_MBEDTLS
#if MBEDTLS_VERSION_NUMBER < 0x02070000
#define mbedtls_sha256_starts_ret mbedtls_sha256_starts
#define mbedtls_sha256_update_ret mbedtls_sha256_update
#define mbedtls_sha256_finish_ret mbedtls_sha256_finish
#endif
STATIC mp_obj_t uhashlib_sha256_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 0, 1, false);
mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(mbedtls_sha256_context));
o->base.type = type;
mbedtls_sha256_init((mbedtls_sha256_context*)&o->state);
mbedtls_sha256_starts_ret((mbedtls_sha256_context*)&o->state, 0);
if (n_args == 1) {
uhashlib_sha256_update(MP_OBJ_FROM_PTR(o), args[0]);
}
return MP_OBJ_FROM_PTR(o);
}
STATIC mp_obj_t uhashlib_sha256_update(mp_obj_t self_in, mp_obj_t arg) {
mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ);
mbedtls_sha256_update_ret((mbedtls_sha256_context*)&self->state, bufinfo.buf, bufinfo.len);
return mp_const_none;
}
STATIC mp_obj_t uhashlib_sha256_digest(mp_obj_t self_in) {
mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
vstr_t vstr;
vstr_init_len(&vstr, 32);
mbedtls_sha256_finish_ret((mbedtls_sha256_context*)&self->state, (unsigned char *)vstr.buf);
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
}
#else
#include "crypto-algorithms/sha256.c"
STATIC mp_obj_t uhashlib_sha256_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 0, 1, false);
mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(CRYAL_SHA256_CTX));
o->base.type = type;
sha256_init((CRYAL_SHA256_CTX*)o->state);
if (n_args == 1) {
uhashlib_sha256_update(MP_OBJ_FROM_PTR(o), args[0]);
}
return MP_OBJ_FROM_PTR(o);
}
STATIC mp_obj_t uhashlib_sha256_update(mp_obj_t self_in, mp_obj_t arg) {
mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ);
sha256_update((CRYAL_SHA256_CTX*)self->state, bufinfo.buf, bufinfo.len);
return mp_const_none;
}
STATIC mp_obj_t uhashlib_sha256_digest(mp_obj_t self_in) {
mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
vstr_t vstr;
vstr_init_len(&vstr, SHA256_BLOCK_SIZE);
sha256_final((CRYAL_SHA256_CTX*)self->state, (byte*)vstr.buf);
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
}
#endif
STATIC MP_DEFINE_CONST_FUN_OBJ_2(uhashlib_sha256_update_obj, uhashlib_sha256_update);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(uhashlib_sha256_digest_obj, uhashlib_sha256_digest);
STATIC const mp_rom_map_elem_t uhashlib_sha256_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&uhashlib_sha256_update_obj) },
{ MP_ROM_QSTR(MP_QSTR_digest), MP_ROM_PTR(&uhashlib_sha256_digest_obj) },
};
STATIC MP_DEFINE_CONST_DICT(uhashlib_sha256_locals_dict, uhashlib_sha256_locals_dict_table);
STATIC const mp_obj_type_t uhashlib_sha256_type = {
{ &mp_type_type },
.name = MP_QSTR_sha256,
.make_new = uhashlib_sha256_make_new,
.locals_dict = (void*)&uhashlib_sha256_locals_dict,
};
#endif
#if MICROPY_PY_UHASHLIB_SHA1
STATIC mp_obj_t uhashlib_sha1_update(mp_obj_t self_in, mp_obj_t arg);
#if MICROPY_SSL_AXTLS
STATIC mp_obj_t uhashlib_sha1_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 0, 1, false);
mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(SHA1_CTX));
o->base.type = type;
SHA1_Init((SHA1_CTX*)o->state);
if (n_args == 1) {
uhashlib_sha1_update(MP_OBJ_FROM_PTR(o), args[0]);
}
return MP_OBJ_FROM_PTR(o);
}
STATIC mp_obj_t uhashlib_sha1_update(mp_obj_t self_in, mp_obj_t arg) {
mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ);
SHA1_Update((SHA1_CTX*)self->state, bufinfo.buf, bufinfo.len);
return mp_const_none;
}
STATIC mp_obj_t uhashlib_sha1_digest(mp_obj_t self_in) {
mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
vstr_t vstr;
vstr_init_len(&vstr, SHA1_SIZE);
SHA1_Final((byte*)vstr.buf, (SHA1_CTX*)self->state);
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
}
#endif
#if MICROPY_SSL_MBEDTLS
#if MBEDTLS_VERSION_NUMBER < 0x02070000
#define mbedtls_sha1_starts_ret mbedtls_sha1_starts
#define mbedtls_sha1_update_ret mbedtls_sha1_update
#define mbedtls_sha1_finish_ret mbedtls_sha1_finish
#endif
STATIC mp_obj_t uhashlib_sha1_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 0, 1, false);
mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(mbedtls_sha1_context));
o->base.type = type;
mbedtls_sha1_init((mbedtls_sha1_context*)o->state);
mbedtls_sha1_starts_ret((mbedtls_sha1_context*)o->state);
if (n_args == 1) {
uhashlib_sha1_update(MP_OBJ_FROM_PTR(o), args[0]);
}
return MP_OBJ_FROM_PTR(o);
}
STATIC mp_obj_t uhashlib_sha1_update(mp_obj_t self_in, mp_obj_t arg) {
mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ);
mbedtls_sha1_update_ret((mbedtls_sha1_context*)self->state, bufinfo.buf, bufinfo.len);
return mp_const_none;
}
STATIC mp_obj_t uhashlib_sha1_digest(mp_obj_t self_in) {
mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
vstr_t vstr;
vstr_init_len(&vstr, 20);
mbedtls_sha1_finish_ret((mbedtls_sha1_context*)self->state, (byte*)vstr.buf);
mbedtls_sha1_free((mbedtls_sha1_context*)self->state);
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
}
#endif
STATIC MP_DEFINE_CONST_FUN_OBJ_2(uhashlib_sha1_update_obj, uhashlib_sha1_update);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(uhashlib_sha1_digest_obj, uhashlib_sha1_digest);
STATIC const mp_rom_map_elem_t uhashlib_sha1_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&uhashlib_sha1_update_obj) },
{ MP_ROM_QSTR(MP_QSTR_digest), MP_ROM_PTR(&uhashlib_sha1_digest_obj) },
};
STATIC MP_DEFINE_CONST_DICT(uhashlib_sha1_locals_dict, uhashlib_sha1_locals_dict_table);
STATIC const mp_obj_type_t uhashlib_sha1_type = {
{ &mp_type_type },
.name = MP_QSTR_sha1,
.make_new = uhashlib_sha1_make_new,
.locals_dict = (void*)&uhashlib_sha1_locals_dict,
};
#endif
#if MICROPY_PY_UHASHLIB_MD5
STATIC mp_obj_t uhashlib_md5_update(mp_obj_t self_in, mp_obj_t arg);
#if MICROPY_SSL_AXTLS
STATIC mp_obj_t uhashlib_md5_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 0, 1, false);
mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(MD5_CTX));
o->base.type = type;
MD5_Init((MD5_CTX*)o->state);
if (n_args == 1) {
uhashlib_md5_update(MP_OBJ_FROM_PTR(o), args[0]);
}
return MP_OBJ_FROM_PTR(o);
}
STATIC mp_obj_t uhashlib_md5_update(mp_obj_t self_in, mp_obj_t arg) {
mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ);
MD5_Update((MD5_CTX*)self->state, bufinfo.buf, bufinfo.len);
return mp_const_none;
}
STATIC mp_obj_t uhashlib_md5_digest(mp_obj_t self_in) {
mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
vstr_t vstr;
vstr_init_len(&vstr, MD5_SIZE);
MD5_Final((byte*)vstr.buf, (MD5_CTX*)self->state);
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
}
#endif // MICROPY_SSL_AXTLS
#if MICROPY_SSL_MBEDTLS
#if MBEDTLS_VERSION_NUMBER < 0x02070000
#define mbedtls_md5_starts_ret mbedtls_md5_starts
#define mbedtls_md5_update_ret mbedtls_md5_update
#define mbedtls_md5_finish_ret mbedtls_md5_finish
#endif
STATIC mp_obj_t uhashlib_md5_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 0, 1, false);
mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(mbedtls_md5_context));
o->base.type = type;
mbedtls_md5_init((mbedtls_md5_context*)o->state);
mbedtls_md5_starts_ret((mbedtls_md5_context*)o->state);
if (n_args == 1) {
uhashlib_md5_update(MP_OBJ_FROM_PTR(o), args[0]);
}
return MP_OBJ_FROM_PTR(o);
}
STATIC mp_obj_t uhashlib_md5_update(mp_obj_t self_in, mp_obj_t arg) {
mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ);
mbedtls_md5_update_ret((mbedtls_md5_context*)self->state, bufinfo.buf, bufinfo.len);
return mp_const_none;
}
STATIC mp_obj_t uhashlib_md5_digest(mp_obj_t self_in) {
mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
vstr_t vstr;
vstr_init_len(&vstr, 16);
mbedtls_md5_finish_ret((mbedtls_md5_context*)self->state, (byte*)vstr.buf);
mbedtls_md5_free((mbedtls_md5_context*)self->state);
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
}
#endif // MICROPY_SSL_MBEDTLS
STATIC MP_DEFINE_CONST_FUN_OBJ_2(uhashlib_md5_update_obj, uhashlib_md5_update);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(uhashlib_md5_digest_obj, uhashlib_md5_digest);
STATIC const mp_rom_map_elem_t uhashlib_md5_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&uhashlib_md5_update_obj) },
{ MP_ROM_QSTR(MP_QSTR_digest), MP_ROM_PTR(&uhashlib_md5_digest_obj) },
};
STATIC MP_DEFINE_CONST_DICT(uhashlib_md5_locals_dict, uhashlib_md5_locals_dict_table);
STATIC const mp_obj_type_t uhashlib_md5_type = {
{ &mp_type_type },
.name = MP_QSTR_md5,
.make_new = uhashlib_md5_make_new,
.locals_dict = (void*)&uhashlib_md5_locals_dict,
};
#endif // MICROPY_PY_UHASHLIB_MD5
STATIC const mp_rom_map_elem_t mp_module_uhashlib_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uhashlib) },
#if MICROPY_PY_UHASHLIB_SHA256
{ MP_ROM_QSTR(MP_QSTR_sha256), MP_ROM_PTR(&uhashlib_sha256_type) },
#endif
#if MICROPY_PY_UHASHLIB_SHA1
{ MP_ROM_QSTR(MP_QSTR_sha1), MP_ROM_PTR(&uhashlib_sha1_type) },
#endif
#if MICROPY_PY_UHASHLIB_MD5
{ MP_ROM_QSTR(MP_QSTR_md5), MP_ROM_PTR(&uhashlib_md5_type) },
#endif
};
STATIC MP_DEFINE_CONST_DICT(mp_module_uhashlib_globals, mp_module_uhashlib_globals_table);
const mp_obj_module_t mp_module_uhashlib = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_uhashlib_globals,
};
#endif //MICROPY_PY_UHASHLIB
@@ -0,0 +1,122 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2014 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/objlist.h"
#include "py/runtime.h"
#if MICROPY_PY_UHEAPQ
// the algorithm here is modelled on CPython's heapq.py
STATIC mp_obj_list_t *uheapq_get_heap(mp_obj_t heap_in) {
if (!mp_obj_is_type(heap_in, &mp_type_list)) {
mp_raise_TypeError("heap must be a list");
}
return MP_OBJ_TO_PTR(heap_in);
}
STATIC void uheapq_heap_siftdown(mp_obj_list_t *heap, mp_uint_t start_pos, mp_uint_t pos) {
mp_obj_t item = heap->items[pos];
while (pos > start_pos) {
mp_uint_t parent_pos = (pos - 1) >> 1;
mp_obj_t parent = heap->items[parent_pos];
if (mp_binary_op(MP_BINARY_OP_LESS, item, parent) == mp_const_true) {
heap->items[pos] = parent;
pos = parent_pos;
} else {
break;
}
}
heap->items[pos] = item;
}
STATIC void uheapq_heap_siftup(mp_obj_list_t *heap, mp_uint_t pos) {
mp_uint_t start_pos = pos;
mp_uint_t end_pos = heap->len;
mp_obj_t item = heap->items[pos];
for (mp_uint_t child_pos = 2 * pos + 1; child_pos < end_pos; child_pos = 2 * pos + 1) {
// choose right child if it's <= left child
if (child_pos + 1 < end_pos && mp_binary_op(MP_BINARY_OP_LESS, heap->items[child_pos], heap->items[child_pos + 1]) == mp_const_false) {
child_pos += 1;
}
// bubble up the smaller child
heap->items[pos] = heap->items[child_pos];
pos = child_pos;
}
heap->items[pos] = item;
uheapq_heap_siftdown(heap, start_pos, pos);
}
STATIC mp_obj_t mod_uheapq_heappush(mp_obj_t heap_in, mp_obj_t item) {
mp_obj_list_t *heap = uheapq_get_heap(heap_in);
mp_obj_list_append(heap_in, item);
uheapq_heap_siftdown(heap, 0, heap->len - 1);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_uheapq_heappush_obj, mod_uheapq_heappush);
STATIC mp_obj_t mod_uheapq_heappop(mp_obj_t heap_in) {
mp_obj_list_t *heap = uheapq_get_heap(heap_in);
if (heap->len == 0) {
mp_raise_msg(&mp_type_IndexError, "empty heap");
}
mp_obj_t item = heap->items[0];
heap->len -= 1;
heap->items[0] = heap->items[heap->len];
heap->items[heap->len] = MP_OBJ_NULL; // so we don't retain a pointer
if (heap->len) {
uheapq_heap_siftup(heap, 0);
}
return item;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_uheapq_heappop_obj, mod_uheapq_heappop);
STATIC mp_obj_t mod_uheapq_heapify(mp_obj_t heap_in) {
mp_obj_list_t *heap = uheapq_get_heap(heap_in);
for (mp_uint_t i = heap->len / 2; i > 0;) {
uheapq_heap_siftup(heap, --i);
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_uheapq_heapify_obj, mod_uheapq_heapify);
#if !MICROPY_ENABLE_DYNRUNTIME
STATIC const mp_rom_map_elem_t mp_module_uheapq_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uheapq) },
{ MP_ROM_QSTR(MP_QSTR_heappush), MP_ROM_PTR(&mod_uheapq_heappush_obj) },
{ MP_ROM_QSTR(MP_QSTR_heappop), MP_ROM_PTR(&mod_uheapq_heappop_obj) },
{ MP_ROM_QSTR(MP_QSTR_heapify), MP_ROM_PTR(&mod_uheapq_heapify_obj) },
};
STATIC MP_DEFINE_CONST_DICT(mp_module_uheapq_globals, mp_module_uheapq_globals_table);
const mp_obj_module_t mp_module_uheapq = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_uheapq_globals,
};
#endif
#endif //MICROPY_PY_UHEAPQ
@@ -0,0 +1,307 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2014-2019 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdio.h>
#include "py/objlist.h"
#include "py/objstringio.h"
#include "py/parsenum.h"
#include "py/runtime.h"
#include "py/stream.h"
#if MICROPY_PY_UJSON
STATIC mp_obj_t mod_ujson_dump(mp_obj_t obj, mp_obj_t stream) {
mp_get_stream_raise(stream, MP_STREAM_OP_WRITE);
mp_print_t print = {MP_OBJ_TO_PTR(stream), mp_stream_write_adaptor};
mp_obj_print_helper(&print, obj, PRINT_JSON);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_ujson_dump_obj, mod_ujson_dump);
STATIC mp_obj_t mod_ujson_dumps(mp_obj_t obj) {
vstr_t vstr;
mp_print_t print;
vstr_init_print(&vstr, 8, &print);
mp_obj_print_helper(&print, obj, PRINT_JSON);
return mp_obj_new_str_from_vstr(&mp_type_str, &vstr);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_ujson_dumps_obj, mod_ujson_dumps);
// The function below implements a simple non-recursive JSON parser.
//
// The JSON specification is at http://www.ietf.org/rfc/rfc4627.txt
// The parser here will parse any valid JSON and return the correct
// corresponding Python object. It allows through a superset of JSON, since
// it treats commas and colons as "whitespace", and doesn't care if
// brackets/braces are correctly paired. It will raise a ValueError if the
// input is outside it's specs.
//
// Most of the work is parsing the primitives (null, false, true, numbers,
// strings). It does 1 pass over the input stream. It tries to be fast and
// small in code size, while not using more RAM than necessary.
typedef struct _ujson_stream_t {
mp_obj_t stream_obj;
mp_uint_t (*read)(mp_obj_t obj, void *buf, mp_uint_t size, int *errcode);
int errcode;
byte cur;
} ujson_stream_t;
#define S_EOF (0) // null is not allowed in json stream so is ok as EOF marker
#define S_END(s) ((s).cur == S_EOF)
#define S_CUR(s) ((s).cur)
#define S_NEXT(s) (ujson_stream_next(&(s)))
STATIC byte ujson_stream_next(ujson_stream_t *s) {
mp_uint_t ret = s->read(s->stream_obj, &s->cur, 1, &s->errcode);
if (s->errcode != 0) {
mp_raise_OSError(s->errcode);
}
if (ret == 0) {
s->cur = S_EOF;
}
return s->cur;
}
STATIC mp_obj_t mod_ujson_load(mp_obj_t stream_obj) {
const mp_stream_p_t *stream_p = mp_get_stream_raise(stream_obj, MP_STREAM_OP_READ);
ujson_stream_t s = {stream_obj, stream_p->read, 0, 0};
vstr_t vstr;
vstr_init(&vstr, 8);
mp_obj_list_t stack; // we use a list as a simple stack for nested JSON
stack.len = 0;
stack.items = NULL;
mp_obj_t stack_top = MP_OBJ_NULL;
mp_obj_type_t *stack_top_type = NULL;
mp_obj_t stack_key = MP_OBJ_NULL;
S_NEXT(s);
for (;;) {
cont:
if (S_END(s)) {
break;
}
mp_obj_t next = MP_OBJ_NULL;
bool enter = false;
byte cur = S_CUR(s);
S_NEXT(s);
switch (cur) {
case ',':
case ':':
case ' ':
case '\t':
case '\n':
case '\r':
goto cont;
case 'n':
if (S_CUR(s) == 'u' && S_NEXT(s) == 'l' && S_NEXT(s) == 'l') {
S_NEXT(s);
next = mp_const_none;
} else {
goto fail;
}
break;
case 'f':
if (S_CUR(s) == 'a' && S_NEXT(s) == 'l' && S_NEXT(s) == 's' && S_NEXT(s) == 'e') {
S_NEXT(s);
next = mp_const_false;
} else {
goto fail;
}
break;
case 't':
if (S_CUR(s) == 'r' && S_NEXT(s) == 'u' && S_NEXT(s) == 'e') {
S_NEXT(s);
next = mp_const_true;
} else {
goto fail;
}
break;
case '"':
vstr_reset(&vstr);
for (; !S_END(s) && S_CUR(s) != '"';) {
byte c = S_CUR(s);
if (c == '\\') {
c = S_NEXT(s);
switch (c) {
case 'b': c = 0x08; break;
case 'f': c = 0x0c; break;
case 'n': c = 0x0a; break;
case 'r': c = 0x0d; break;
case 't': c = 0x09; break;
case 'u': {
mp_uint_t num = 0;
for (int i = 0; i < 4; i++) {
c = (S_NEXT(s) | 0x20) - '0';
if (c > 9) {
c -= ('a' - ('9' + 1));
}
num = (num << 4) | c;
}
vstr_add_char(&vstr, num);
goto str_cont;
}
}
}
vstr_add_byte(&vstr, c);
str_cont:
S_NEXT(s);
}
if (S_END(s)) {
goto fail;
}
S_NEXT(s);
next = mp_obj_new_str(vstr.buf, vstr.len);
break;
case '-':
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': {
bool flt = false;
vstr_reset(&vstr);
for (;;) {
vstr_add_byte(&vstr, cur);
cur = S_CUR(s);
if (cur == '.' || cur == 'E' || cur == 'e') {
flt = true;
} else if (cur == '+' || cur == '-' || unichar_isdigit(cur)) {
// pass
} else {
break;
}
S_NEXT(s);
}
if (flt) {
next = mp_parse_num_decimal(vstr.buf, vstr.len, false, false, NULL);
} else {
next = mp_parse_num_integer(vstr.buf, vstr.len, 10, NULL);
}
break;
}
case '[':
next = mp_obj_new_list(0, NULL);
enter = true;
break;
case '{':
next = mp_obj_new_dict(0);
enter = true;
break;
case '}':
case ']': {
if (stack_top == MP_OBJ_NULL) {
// no object at all
goto fail;
}
if (stack.len == 0) {
// finished; compound object
goto success;
}
stack.len -= 1;
stack_top = stack.items[stack.len];
stack_top_type = mp_obj_get_type(stack_top);
goto cont;
}
default:
goto fail;
}
if (stack_top == MP_OBJ_NULL) {
stack_top = next;
stack_top_type = mp_obj_get_type(stack_top);
if (!enter) {
// finished; single primitive only
goto success;
}
} else {
// append to list or dict
if (stack_top_type == &mp_type_list) {
mp_obj_list_append(stack_top, next);
} else {
if (stack_key == MP_OBJ_NULL) {
stack_key = next;
if (enter) {
goto fail;
}
} else {
mp_obj_dict_store(stack_top, stack_key, next);
stack_key = MP_OBJ_NULL;
}
}
if (enter) {
if (stack.items == NULL) {
mp_obj_list_init(&stack, 1);
stack.items[0] = stack_top;
} else {
mp_obj_list_append(MP_OBJ_FROM_PTR(&stack), stack_top);
}
stack_top = next;
stack_top_type = mp_obj_get_type(stack_top);
}
}
}
success:
// eat trailing whitespace
while (unichar_isspace(S_CUR(s))) {
S_NEXT(s);
}
if (!S_END(s)) {
// unexpected chars
goto fail;
}
if (stack_top == MP_OBJ_NULL || stack.len != 0) {
// not exactly 1 object
goto fail;
}
vstr_clear(&vstr);
return stack_top;
fail:
mp_raise_ValueError("syntax error in JSON");
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_ujson_load_obj, mod_ujson_load);
STATIC mp_obj_t mod_ujson_loads(mp_obj_t obj) {
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(obj, &bufinfo, MP_BUFFER_READ);
vstr_t vstr = {bufinfo.len, bufinfo.len, (char*)bufinfo.buf, true};
mp_obj_stringio_t sio = {{&mp_type_stringio}, &vstr, 0, MP_OBJ_NULL};
return mod_ujson_load(MP_OBJ_FROM_PTR(&sio));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_ujson_loads_obj, mod_ujson_loads);
STATIC const mp_rom_map_elem_t mp_module_ujson_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ujson) },
{ MP_ROM_QSTR(MP_QSTR_dump), MP_ROM_PTR(&mod_ujson_dump_obj) },
{ MP_ROM_QSTR(MP_QSTR_dumps), MP_ROM_PTR(&mod_ujson_dumps_obj) },
{ MP_ROM_QSTR(MP_QSTR_load), MP_ROM_PTR(&mod_ujson_load_obj) },
{ MP_ROM_QSTR(MP_QSTR_loads), MP_ROM_PTR(&mod_ujson_loads_obj) },
};
STATIC MP_DEFINE_CONST_DICT(mp_module_ujson_globals, mp_module_ujson_globals_table);
const mp_obj_module_t mp_module_ujson = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_ujson_globals,
};
#endif //MICROPY_PY_UJSON
@@ -0,0 +1,240 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Paul Sokolovsky
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <assert.h>
#include <string.h>
#include "py/runtime.h"
#if MICROPY_PY_URANDOM
// Yasmarang random number generator
// by Ilya Levin
// http://www.literatecode.com/yasmarang
// Public Domain
#if !MICROPY_ENABLE_DYNRUNTIME
STATIC uint32_t yasmarang_pad = 0xeda4baba, yasmarang_n = 69, yasmarang_d = 233;
STATIC uint8_t yasmarang_dat = 0;
#endif
STATIC uint32_t yasmarang(void)
{
yasmarang_pad += yasmarang_dat + yasmarang_d * yasmarang_n;
yasmarang_pad = (yasmarang_pad<<3) + (yasmarang_pad>>29);
yasmarang_n = yasmarang_pad | 2;
yasmarang_d ^= (yasmarang_pad<<31) + (yasmarang_pad>>1);
yasmarang_dat ^= (char) yasmarang_pad ^ (yasmarang_d>>8) ^ 1;
return (yasmarang_pad^(yasmarang_d<<5)^(yasmarang_pad>>18)^(yasmarang_dat<<1));
} /* yasmarang */
// End of Yasmarang
#if MICROPY_PY_URANDOM_EXTRA_FUNCS
// returns an unsigned integer below the given argument
// n must not be zero
STATIC uint32_t yasmarang_randbelow(uint32_t n) {
uint32_t mask = 1;
while ((n & mask) < n) {
mask = (mask << 1) | 1;
}
uint32_t r;
do {
r = yasmarang() & mask;
} while (r >= n);
return r;
}
#endif
STATIC mp_obj_t mod_urandom_getrandbits(mp_obj_t num_in) {
int n = mp_obj_get_int(num_in);
if (n > 32 || n == 0) {
mp_raise_ValueError(NULL);
}
uint32_t mask = ~0;
// Beware of C undefined behavior when shifting by >= than bit size
mask >>= (32 - n);
return mp_obj_new_int_from_uint(yasmarang() & mask);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_urandom_getrandbits_obj, mod_urandom_getrandbits);
STATIC mp_obj_t mod_urandom_seed(mp_obj_t seed_in) {
mp_uint_t seed = mp_obj_get_int_truncated(seed_in);
yasmarang_pad = seed;
yasmarang_n = 69;
yasmarang_d = 233;
yasmarang_dat = 0;
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_urandom_seed_obj, mod_urandom_seed);
#if MICROPY_PY_URANDOM_EXTRA_FUNCS
STATIC mp_obj_t mod_urandom_randrange(size_t n_args, const mp_obj_t *args) {
mp_int_t start = mp_obj_get_int(args[0]);
if (n_args == 1) {
// range(stop)
if (start > 0) {
return mp_obj_new_int(yasmarang_randbelow(start));
} else {
goto error;
}
} else {
mp_int_t stop = mp_obj_get_int(args[1]);
if (n_args == 2) {
// range(start, stop)
if (start < stop) {
return mp_obj_new_int(start + yasmarang_randbelow(stop - start));
} else {
goto error;
}
} else {
// range(start, stop, step)
mp_int_t step = mp_obj_get_int(args[2]);
mp_int_t n;
if (step > 0) {
n = (stop - start + step - 1) / step;
} else if (step < 0) {
n = (stop - start + step + 1) / step;
} else {
goto error;
}
if (n > 0) {
return mp_obj_new_int(start + step * yasmarang_randbelow(n));
} else {
goto error;
}
}
}
error:
mp_raise_ValueError(NULL);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_urandom_randrange_obj, 1, 3, mod_urandom_randrange);
STATIC mp_obj_t mod_urandom_randint(mp_obj_t a_in, mp_obj_t b_in) {
mp_int_t a = mp_obj_get_int(a_in);
mp_int_t b = mp_obj_get_int(b_in);
if (a <= b) {
return mp_obj_new_int(a + yasmarang_randbelow(b - a + 1));
} else {
mp_raise_ValueError(NULL);
}
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_urandom_randint_obj, mod_urandom_randint);
STATIC mp_obj_t mod_urandom_choice(mp_obj_t seq) {
mp_int_t len = mp_obj_get_int(mp_obj_len(seq));
if (len > 0) {
return mp_obj_subscr(seq, mp_obj_new_int(yasmarang_randbelow(len)), MP_OBJ_SENTINEL);
} else {
nlr_raise(mp_obj_new_exception(&mp_type_IndexError));
}
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_urandom_choice_obj, mod_urandom_choice);
#if MICROPY_PY_BUILTINS_FLOAT
// returns a number in the range [0..1) using Yasmarang to fill in the fraction bits
STATIC mp_float_t yasmarang_float(void) {
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
typedef uint64_t mp_float_int_t;
#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
typedef uint32_t mp_float_int_t;
#endif
union {
mp_float_t f;
#if MP_ENDIANNESS_LITTLE
struct { mp_float_int_t frc:MP_FLOAT_FRAC_BITS, exp:MP_FLOAT_EXP_BITS, sgn:1; } p;
#else
struct { mp_float_int_t sgn:1, exp:MP_FLOAT_EXP_BITS, frc:MP_FLOAT_FRAC_BITS; } p;
#endif
} u;
u.p.sgn = 0;
u.p.exp = (1 << (MP_FLOAT_EXP_BITS - 1)) - 1;
if (MP_FLOAT_FRAC_BITS <= 32) {
u.p.frc = yasmarang();
} else {
u.p.frc = ((uint64_t)yasmarang() << 32) | (uint64_t)yasmarang();
}
return u.f - 1;
}
STATIC mp_obj_t mod_urandom_random(void) {
return mp_obj_new_float(yasmarang_float());
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_urandom_random_obj, mod_urandom_random);
STATIC mp_obj_t mod_urandom_uniform(mp_obj_t a_in, mp_obj_t b_in) {
mp_float_t a = mp_obj_get_float(a_in);
mp_float_t b = mp_obj_get_float(b_in);
return mp_obj_new_float(a + (b - a) * yasmarang_float());
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_urandom_uniform_obj, mod_urandom_uniform);
#endif
#endif // MICROPY_PY_URANDOM_EXTRA_FUNCS
#ifdef MICROPY_PY_URANDOM_SEED_INIT_FUNC
STATIC mp_obj_t mod_urandom___init__() {
mod_urandom_seed(MP_OBJ_NEW_SMALL_INT(MICROPY_PY_URANDOM_SEED_INIT_FUNC));
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_urandom___init___obj, mod_urandom___init__);
#endif
#if !MICROPY_ENABLE_DYNRUNTIME
STATIC const mp_rom_map_elem_t mp_module_urandom_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_urandom) },
#ifdef MICROPY_PY_URANDOM_SEED_INIT_FUNC
{ MP_ROM_QSTR(MP_QSTR___init__), MP_ROM_PTR(&mod_urandom___init___obj) },
#endif
{ MP_ROM_QSTR(MP_QSTR_getrandbits), MP_ROM_PTR(&mod_urandom_getrandbits_obj) },
{ MP_ROM_QSTR(MP_QSTR_seed), MP_ROM_PTR(&mod_urandom_seed_obj) },
#if MICROPY_PY_URANDOM_EXTRA_FUNCS
{ MP_ROM_QSTR(MP_QSTR_randrange), MP_ROM_PTR(&mod_urandom_randrange_obj) },
{ MP_ROM_QSTR(MP_QSTR_randint), MP_ROM_PTR(&mod_urandom_randint_obj) },
{ MP_ROM_QSTR(MP_QSTR_choice), MP_ROM_PTR(&mod_urandom_choice_obj) },
#if MICROPY_PY_BUILTINS_FLOAT
{ MP_ROM_QSTR(MP_QSTR_random), MP_ROM_PTR(&mod_urandom_random_obj) },
{ MP_ROM_QSTR(MP_QSTR_uniform), MP_ROM_PTR(&mod_urandom_uniform_obj) },
#endif
#endif
};
STATIC MP_DEFINE_CONST_DICT(mp_module_urandom_globals, mp_module_urandom_globals_table);
const mp_obj_module_t mp_module_urandom = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_urandom_globals,
};
#endif
#endif //MICROPY_PY_URANDOM
@@ -0,0 +1,477 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2014 Paul Sokolovsky
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include "py/runtime.h"
#include "py/binary.h"
#include "py/objstr.h"
#include "py/stackctrl.h"
#if MICROPY_PY_URE
#define re1_5_stack_chk() MP_STACK_CHECK()
#include "re1.5/re1.5.h"
#define FLAG_DEBUG 0x1000
typedef struct _mp_obj_re_t {
mp_obj_base_t base;
ByteProg re;
} mp_obj_re_t;
typedef struct _mp_obj_match_t {
mp_obj_base_t base;
int num_matches;
mp_obj_t str;
const char *caps[0];
} mp_obj_match_t;
STATIC void match_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
(void)kind;
mp_obj_match_t *self = MP_OBJ_TO_PTR(self_in);
mp_printf(print, "<match num=%d>", self->num_matches);
}
STATIC mp_obj_t match_group(mp_obj_t self_in, mp_obj_t no_in) {
mp_obj_match_t *self = MP_OBJ_TO_PTR(self_in);
mp_int_t no = mp_obj_get_int(no_in);
if (no < 0 || no >= self->num_matches) {
nlr_raise(mp_obj_new_exception_arg1(&mp_type_IndexError, no_in));
}
const char *start = self->caps[no * 2];
if (start == NULL) {
// no match for this group
return mp_const_none;
}
return mp_obj_new_str_of_type(mp_obj_get_type(self->str),
(const byte*)start, self->caps[no * 2 + 1] - start);
}
MP_DEFINE_CONST_FUN_OBJ_2(match_group_obj, match_group);
#if MICROPY_PY_URE_MATCH_GROUPS
STATIC mp_obj_t match_groups(mp_obj_t self_in) {
mp_obj_match_t *self = MP_OBJ_TO_PTR(self_in);
if (self->num_matches <= 1) {
return mp_const_empty_tuple;
}
mp_obj_tuple_t *groups = MP_OBJ_TO_PTR(mp_obj_new_tuple(self->num_matches - 1, NULL));
for (int i = 1; i < self->num_matches; ++i) {
groups->items[i - 1] = match_group(self_in, MP_OBJ_NEW_SMALL_INT(i));
}
return MP_OBJ_FROM_PTR(groups);
}
MP_DEFINE_CONST_FUN_OBJ_1(match_groups_obj, match_groups);
#endif
#if MICROPY_PY_URE_MATCH_SPAN_START_END
STATIC void match_span_helper(size_t n_args, const mp_obj_t *args, mp_obj_t span[2]) {
mp_obj_match_t *self = MP_OBJ_TO_PTR(args[0]);
mp_int_t no = 0;
if (n_args == 2) {
no = mp_obj_get_int(args[1]);
if (no < 0 || no >= self->num_matches) {
nlr_raise(mp_obj_new_exception_arg1(&mp_type_IndexError, args[1]));
}
}
mp_int_t s = -1;
mp_int_t e = -1;
const char *start = self->caps[no * 2];
if (start != NULL) {
// have a match for this group
const char *begin = mp_obj_str_get_str(self->str);
s = start - begin;
e = self->caps[no * 2 + 1] - begin;
}
span[0] = mp_obj_new_int(s);
span[1] = mp_obj_new_int(e);
}
STATIC mp_obj_t match_span(size_t n_args, const mp_obj_t *args) {
mp_obj_t span[2];
match_span_helper(n_args, args, span);
return mp_obj_new_tuple(2, span);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(match_span_obj, 1, 2, match_span);
STATIC mp_obj_t match_start(size_t n_args, const mp_obj_t *args) {
mp_obj_t span[2];
match_span_helper(n_args, args, span);
return span[0];
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(match_start_obj, 1, 2, match_start);
STATIC mp_obj_t match_end(size_t n_args, const mp_obj_t *args) {
mp_obj_t span[2];
match_span_helper(n_args, args, span);
return span[1];
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(match_end_obj, 1, 2, match_end);
#endif
#if !MICROPY_ENABLE_DYNRUNTIME
STATIC const mp_rom_map_elem_t match_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_group), MP_ROM_PTR(&match_group_obj) },
#if MICROPY_PY_URE_MATCH_GROUPS
{ MP_ROM_QSTR(MP_QSTR_groups), MP_ROM_PTR(&match_groups_obj) },
#endif
#if MICROPY_PY_URE_MATCH_SPAN_START_END
{ MP_ROM_QSTR(MP_QSTR_span), MP_ROM_PTR(&match_span_obj) },
{ MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&match_start_obj) },
{ MP_ROM_QSTR(MP_QSTR_end), MP_ROM_PTR(&match_end_obj) },
#endif
};
STATIC MP_DEFINE_CONST_DICT(match_locals_dict, match_locals_dict_table);
STATIC const mp_obj_type_t match_type = {
{ &mp_type_type },
.name = MP_QSTR_match,
.print = match_print,
.locals_dict = (void*)&match_locals_dict,
};
#endif
STATIC void re_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
(void)kind;
mp_obj_re_t *self = MP_OBJ_TO_PTR(self_in);
mp_printf(print, "<re %p>", self);
}
STATIC mp_obj_t ure_exec(bool is_anchored, uint n_args, const mp_obj_t *args) {
(void)n_args;
mp_obj_re_t *self = MP_OBJ_TO_PTR(args[0]);
Subject subj;
size_t len;
subj.begin = mp_obj_str_get_data(args[1], &len);
subj.end = subj.begin + len;
int caps_num = (self->re.sub + 1) * 2;
mp_obj_match_t *match = m_new_obj_var(mp_obj_match_t, char*, caps_num);
// cast is a workaround for a bug in msvc: it treats const char** as a const pointer instead of a pointer to pointer to const char
memset((char*)match->caps, 0, caps_num * sizeof(char*));
int res = re1_5_recursiveloopprog(&self->re, &subj, match->caps, caps_num, is_anchored);
if (res == 0) {
m_del_var(mp_obj_match_t, char*, caps_num, match);
return mp_const_none;
}
match->base.type = &match_type;
match->num_matches = caps_num / 2; // caps_num counts start and end pointers
match->str = args[1];
return MP_OBJ_FROM_PTR(match);
}
STATIC mp_obj_t re_match(size_t n_args, const mp_obj_t *args) {
return ure_exec(true, n_args, args);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_match_obj, 2, 4, re_match);
STATIC mp_obj_t re_search(size_t n_args, const mp_obj_t *args) {
return ure_exec(false, n_args, args);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_search_obj, 2, 4, re_search);
STATIC mp_obj_t re_split(size_t n_args, const mp_obj_t *args) {
mp_obj_re_t *self = MP_OBJ_TO_PTR(args[0]);
Subject subj;
size_t len;
const mp_obj_type_t *str_type = mp_obj_get_type(args[1]);
subj.begin = mp_obj_str_get_data(args[1], &len);
subj.end = subj.begin + len;
int caps_num = (self->re.sub + 1) * 2;
int maxsplit = 0;
if (n_args > 2) {
maxsplit = mp_obj_get_int(args[2]);
}
mp_obj_t retval = mp_obj_new_list(0, NULL);
const char **caps = mp_local_alloc(caps_num * sizeof(char*));
while (true) {
// cast is a workaround for a bug in msvc: it treats const char** as a const pointer instead of a pointer to pointer to const char
memset((char**)caps, 0, caps_num * sizeof(char*));
int res = re1_5_recursiveloopprog(&self->re, &subj, caps, caps_num, false);
// if we didn't have a match, or had an empty match, it's time to stop
if (!res || caps[0] == caps[1]) {
break;
}
mp_obj_t s = mp_obj_new_str_of_type(str_type, (const byte*)subj.begin, caps[0] - subj.begin);
mp_obj_list_append(retval, s);
if (self->re.sub > 0) {
mp_raise_NotImplementedError("Splitting with sub-captures");
}
subj.begin = caps[1];
if (maxsplit > 0 && --maxsplit == 0) {
break;
}
}
// cast is a workaround for a bug in msvc (see above)
mp_local_free((char**)caps);
mp_obj_t s = mp_obj_new_str_of_type(str_type, (const byte*)subj.begin, subj.end - subj.begin);
mp_obj_list_append(retval, s);
return retval;
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_split_obj, 2, 3, re_split);
#if MICROPY_PY_URE_SUB
STATIC mp_obj_t re_sub_helper(mp_obj_t self_in, size_t n_args, const mp_obj_t *args) {
mp_obj_re_t *self = MP_OBJ_TO_PTR(self_in);
mp_obj_t replace = args[1];
mp_obj_t where = args[2];
mp_int_t count = 0;
if (n_args > 3) {
count = mp_obj_get_int(args[3]);
// Note: flags are currently ignored
}
size_t where_len;
const char *where_str = mp_obj_str_get_data(where, &where_len);
Subject subj;
subj.begin = where_str;
subj.end = subj.begin + where_len;
int caps_num = (self->re.sub + 1) * 2;
vstr_t vstr_return;
vstr_return.buf = NULL; // We'll init the vstr after the first match
mp_obj_match_t *match = mp_local_alloc(sizeof(mp_obj_match_t) + caps_num * sizeof(char*));
match->base.type = &match_type;
match->num_matches = caps_num / 2; // caps_num counts start and end pointers
match->str = where;
for (;;) {
// cast is a workaround for a bug in msvc: it treats const char** as a const pointer instead of a pointer to pointer to const char
memset((char*)match->caps, 0, caps_num * sizeof(char*));
int res = re1_5_recursiveloopprog(&self->re, &subj, match->caps, caps_num, false);
// If we didn't have a match, or had an empty match, it's time to stop
if (!res || match->caps[0] == match->caps[1]) {
break;
}
// Initialise the vstr if it's not already
if (vstr_return.buf == NULL) {
vstr_init(&vstr_return, match->caps[0] - subj.begin);
}
// Add pre-match string
vstr_add_strn(&vstr_return, subj.begin, match->caps[0] - subj.begin);
// Get replacement string
const char* repl = mp_obj_str_get_str((mp_obj_is_callable(replace) ? mp_call_function_1(replace, MP_OBJ_FROM_PTR(match)) : replace));
// Append replacement string to result, substituting any regex groups
while (*repl != '\0') {
if (*repl == '\\') {
++repl;
bool is_g_format = false;
if (*repl == 'g' && repl[1] == '<') {
// Group specified with syntax "\g<number>"
repl += 2;
is_g_format = true;
}
if ('0' <= *repl && *repl <= '9') {
// Group specified with syntax "\g<number>" or "\number"
unsigned int match_no = 0;
do {
match_no = match_no * 10 + (*repl++ - '0');
} while ('0' <= *repl && *repl <= '9');
if (is_g_format && *repl == '>') {
++repl;
}
if (match_no >= (unsigned int)match->num_matches) {
nlr_raise(mp_obj_new_exception_arg1(&mp_type_IndexError, MP_OBJ_NEW_SMALL_INT(match_no)));
}
const char *start_match = match->caps[match_no * 2];
if (start_match != NULL) {
// Add the substring matched by group
const char *end_match = match->caps[match_no * 2 + 1];
vstr_add_strn(&vstr_return, start_match, end_match - start_match);
}
}
} else {
// Just add the current byte from the replacement string
vstr_add_byte(&vstr_return, *repl++);
}
}
// Move start pointer to end of last match
subj.begin = match->caps[1];
// Stop substitutions if count was given and gets to 0
if (count > 0 && --count == 0) {
break;
}
}
mp_local_free(match);
if (vstr_return.buf == NULL) {
// Optimisation for case of no substitutions
return where;
}
// Add post-match string
vstr_add_strn(&vstr_return, subj.begin, subj.end - subj.begin);
return mp_obj_new_str_from_vstr(mp_obj_get_type(where), &vstr_return);
}
STATIC mp_obj_t re_sub(size_t n_args, const mp_obj_t *args) {
return re_sub_helper(args[0], n_args, args);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_sub_obj, 3, 5, re_sub);
#endif
#if !MICROPY_ENABLE_DYNRUNTIME
STATIC const mp_rom_map_elem_t re_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_match), MP_ROM_PTR(&re_match_obj) },
{ MP_ROM_QSTR(MP_QSTR_search), MP_ROM_PTR(&re_search_obj) },
{ MP_ROM_QSTR(MP_QSTR_split), MP_ROM_PTR(&re_split_obj) },
#if MICROPY_PY_URE_SUB
{ MP_ROM_QSTR(MP_QSTR_sub), MP_ROM_PTR(&re_sub_obj) },
#endif
};
STATIC MP_DEFINE_CONST_DICT(re_locals_dict, re_locals_dict_table);
STATIC const mp_obj_type_t re_type = {
{ &mp_type_type },
.name = MP_QSTR_ure,
.print = re_print,
.locals_dict = (void*)&re_locals_dict,
};
#endif
STATIC mp_obj_t mod_re_compile(size_t n_args, const mp_obj_t *args) {
(void)n_args;
const char *re_str = mp_obj_str_get_str(args[0]);
int size = re1_5_sizecode(re_str);
if (size == -1) {
goto error;
}
mp_obj_re_t *o = m_new_obj_var(mp_obj_re_t, char, size);
o->base.type = &re_type;
#if MICROPY_PY_URE_DEBUG
int flags = 0;
if (n_args > 1) {
flags = mp_obj_get_int(args[1]);
}
#endif
int error = re1_5_compilecode(&o->re, re_str);
if (error != 0) {
error:
mp_raise_ValueError("Error in regex");
}
#if MICROPY_PY_URE_DEBUG
if (flags & FLAG_DEBUG) {
re1_5_dumpcode(&o->re);
}
#endif
return MP_OBJ_FROM_PTR(o);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_re_compile_obj, 1, 2, mod_re_compile);
STATIC mp_obj_t mod_re_exec(bool is_anchored, uint n_args, const mp_obj_t *args) {
(void)n_args;
mp_obj_t self = mod_re_compile(1, args);
const mp_obj_t args2[] = {self, args[1]};
mp_obj_t match = ure_exec(is_anchored, 2, args2);
return match;
}
STATIC mp_obj_t mod_re_match(size_t n_args, const mp_obj_t *args) {
return mod_re_exec(true, n_args, args);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_re_match_obj, 2, 4, mod_re_match);
STATIC mp_obj_t mod_re_search(size_t n_args, const mp_obj_t *args) {
return mod_re_exec(false, n_args, args);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_re_search_obj, 2, 4, mod_re_search);
#if MICROPY_PY_URE_SUB
STATIC mp_obj_t mod_re_sub(size_t n_args, const mp_obj_t *args) {
mp_obj_t self = mod_re_compile(1, args);
return re_sub_helper(self, n_args, args);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_re_sub_obj, 3, 5, mod_re_sub);
#endif
#if !MICROPY_ENABLE_DYNRUNTIME
STATIC const mp_rom_map_elem_t mp_module_re_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ure) },
{ MP_ROM_QSTR(MP_QSTR_compile), MP_ROM_PTR(&mod_re_compile_obj) },
{ MP_ROM_QSTR(MP_QSTR_match), MP_ROM_PTR(&mod_re_match_obj) },
{ MP_ROM_QSTR(MP_QSTR_search), MP_ROM_PTR(&mod_re_search_obj) },
#if MICROPY_PY_URE_SUB
{ MP_ROM_QSTR(MP_QSTR_sub), MP_ROM_PTR(&mod_re_sub_obj) },
#endif
#if MICROPY_PY_URE_DEBUG
{ MP_ROM_QSTR(MP_QSTR_DEBUG), MP_ROM_INT(FLAG_DEBUG) },
#endif
};
STATIC MP_DEFINE_CONST_DICT(mp_module_re_globals, mp_module_re_globals_table);
const mp_obj_module_t mp_module_ure = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_re_globals,
};
#endif
// Source files #include'd here to make sure they're compiled in
// only if module is enabled by config setting.
#define re1_5_fatal(x) assert(!x)
#include "re1.5/compilecode.c"
#if MICROPY_PY_URE_DEBUG
#include "re1.5/dumpcode.c"
#endif
#include "re1.5/recursiveloop.c"
#include "re1.5/charclass.c"
#endif //MICROPY_PY_URE
@@ -0,0 +1,379 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2014 Damien P. George
* Copyright (c) 2015-2017 Paul Sokolovsky
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/mpconfig.h"
#if MICROPY_PY_USELECT
#include <stdio.h>
#include "py/runtime.h"
#include "py/obj.h"
#include "py/objlist.h"
#include "py/stream.h"
#include "py/mperrno.h"
#include "py/mphal.h"
// Flags for poll()
#define FLAG_ONESHOT (1)
/// \module select - Provides select function to wait for events on a stream
///
/// This module provides the select function.
typedef struct _poll_obj_t {
mp_obj_t obj;
mp_uint_t (*ioctl)(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode);
mp_uint_t flags;
mp_uint_t flags_ret;
} poll_obj_t;
STATIC void poll_map_add(mp_map_t *poll_map, const mp_obj_t *obj, mp_uint_t obj_len, mp_uint_t flags, bool or_flags) {
for (mp_uint_t i = 0; i < obj_len; i++) {
mp_map_elem_t *elem = mp_map_lookup(poll_map, mp_obj_id(obj[i]), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
if (elem->value == MP_OBJ_NULL) {
// object not found; get its ioctl and add it to the poll list
const mp_stream_p_t *stream_p = mp_get_stream_raise(obj[i], MP_STREAM_OP_IOCTL);
poll_obj_t *poll_obj = m_new_obj(poll_obj_t);
poll_obj->obj = obj[i];
poll_obj->ioctl = stream_p->ioctl;
poll_obj->flags = flags;
poll_obj->flags_ret = 0;
elem->value = MP_OBJ_FROM_PTR(poll_obj);
} else {
// object exists; update its flags
if (or_flags) {
((poll_obj_t*)MP_OBJ_TO_PTR(elem->value))->flags |= flags;
} else {
((poll_obj_t*)MP_OBJ_TO_PTR(elem->value))->flags = flags;
}
}
}
}
// poll each object in the map
STATIC mp_uint_t poll_map_poll(mp_map_t *poll_map, size_t *rwx_num) {
mp_uint_t n_ready = 0;
for (mp_uint_t i = 0; i < poll_map->alloc; ++i) {
if (!mp_map_slot_is_filled(poll_map, i)) {
continue;
}
poll_obj_t *poll_obj = MP_OBJ_TO_PTR(poll_map->table[i].value);
int errcode;
mp_int_t ret = poll_obj->ioctl(poll_obj->obj, MP_STREAM_POLL, poll_obj->flags, &errcode);
poll_obj->flags_ret = ret;
if (ret == -1) {
// error doing ioctl
mp_raise_OSError(errcode);
}
if (ret != 0) {
// object is ready
n_ready += 1;
if (rwx_num != NULL) {
if (ret & MP_STREAM_POLL_RD) {
rwx_num[0] += 1;
}
if (ret & MP_STREAM_POLL_WR) {
rwx_num[1] += 1;
}
if ((ret & ~(MP_STREAM_POLL_RD | MP_STREAM_POLL_WR)) != 0) {
rwx_num[2] += 1;
}
}
}
}
return n_ready;
}
/// \function select(rlist, wlist, xlist[, timeout])
STATIC mp_obj_t select_select(size_t n_args, const mp_obj_t *args) {
// get array data from tuple/list arguments
size_t rwx_len[3];
mp_obj_t *r_array, *w_array, *x_array;
mp_obj_get_array(args[0], &rwx_len[0], &r_array);
mp_obj_get_array(args[1], &rwx_len[1], &w_array);
mp_obj_get_array(args[2], &rwx_len[2], &x_array);
// get timeout
mp_uint_t timeout = -1;
if (n_args == 4) {
if (args[3] != mp_const_none) {
#if MICROPY_PY_BUILTINS_FLOAT
float timeout_f = mp_obj_get_float(args[3]);
if (timeout_f >= 0) {
timeout = (mp_uint_t)(timeout_f * 1000);
}
#else
timeout = mp_obj_get_int(args[3]) * 1000;
#endif
}
}
// merge separate lists and get the ioctl function for each object
mp_map_t poll_map;
mp_map_init(&poll_map, rwx_len[0] + rwx_len[1] + rwx_len[2]);
poll_map_add(&poll_map, r_array, rwx_len[0], MP_STREAM_POLL_RD, true);
poll_map_add(&poll_map, w_array, rwx_len[1], MP_STREAM_POLL_WR, true);
poll_map_add(&poll_map, x_array, rwx_len[2], MP_STREAM_POLL_ERR | MP_STREAM_POLL_HUP, true);
mp_uint_t start_tick = mp_hal_ticks_ms();
rwx_len[0] = rwx_len[1] = rwx_len[2] = 0;
for (;;) {
// poll the objects
mp_uint_t n_ready = poll_map_poll(&poll_map, rwx_len);
if (n_ready > 0 || (timeout != -1 && mp_hal_ticks_ms() - start_tick >= timeout)) {
// one or more objects are ready, or we had a timeout
mp_obj_t list_array[3];
list_array[0] = mp_obj_new_list(rwx_len[0], NULL);
list_array[1] = mp_obj_new_list(rwx_len[1], NULL);
list_array[2] = mp_obj_new_list(rwx_len[2], NULL);
rwx_len[0] = rwx_len[1] = rwx_len[2] = 0;
for (mp_uint_t i = 0; i < poll_map.alloc; ++i) {
if (!mp_map_slot_is_filled(&poll_map, i)) {
continue;
}
poll_obj_t *poll_obj = MP_OBJ_TO_PTR(poll_map.table[i].value);
if (poll_obj->flags_ret & MP_STREAM_POLL_RD) {
((mp_obj_list_t*)MP_OBJ_TO_PTR(list_array[0]))->items[rwx_len[0]++] = poll_obj->obj;
}
if (poll_obj->flags_ret & MP_STREAM_POLL_WR) {
((mp_obj_list_t*)MP_OBJ_TO_PTR(list_array[1]))->items[rwx_len[1]++] = poll_obj->obj;
}
if ((poll_obj->flags_ret & ~(MP_STREAM_POLL_RD | MP_STREAM_POLL_WR)) != 0) {
((mp_obj_list_t*)MP_OBJ_TO_PTR(list_array[2]))->items[rwx_len[2]++] = poll_obj->obj;
}
}
mp_map_deinit(&poll_map);
return mp_obj_new_tuple(3, list_array);
}
MICROPY_EVENT_POLL_HOOK
}
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_select_select_obj, 3, 4, select_select);
/// \class Poll - poll class
typedef struct _mp_obj_poll_t {
mp_obj_base_t base;
mp_map_t poll_map;
short iter_cnt;
short iter_idx;
int flags;
// callee-owned tuple
mp_obj_t ret_tuple;
} mp_obj_poll_t;
/// \method register(obj[, eventmask])
STATIC mp_obj_t poll_register(size_t n_args, const mp_obj_t *args) {
mp_obj_poll_t *self = MP_OBJ_TO_PTR(args[0]);
mp_uint_t flags;
if (n_args == 3) {
flags = mp_obj_get_int(args[2]);
} else {
flags = MP_STREAM_POLL_RD | MP_STREAM_POLL_WR;
}
poll_map_add(&self->poll_map, &args[1], 1, flags, false);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_register_obj, 2, 3, poll_register);
/// \method unregister(obj)
STATIC mp_obj_t poll_unregister(mp_obj_t self_in, mp_obj_t obj_in) {
mp_obj_poll_t *self = MP_OBJ_TO_PTR(self_in);
mp_map_lookup(&self->poll_map, mp_obj_id(obj_in), MP_MAP_LOOKUP_REMOVE_IF_FOUND);
// TODO raise KeyError if obj didn't exist in map
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_2(poll_unregister_obj, poll_unregister);
/// \method modify(obj, eventmask)
STATIC mp_obj_t poll_modify(mp_obj_t self_in, mp_obj_t obj_in, mp_obj_t eventmask_in) {
mp_obj_poll_t *self = MP_OBJ_TO_PTR(self_in);
mp_map_elem_t *elem = mp_map_lookup(&self->poll_map, mp_obj_id(obj_in), MP_MAP_LOOKUP);
if (elem == NULL) {
mp_raise_OSError(MP_ENOENT);
}
((poll_obj_t*)MP_OBJ_TO_PTR(elem->value))->flags = mp_obj_get_int(eventmask_in);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_3(poll_modify_obj, poll_modify);
STATIC mp_uint_t poll_poll_internal(uint n_args, const mp_obj_t *args) {
mp_obj_poll_t *self = MP_OBJ_TO_PTR(args[0]);
// work out timeout (its given already in ms)
mp_uint_t timeout = -1;
int flags = 0;
if (n_args >= 2) {
if (args[1] != mp_const_none) {
mp_int_t timeout_i = mp_obj_get_int(args[1]);
if (timeout_i >= 0) {
timeout = timeout_i;
}
}
if (n_args >= 3) {
flags = mp_obj_get_int(args[2]);
}
}
self->flags = flags;
mp_uint_t start_tick = mp_hal_ticks_ms();
mp_uint_t n_ready;
for (;;) {
// poll the objects
n_ready = poll_map_poll(&self->poll_map, NULL);
if (n_ready > 0 || (timeout != -1 && mp_hal_ticks_ms() - start_tick >= timeout)) {
break;
}
MICROPY_EVENT_POLL_HOOK
}
return n_ready;
}
STATIC mp_obj_t poll_poll(size_t n_args, const mp_obj_t *args) {
mp_obj_poll_t *self = MP_OBJ_TO_PTR(args[0]);
mp_uint_t n_ready = poll_poll_internal(n_args, args);
// one or more objects are ready, or we had a timeout
mp_obj_list_t *ret_list = MP_OBJ_TO_PTR(mp_obj_new_list(n_ready, NULL));
n_ready = 0;
for (mp_uint_t i = 0; i < self->poll_map.alloc; ++i) {
if (!mp_map_slot_is_filled(&self->poll_map, i)) {
continue;
}
poll_obj_t *poll_obj = MP_OBJ_TO_PTR(self->poll_map.table[i].value);
if (poll_obj->flags_ret != 0) {
mp_obj_t tuple[2] = {poll_obj->obj, MP_OBJ_NEW_SMALL_INT(poll_obj->flags_ret)};
ret_list->items[n_ready++] = mp_obj_new_tuple(2, tuple);
if (self->flags & FLAG_ONESHOT) {
// Don't poll next time, until new event flags will be set explicitly
poll_obj->flags = 0;
}
}
}
return MP_OBJ_FROM_PTR(ret_list);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_poll_obj, 1, 3, poll_poll);
STATIC mp_obj_t poll_ipoll(size_t n_args, const mp_obj_t *args) {
mp_obj_poll_t *self = MP_OBJ_TO_PTR(args[0]);
if (self->ret_tuple == MP_OBJ_NULL) {
self->ret_tuple = mp_obj_new_tuple(2, NULL);
}
int n_ready = poll_poll_internal(n_args, args);
self->iter_cnt = n_ready;
self->iter_idx = 0;
return args[0];
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_ipoll_obj, 1, 3, poll_ipoll);
STATIC mp_obj_t poll_iternext(mp_obj_t self_in) {
mp_obj_poll_t *self = MP_OBJ_TO_PTR(self_in);
if (self->iter_cnt == 0) {
return MP_OBJ_STOP_ITERATION;
}
self->iter_cnt--;
for (mp_uint_t i = self->iter_idx; i < self->poll_map.alloc; ++i) {
self->iter_idx++;
if (!mp_map_slot_is_filled(&self->poll_map, i)) {
continue;
}
poll_obj_t *poll_obj = MP_OBJ_TO_PTR(self->poll_map.table[i].value);
if (poll_obj->flags_ret != 0) {
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(self->ret_tuple);
t->items[0] = poll_obj->obj;
t->items[1] = MP_OBJ_NEW_SMALL_INT(poll_obj->flags_ret);
if (self->flags & FLAG_ONESHOT) {
// Don't poll next time, until new event flags will be set explicitly
poll_obj->flags = 0;
}
return MP_OBJ_FROM_PTR(t);
}
}
assert(!"inconsistent number of poll active entries");
self->iter_cnt = 0;
return MP_OBJ_STOP_ITERATION;
}
STATIC const mp_rom_map_elem_t poll_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_register), MP_ROM_PTR(&poll_register_obj) },
{ MP_ROM_QSTR(MP_QSTR_unregister), MP_ROM_PTR(&poll_unregister_obj) },
{ MP_ROM_QSTR(MP_QSTR_modify), MP_ROM_PTR(&poll_modify_obj) },
{ MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&poll_poll_obj) },
{ MP_ROM_QSTR(MP_QSTR_ipoll), MP_ROM_PTR(&poll_ipoll_obj) },
};
STATIC MP_DEFINE_CONST_DICT(poll_locals_dict, poll_locals_dict_table);
STATIC const mp_obj_type_t mp_type_poll = {
{ &mp_type_type },
.name = MP_QSTR_poll,
.getiter = mp_identity_getiter,
.iternext = poll_iternext,
.locals_dict = (void*)&poll_locals_dict,
};
/// \function poll()
STATIC mp_obj_t select_poll(void) {
mp_obj_poll_t *poll = m_new_obj(mp_obj_poll_t);
poll->base.type = &mp_type_poll;
mp_map_init(&poll->poll_map, 0);
poll->iter_cnt = 0;
poll->ret_tuple = MP_OBJ_NULL;
return MP_OBJ_FROM_PTR(poll);
}
MP_DEFINE_CONST_FUN_OBJ_0(mp_select_poll_obj, select_poll);
STATIC const mp_rom_map_elem_t mp_module_select_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uselect) },
{ MP_ROM_QSTR(MP_QSTR_select), MP_ROM_PTR(&mp_select_select_obj) },
{ MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&mp_select_poll_obj) },
{ MP_ROM_QSTR(MP_QSTR_POLLIN), MP_ROM_INT(MP_STREAM_POLL_RD) },
{ MP_ROM_QSTR(MP_QSTR_POLLOUT), MP_ROM_INT(MP_STREAM_POLL_WR) },
{ MP_ROM_QSTR(MP_QSTR_POLLERR), MP_ROM_INT(MP_STREAM_POLL_ERR) },
{ MP_ROM_QSTR(MP_QSTR_POLLHUP), MP_ROM_INT(MP_STREAM_POLL_HUP) },
};
STATIC MP_DEFINE_CONST_DICT(mp_module_select_globals, mp_module_select_globals_table);
const mp_obj_module_t mp_module_uselect = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_select_globals,
};
#endif // MICROPY_PY_USELECT
@@ -0,0 +1,340 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2019 Paul Sokolovsky
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdio.h>
#include <string.h>
#include "py/runtime.h"
#include "py/stream.h"
#include "py/objstr.h"
#if MICROPY_PY_USSL && MICROPY_SSL_AXTLS
#include "ssl.h"
typedef struct _mp_obj_ssl_socket_t {
mp_obj_base_t base;
mp_obj_t sock;
SSL_CTX *ssl_ctx;
SSL *ssl_sock;
byte *buf;
uint32_t bytes_left;
bool blocking;
} mp_obj_ssl_socket_t;
struct ssl_args {
mp_arg_val_t key;
mp_arg_val_t cert;
mp_arg_val_t server_side;
mp_arg_val_t server_hostname;
mp_arg_val_t do_handshake;
};
STATIC const mp_obj_type_t ussl_socket_type;
// Table of error strings corresponding to SSL_xxx error codes.
STATIC const char *const ssl_error_tab1[] = {
"NOT_OK",
"DEAD",
"CLOSE_NOTIFY",
"EAGAIN",
};
STATIC const char *const ssl_error_tab2[] = {
"CONN_LOST",
"RECORD_OVERFLOW",
"SOCK_SETUP_FAILURE",
NULL,
"INVALID_HANDSHAKE",
"INVALID_PROT_MSG",
"INVALID_HMAC",
"INVALID_VERSION",
"UNSUPPORTED_EXTENSION",
"INVALID_SESSION",
"NO_CIPHER",
"INVALID_CERT_HASH_ALG",
"BAD_CERTIFICATE",
"INVALID_KEY",
NULL,
"FINISHED_INVALID",
"NO_CERT_DEFINED",
"NO_CLIENT_RENOG",
"NOT_SUPPORTED",
};
STATIC NORETURN void ussl_raise_error(int err) {
MP_STATIC_ASSERT(SSL_NOT_OK - 3 == SSL_EAGAIN);
MP_STATIC_ASSERT(SSL_ERROR_CONN_LOST - 18 == SSL_ERROR_NOT_SUPPORTED);
// Check if err corresponds to something in one of the error string tables.
const char *errstr = NULL;
if (SSL_NOT_OK >= err && err >= SSL_EAGAIN) {
errstr = ssl_error_tab1[SSL_NOT_OK - err];
} else if (SSL_ERROR_CONN_LOST >= err && err >= SSL_ERROR_NOT_SUPPORTED) {
errstr = ssl_error_tab2[SSL_ERROR_CONN_LOST - err];
}
// Unknown error, just raise the error code.
if (errstr == NULL) {
mp_raise_OSError(err);
}
// Construct string object.
mp_obj_str_t *o_str = m_new_obj_maybe(mp_obj_str_t);
if (o_str == NULL) {
mp_raise_OSError(err);
}
o_str->base.type = &mp_type_str;
o_str->data = (const byte *)errstr;
o_str->len = strlen((char *)o_str->data);
o_str->hash = qstr_compute_hash(o_str->data, o_str->len);
// Raise OSError(err, str).
mp_obj_t args[2] = { MP_OBJ_NEW_SMALL_INT(err), MP_OBJ_FROM_PTR(o_str)};
nlr_raise(mp_obj_exception_make_new(&mp_type_OSError, 2, 0, args));
}
STATIC mp_obj_ssl_socket_t *ussl_socket_new(mp_obj_t sock, struct ssl_args *args) {
#if MICROPY_PY_USSL_FINALISER
mp_obj_ssl_socket_t *o = m_new_obj_with_finaliser(mp_obj_ssl_socket_t);
#else
mp_obj_ssl_socket_t *o = m_new_obj(mp_obj_ssl_socket_t);
#endif
o->base.type = &ussl_socket_type;
o->buf = NULL;
o->bytes_left = 0;
o->sock = sock;
o->blocking = true;
uint32_t options = SSL_SERVER_VERIFY_LATER;
if (!args->do_handshake.u_bool) {
options |= SSL_CONNECT_IN_PARTS;
}
if (args->key.u_obj != mp_const_none) {
options |= SSL_NO_DEFAULT_KEY;
}
if ((o->ssl_ctx = ssl_ctx_new(options, SSL_DEFAULT_CLNT_SESS)) == NULL) {
mp_raise_OSError(MP_EINVAL);
}
if (args->key.u_obj != mp_const_none) {
size_t len;
const byte *data = (const byte *)mp_obj_str_get_data(args->key.u_obj, &len);
int res = ssl_obj_memory_load(o->ssl_ctx, SSL_OBJ_RSA_KEY, data, len, NULL);
if (res != SSL_OK) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid key"));
}
data = (const byte *)mp_obj_str_get_data(args->cert.u_obj, &len);
res = ssl_obj_memory_load(o->ssl_ctx, SSL_OBJ_X509_CERT, data, len, NULL);
if (res != SSL_OK) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid cert"));
}
}
if (args->server_side.u_bool) {
o->ssl_sock = ssl_server_new(o->ssl_ctx, (long)sock);
} else {
SSL_EXTENSIONS *ext = ssl_ext_new();
if (args->server_hostname.u_obj != mp_const_none) {
ext->host_name = (char *)mp_obj_str_get_str(args->server_hostname.u_obj);
}
o->ssl_sock = ssl_client_new(o->ssl_ctx, (long)sock, NULL, 0, ext);
if (args->do_handshake.u_bool) {
int res = ssl_handshake_status(o->ssl_sock);
if (res != SSL_OK) {
ussl_raise_error(res);
}
}
}
return o;
}
STATIC void ussl_socket_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
(void)kind;
mp_obj_ssl_socket_t *self = MP_OBJ_TO_PTR(self_in);
mp_printf(print, "<_SSLSocket %p>", self->ssl_sock);
}
STATIC mp_uint_t ussl_socket_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) {
mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(o_in);
if (o->ssl_sock == NULL) {
*errcode = EBADF;
return MP_STREAM_ERROR;
}
while (o->bytes_left == 0) {
mp_int_t r = ssl_read(o->ssl_sock, &o->buf);
if (r == SSL_OK) {
// SSL_OK from ssl_read() means "everything is ok, but there's
// no user data yet". It may happen e.g. if handshake is not
// finished yet. The best way we can treat it is by returning
// EAGAIN. This may be a bit unexpected in blocking mode, but
// default is to perform complete handshake in constructor, so
// this should not happen in blocking mode. On the other hand,
// in nonblocking mode EAGAIN (comparing to the alternative of
// looping) is really preferrable.
if (o->blocking) {
continue;
} else {
goto eagain;
}
}
if (r < 0) {
if (r == SSL_CLOSE_NOTIFY || r == SSL_ERROR_CONN_LOST) {
// EOF
return 0;
}
if (r == SSL_EAGAIN) {
eagain:
r = MP_EAGAIN;
}
*errcode = r;
return MP_STREAM_ERROR;
}
o->bytes_left = r;
}
if (size > o->bytes_left) {
size = o->bytes_left;
}
memcpy(buf, o->buf, size);
o->buf += size;
o->bytes_left -= size;
return size;
}
STATIC mp_uint_t ussl_socket_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) {
mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(o_in);
if (o->ssl_sock == NULL) {
*errcode = EBADF;
return MP_STREAM_ERROR;
}
mp_int_t r = ssl_write(o->ssl_sock, buf, size);
if (r < 0) {
*errcode = r;
return MP_STREAM_ERROR;
}
return r;
}
STATIC mp_uint_t ussl_socket_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) {
mp_obj_ssl_socket_t *self = MP_OBJ_TO_PTR(o_in);
if (request == MP_STREAM_CLOSE && self->ssl_sock != NULL) {
ssl_free(self->ssl_sock);
ssl_ctx_free(self->ssl_ctx);
self->ssl_sock = NULL;
}
// Pass all requests down to the underlying socket
return mp_get_stream(self->sock)->ioctl(self->sock, request, arg, errcode);
}
STATIC mp_obj_t ussl_socket_setblocking(mp_obj_t self_in, mp_obj_t flag_in) {
mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(self_in);
mp_obj_t sock = o->sock;
mp_obj_t dest[3];
mp_load_method(sock, MP_QSTR_setblocking, dest);
dest[2] = flag_in;
mp_obj_t res = mp_call_method_n_kw(1, 0, dest);
o->blocking = mp_obj_is_true(flag_in);
return res;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(ussl_socket_setblocking_obj, ussl_socket_setblocking);
STATIC const mp_rom_map_elem_t ussl_socket_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
{ MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
{ MP_ROM_QSTR(MP_QSTR_setblocking), MP_ROM_PTR(&ussl_socket_setblocking_obj) },
{ MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) },
#if MICROPY_PY_USSL_FINALISER
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_stream_close_obj) },
#endif
};
STATIC MP_DEFINE_CONST_DICT(ussl_socket_locals_dict, ussl_socket_locals_dict_table);
STATIC const mp_stream_p_t ussl_socket_stream_p = {
.read = ussl_socket_read,
.write = ussl_socket_write,
.ioctl = ussl_socket_ioctl,
};
STATIC const mp_obj_type_t ussl_socket_type = {
{ &mp_type_type },
// Save on qstr's, reuse same as for module
.name = MP_QSTR_ussl,
.print = ussl_socket_print,
.getiter = NULL,
.iternext = NULL,
.protocol = &ussl_socket_stream_p,
.locals_dict = (void *)&ussl_socket_locals_dict,
};
STATIC mp_obj_t mod_ssl_wrap_socket(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
// TODO: Implement more args
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_key, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_cert, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_server_side, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
{ MP_QSTR_server_hostname, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_do_handshake, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} },
};
// TODO: Check that sock implements stream protocol
mp_obj_t sock = pos_args[0];
struct ssl_args args;
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args,
MP_ARRAY_SIZE(allowed_args), allowed_args, (mp_arg_val_t *)&args);
return MP_OBJ_FROM_PTR(ussl_socket_new(sock, &args));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_ssl_wrap_socket_obj, 1, mod_ssl_wrap_socket);
STATIC const mp_rom_map_elem_t mp_module_ssl_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ussl) },
{ MP_ROM_QSTR(MP_QSTR_wrap_socket), MP_ROM_PTR(&mod_ssl_wrap_socket_obj) },
};
STATIC MP_DEFINE_CONST_DICT(mp_module_ssl_globals, mp_module_ssl_globals_table);
const mp_obj_module_t mp_module_ussl = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&mp_module_ssl_globals,
};
#endif // MICROPY_PY_USSL
@@ -0,0 +1,420 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Linaro Ltd.
* Copyright (c) 2019 Paul Sokolovsky
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/mpconfig.h"
#if MICROPY_PY_USSL && MICROPY_SSL_MBEDTLS
#include <stdio.h>
#include <string.h>
#include <errno.h> // needed because mp_is_nonblocking_error uses system error codes
#include "py/runtime.h"
#include "py/stream.h"
#include "py/objstr.h"
// mbedtls_time_t
#include "mbedtls/platform.h"
#include "mbedtls/ssl.h"
#include "mbedtls/x509_crt.h"
#include "mbedtls/pk.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/debug.h"
#include "mbedtls/error.h"
typedef struct _mp_obj_ssl_socket_t {
mp_obj_base_t base;
mp_obj_t sock;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_ssl_context ssl;
mbedtls_ssl_config conf;
mbedtls_x509_crt cacert;
mbedtls_x509_crt cert;
mbedtls_pk_context pkey;
} mp_obj_ssl_socket_t;
struct ssl_args {
mp_arg_val_t key;
mp_arg_val_t cert;
mp_arg_val_t server_side;
mp_arg_val_t server_hostname;
mp_arg_val_t do_handshake;
};
STATIC const mp_obj_type_t ussl_socket_type;
#ifdef MBEDTLS_DEBUG_C
STATIC void mbedtls_debug(void *ctx, int level, const char *file, int line, const char *str) {
(void)ctx;
(void)level;
printf("DBG:%s:%04d: %s\n", file, line, str);
}
#endif
STATIC NORETURN void mbedtls_raise_error(int err) {
// _mbedtls_ssl_send and _mbedtls_ssl_recv (below) turn positive error codes from the
// underlying socket into negative codes to pass them through mbedtls. Here we turn them
// positive again so they get interpreted as the OSError they really are. The
// cut-off of -256 is a bit hacky, sigh.
if (err < 0 && err > -256) {
mp_raise_OSError(-err);
}
#if defined(MBEDTLS_ERROR_C)
// Including mbedtls_strerror takes about 1.5KB due to the error strings.
// MBEDTLS_ERROR_C is the define used by mbedtls to conditionally include mbedtls_strerror.
// It is set/unset in the MBEDTLS_CONFIG_FILE which is defined in the Makefile.
// Try to allocate memory for the message
#define ERR_STR_MAX 80 // mbedtls_strerror truncates if it doesn't fit
mp_obj_str_t *o_str = m_new_obj_maybe(mp_obj_str_t);
byte *o_str_buf = m_new_maybe(byte, ERR_STR_MAX);
if (o_str == NULL || o_str_buf == NULL) {
mp_raise_OSError(err);
}
// print the error message into the allocated buffer
mbedtls_strerror(err, (char *)o_str_buf, ERR_STR_MAX);
size_t len = strlen((char *)o_str_buf);
// Put the exception object together
o_str->base.type = &mp_type_str;
o_str->data = o_str_buf;
o_str->len = len;
o_str->hash = qstr_compute_hash(o_str->data, o_str->len);
// raise
mp_obj_t args[2] = { MP_OBJ_NEW_SMALL_INT(err), MP_OBJ_FROM_PTR(o_str)};
nlr_raise(mp_obj_exception_make_new(&mp_type_OSError, 2, 0, args));
#else
// mbedtls is compiled without error strings so we simply return the err number
mp_raise_OSError(err); // err is typically a large negative number
#endif
}
STATIC int _mbedtls_ssl_send(void *ctx, const byte *buf, size_t len) {
mp_obj_t sock = *(mp_obj_t *)ctx;
const mp_stream_p_t *sock_stream = mp_get_stream(sock);
int err;
mp_uint_t out_sz = sock_stream->write(sock, buf, len, &err);
if (out_sz == MP_STREAM_ERROR) {
if (mp_is_nonblocking_error(err)) {
return MBEDTLS_ERR_SSL_WANT_WRITE;
}
return -err; // convert an MP_ERRNO to something mbedtls passes through as error
} else {
return out_sz;
}
}
STATIC int _mbedtls_ssl_recv(void *ctx, byte *buf, size_t len) {
mp_obj_t sock = *(mp_obj_t *)ctx;
const mp_stream_p_t *sock_stream = mp_get_stream(sock);
int err;
mp_uint_t out_sz = sock_stream->read(sock, buf, len, &err);
if (out_sz == MP_STREAM_ERROR) {
if (mp_is_nonblocking_error(err)) {
return MBEDTLS_ERR_SSL_WANT_READ;
}
return -err;
} else {
return out_sz;
}
}
STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) {
// Verify the socket object has the full stream protocol
mp_get_stream_raise(sock, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL);
#if MICROPY_PY_USSL_FINALISER
mp_obj_ssl_socket_t *o = m_new_obj_with_finaliser(mp_obj_ssl_socket_t);
#else
mp_obj_ssl_socket_t *o = m_new_obj(mp_obj_ssl_socket_t);
#endif
o->base.type = &ussl_socket_type;
o->sock = sock;
int ret;
mbedtls_ssl_init(&o->ssl);
mbedtls_ssl_config_init(&o->conf);
mbedtls_x509_crt_init(&o->cacert);
mbedtls_x509_crt_init(&o->cert);
mbedtls_pk_init(&o->pkey);
mbedtls_ctr_drbg_init(&o->ctr_drbg);
#ifdef MBEDTLS_DEBUG_C
// Debug level (0-4)
mbedtls_debug_set_threshold(0);
#endif
mbedtls_entropy_init(&o->entropy);
const byte seed[] = "upy";
ret = mbedtls_ctr_drbg_seed(&o->ctr_drbg, mbedtls_entropy_func, &o->entropy, seed, sizeof(seed));
if (ret != 0) {
goto cleanup;
}
ret = mbedtls_ssl_config_defaults(&o->conf,
args->server_side.u_bool ? MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT);
if (ret != 0) {
goto cleanup;
}
mbedtls_ssl_conf_authmode(&o->conf, MBEDTLS_SSL_VERIFY_NONE);
mbedtls_ssl_conf_rng(&o->conf, mbedtls_ctr_drbg_random, &o->ctr_drbg);
#ifdef MBEDTLS_DEBUG_C
mbedtls_ssl_conf_dbg(&o->conf, mbedtls_debug, NULL);
#endif
ret = mbedtls_ssl_setup(&o->ssl, &o->conf);
if (ret != 0) {
goto cleanup;
}
if (args->server_hostname.u_obj != mp_const_none) {
const char *sni = mp_obj_str_get_str(args->server_hostname.u_obj);
ret = mbedtls_ssl_set_hostname(&o->ssl, sni);
if (ret != 0) {
goto cleanup;
}
}
mbedtls_ssl_set_bio(&o->ssl, &o->sock, _mbedtls_ssl_send, _mbedtls_ssl_recv, NULL);
if (args->key.u_obj != mp_const_none) {
size_t key_len;
const byte *key = (const byte *)mp_obj_str_get_data(args->key.u_obj, &key_len);
// len should include terminating null
ret = mbedtls_pk_parse_key(&o->pkey, key, key_len + 1, NULL, 0);
if (ret != 0) {
ret = MBEDTLS_ERR_PK_BAD_INPUT_DATA; // use general error for all key errors
goto cleanup;
}
size_t cert_len;
const byte *cert = (const byte *)mp_obj_str_get_data(args->cert.u_obj, &cert_len);
// len should include terminating null
ret = mbedtls_x509_crt_parse(&o->cert, cert, cert_len + 1);
if (ret != 0) {
ret = MBEDTLS_ERR_X509_BAD_INPUT_DATA; // use general error for all cert errors
goto cleanup;
}
ret = mbedtls_ssl_conf_own_cert(&o->conf, &o->cert, &o->pkey);
if (ret != 0) {
goto cleanup;
}
}
if (args->do_handshake.u_bool) {
while ((ret = mbedtls_ssl_handshake(&o->ssl)) != 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
goto cleanup;
}
}
}
return o;
cleanup:
mbedtls_pk_free(&o->pkey);
mbedtls_x509_crt_free(&o->cert);
mbedtls_x509_crt_free(&o->cacert);
mbedtls_ssl_free(&o->ssl);
mbedtls_ssl_config_free(&o->conf);
mbedtls_ctr_drbg_free(&o->ctr_drbg);
mbedtls_entropy_free(&o->entropy);
if (ret == MBEDTLS_ERR_SSL_ALLOC_FAILED) {
mp_raise_OSError(MP_ENOMEM);
} else if (ret == MBEDTLS_ERR_PK_BAD_INPUT_DATA) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid key"));
} else if (ret == MBEDTLS_ERR_X509_BAD_INPUT_DATA) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid cert"));
} else {
mbedtls_raise_error(ret);
}
}
STATIC mp_obj_t mod_ssl_getpeercert(mp_obj_t o_in, mp_obj_t binary_form) {
mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(o_in);
if (!mp_obj_is_true(binary_form)) {
mp_raise_NotImplementedError(NULL);
}
const mbedtls_x509_crt *peer_cert = mbedtls_ssl_get_peer_cert(&o->ssl);
if (peer_cert == NULL) {
return mp_const_none;
}
return mp_obj_new_bytes(peer_cert->raw.p, peer_cert->raw.len);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_ssl_getpeercert_obj, mod_ssl_getpeercert);
STATIC void socket_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
(void)kind;
mp_obj_ssl_socket_t *self = MP_OBJ_TO_PTR(self_in);
mp_printf(print, "<_SSLSocket %p>", self);
}
STATIC mp_uint_t socket_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) {
mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(o_in);
int ret = mbedtls_ssl_read(&o->ssl, buf, size);
if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
// end of stream
return 0;
}
if (ret >= 0) {
return ret;
}
if (ret == MBEDTLS_ERR_SSL_WANT_READ) {
ret = MP_EWOULDBLOCK;
} else if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
// If handshake is not finished, read attempt may end up in protocol
// wanting to write next handshake message. The same may happen with
// renegotation.
ret = MP_EWOULDBLOCK;
}
*errcode = ret;
return MP_STREAM_ERROR;
}
STATIC mp_uint_t socket_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) {
mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(o_in);
int ret = mbedtls_ssl_write(&o->ssl, buf, size);
if (ret >= 0) {
return ret;
}
if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
ret = MP_EWOULDBLOCK;
} else if (ret == MBEDTLS_ERR_SSL_WANT_READ) {
// If handshake is not finished, write attempt may end up in protocol
// wanting to read next handshake message. The same may happen with
// renegotation.
ret = MP_EWOULDBLOCK;
}
*errcode = ret;
return MP_STREAM_ERROR;
}
STATIC mp_obj_t socket_setblocking(mp_obj_t self_in, mp_obj_t flag_in) {
mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(self_in);
mp_obj_t sock = o->sock;
mp_obj_t dest[3];
mp_load_method(sock, MP_QSTR_setblocking, dest);
dest[2] = flag_in;
return mp_call_method_n_kw(1, 0, dest);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, socket_setblocking);
STATIC mp_uint_t socket_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) {
mp_obj_ssl_socket_t *self = MP_OBJ_TO_PTR(o_in);
if (request == MP_STREAM_CLOSE) {
mbedtls_pk_free(&self->pkey);
mbedtls_x509_crt_free(&self->cert);
mbedtls_x509_crt_free(&self->cacert);
mbedtls_ssl_free(&self->ssl);
mbedtls_ssl_config_free(&self->conf);
mbedtls_ctr_drbg_free(&self->ctr_drbg);
mbedtls_entropy_free(&self->entropy);
}
// Pass all requests down to the underlying socket
return mp_get_stream(self->sock)->ioctl(self->sock, request, arg, errcode);
}
STATIC const mp_rom_map_elem_t ussl_socket_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
{ MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
{ MP_ROM_QSTR(MP_QSTR_setblocking), MP_ROM_PTR(&socket_setblocking_obj) },
{ MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) },
#if MICROPY_PY_USSL_FINALISER
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_stream_close_obj) },
#endif
{ MP_ROM_QSTR(MP_QSTR_getpeercert), MP_ROM_PTR(&mod_ssl_getpeercert_obj) },
};
STATIC MP_DEFINE_CONST_DICT(ussl_socket_locals_dict, ussl_socket_locals_dict_table);
STATIC const mp_stream_p_t ussl_socket_stream_p = {
.read = socket_read,
.write = socket_write,
.ioctl = socket_ioctl,
};
STATIC const mp_obj_type_t ussl_socket_type = {
{ &mp_type_type },
// Save on qstr's, reuse same as for module
.name = MP_QSTR_ussl,
.print = socket_print,
.getiter = NULL,
.iternext = NULL,
.protocol = &ussl_socket_stream_p,
.locals_dict = (void *)&ussl_socket_locals_dict,
};
STATIC mp_obj_t mod_ssl_wrap_socket(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
// TODO: Implement more args
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_key, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_cert, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_server_side, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
{ MP_QSTR_server_hostname, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_do_handshake, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} },
};
// TODO: Check that sock implements stream protocol
mp_obj_t sock = pos_args[0];
struct ssl_args args;
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args,
MP_ARRAY_SIZE(allowed_args), allowed_args, (mp_arg_val_t *)&args);
return MP_OBJ_FROM_PTR(socket_new(sock, &args));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_ssl_wrap_socket_obj, 1, mod_ssl_wrap_socket);
STATIC const mp_rom_map_elem_t mp_module_ssl_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ussl) },
{ MP_ROM_QSTR(MP_QSTR_wrap_socket), MP_ROM_PTR(&mod_ssl_wrap_socket_obj) },
};
STATIC MP_DEFINE_CONST_DICT(mp_module_ssl_globals, mp_module_ssl_globals_table);
const mp_obj_module_t mp_module_ussl = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&mp_module_ssl_globals,
};
#endif // MICROPY_PY_USSL
@@ -0,0 +1,230 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2014 Damien P. George
* Copyright (c) 2016-2017 Paul Sokolovsky
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <string.h>
#include "py/objlist.h"
#include "py/runtime.h"
#include "py/smallint.h"
#if MICROPY_PY_UTIMEQ
#define MODULO MICROPY_PY_UTIME_TICKS_PERIOD
#define DEBUG 0
// the algorithm here is modelled on CPython's heapq.py
struct qentry {
mp_uint_t time;
mp_uint_t id;
mp_obj_t callback;
mp_obj_t args;
};
typedef struct _mp_obj_utimeq_t {
mp_obj_base_t base;
mp_uint_t alloc;
mp_uint_t len;
struct qentry items[];
} mp_obj_utimeq_t;
STATIC mp_uint_t utimeq_id;
STATIC mp_obj_utimeq_t *utimeq_get_heap(mp_obj_t heap_in) {
return MP_OBJ_TO_PTR(heap_in);
}
STATIC bool time_less_than(struct qentry *item, struct qentry *parent) {
mp_uint_t item_tm = item->time;
mp_uint_t parent_tm = parent->time;
mp_uint_t res = parent_tm - item_tm;
if (res == 0) {
// TODO: This actually should use the same "ring" logic
// as for time, to avoid artifacts when id's overflow.
return item->id < parent->id;
}
if ((mp_int_t)res < 0) {
res += MODULO;
}
return res && res < (MODULO / 2);
}
STATIC mp_obj_t utimeq_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 1, 1, false);
mp_uint_t alloc = mp_obj_get_int(args[0]);
mp_obj_utimeq_t *o = m_new_obj_var(mp_obj_utimeq_t, struct qentry, alloc);
o->base.type = type;
memset(o->items, 0, sizeof(*o->items) * alloc);
o->alloc = alloc;
o->len = 0;
return MP_OBJ_FROM_PTR(o);
}
STATIC void utimeq_heap_siftdown(mp_obj_utimeq_t *heap, mp_uint_t start_pos, mp_uint_t pos) {
struct qentry item = heap->items[pos];
while (pos > start_pos) {
mp_uint_t parent_pos = (pos - 1) >> 1;
struct qentry *parent = &heap->items[parent_pos];
bool lessthan = time_less_than(&item, parent);
if (lessthan) {
heap->items[pos] = *parent;
pos = parent_pos;
} else {
break;
}
}
heap->items[pos] = item;
}
STATIC void utimeq_heap_siftup(mp_obj_utimeq_t *heap, mp_uint_t pos) {
mp_uint_t start_pos = pos;
mp_uint_t end_pos = heap->len;
struct qentry item = heap->items[pos];
for (mp_uint_t child_pos = 2 * pos + 1; child_pos < end_pos; child_pos = 2 * pos + 1) {
// choose right child if it's <= left child
if (child_pos + 1 < end_pos) {
bool lessthan = time_less_than(&heap->items[child_pos], &heap->items[child_pos + 1]);
if (!lessthan) {
child_pos += 1;
}
}
// bubble up the smaller child
heap->items[pos] = heap->items[child_pos];
pos = child_pos;
}
heap->items[pos] = item;
utimeq_heap_siftdown(heap, start_pos, pos);
}
STATIC mp_obj_t mod_utimeq_heappush(size_t n_args, const mp_obj_t *args) {
(void)n_args;
mp_obj_t heap_in = args[0];
mp_obj_utimeq_t *heap = utimeq_get_heap(heap_in);
if (heap->len == heap->alloc) {
mp_raise_msg(&mp_type_IndexError, "queue overflow");
}
mp_uint_t l = heap->len;
heap->items[l].time = MP_OBJ_SMALL_INT_VALUE(args[1]);
heap->items[l].id = utimeq_id++;
heap->items[l].callback = args[2];
heap->items[l].args = args[3];
utimeq_heap_siftdown(heap, 0, heap->len);
heap->len++;
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_utimeq_heappush_obj, 4, 4, mod_utimeq_heappush);
STATIC mp_obj_t mod_utimeq_heappop(mp_obj_t heap_in, mp_obj_t list_ref) {
mp_obj_utimeq_t *heap = utimeq_get_heap(heap_in);
if (heap->len == 0) {
mp_raise_msg(&mp_type_IndexError, "empty heap");
}
mp_obj_list_t *ret = MP_OBJ_TO_PTR(list_ref);
if (!mp_obj_is_type(list_ref, &mp_type_list) || ret->len < 3) {
mp_raise_TypeError(NULL);
}
struct qentry *item = &heap->items[0];
ret->items[0] = MP_OBJ_NEW_SMALL_INT(item->time);
ret->items[1] = item->callback;
ret->items[2] = item->args;
heap->len -= 1;
heap->items[0] = heap->items[heap->len];
heap->items[heap->len].callback = MP_OBJ_NULL; // so we don't retain a pointer
heap->items[heap->len].args = MP_OBJ_NULL;
if (heap->len) {
utimeq_heap_siftup(heap, 0);
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_utimeq_heappop_obj, mod_utimeq_heappop);
STATIC mp_obj_t mod_utimeq_peektime(mp_obj_t heap_in) {
mp_obj_utimeq_t *heap = utimeq_get_heap(heap_in);
if (heap->len == 0) {
mp_raise_msg(&mp_type_IndexError, "empty heap");
}
struct qentry *item = &heap->items[0];
return MP_OBJ_NEW_SMALL_INT(item->time);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_utimeq_peektime_obj, mod_utimeq_peektime);
#if DEBUG
STATIC mp_obj_t mod_utimeq_dump(mp_obj_t heap_in) {
mp_obj_utimeq_t *heap = utimeq_get_heap(heap_in);
for (int i = 0; i < heap->len; i++) {
printf(UINT_FMT "\t%p\t%p\n", heap->items[i].time,
MP_OBJ_TO_PTR(heap->items[i].callback), MP_OBJ_TO_PTR(heap->items[i].args));
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_utimeq_dump_obj, mod_utimeq_dump);
#endif
STATIC mp_obj_t utimeq_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
mp_obj_utimeq_t *self = MP_OBJ_TO_PTR(self_in);
switch (op) {
case MP_UNARY_OP_BOOL: return mp_obj_new_bool(self->len != 0);
case MP_UNARY_OP_LEN: return MP_OBJ_NEW_SMALL_INT(self->len);
default: return MP_OBJ_NULL; // op not supported
}
}
STATIC const mp_rom_map_elem_t utimeq_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_push), MP_ROM_PTR(&mod_utimeq_heappush_obj) },
{ MP_ROM_QSTR(MP_QSTR_pop), MP_ROM_PTR(&mod_utimeq_heappop_obj) },
{ MP_ROM_QSTR(MP_QSTR_peektime), MP_ROM_PTR(&mod_utimeq_peektime_obj) },
#if DEBUG
{ MP_ROM_QSTR(MP_QSTR_dump), MP_ROM_PTR(&mod_utimeq_dump_obj) },
#endif
};
STATIC MP_DEFINE_CONST_DICT(utimeq_locals_dict, utimeq_locals_dict_table);
STATIC const mp_obj_type_t utimeq_type = {
{ &mp_type_type },
.name = MP_QSTR_utimeq,
.make_new = utimeq_make_new,
.unary_op = utimeq_unary_op,
.locals_dict = (void*)&utimeq_locals_dict,
};
STATIC const mp_rom_map_elem_t mp_module_utimeq_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_utimeq) },
{ MP_ROM_QSTR(MP_QSTR_utimeq), MP_ROM_PTR(&utimeq_type) },
};
STATIC MP_DEFINE_CONST_DICT(mp_module_utimeq_globals, mp_module_utimeq_globals_table);
const mp_obj_module_t mp_module_utimeq = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_utimeq_globals,
};
#endif //MICROPY_PY_UTIMEQ
@@ -0,0 +1,314 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Paul Sokolovsky
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "py/runtime.h"
#include "py/stream.h"
#include "extmod/moduwebsocket.h"
#if MICROPY_PY_UWEBSOCKET
enum { FRAME_HEADER, FRAME_OPT, PAYLOAD, CONTROL };
enum { BLOCKING_WRITE = 0x80 };
typedef struct _mp_obj_websocket_t {
mp_obj_base_t base;
mp_obj_t sock;
uint32_t msg_sz;
byte mask[4];
byte state;
byte to_recv;
byte mask_pos;
byte buf_pos;
byte buf[6];
byte opts;
// Copy of last data frame flags
byte ws_flags;
// Copy of current frame flags
byte last_flags;
} mp_obj_websocket_t;
STATIC mp_uint_t websocket_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode);
STATIC mp_obj_t websocket_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 1, 2, false);
mp_get_stream_raise(args[0], MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL);
mp_obj_websocket_t *o = m_new_obj(mp_obj_websocket_t);
o->base.type = type;
o->sock = args[0];
o->state = FRAME_HEADER;
o->to_recv = 2;
o->mask_pos = 0;
o->buf_pos = 0;
o->opts = FRAME_TXT;
if (n_args > 1 && args[1] == mp_const_true) {
o->opts |= BLOCKING_WRITE;
}
return MP_OBJ_FROM_PTR(o);
}
STATIC mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) {
mp_obj_websocket_t *self = MP_OBJ_TO_PTR(self_in);
const mp_stream_p_t *stream_p = mp_get_stream(self->sock);
while (1) {
if (self->to_recv != 0) {
mp_uint_t out_sz = stream_p->read(self->sock, self->buf + self->buf_pos, self->to_recv, errcode);
if (out_sz == 0 || out_sz == MP_STREAM_ERROR) {
return out_sz;
}
self->buf_pos += out_sz;
self->to_recv -= out_sz;
if (self->to_recv != 0) {
*errcode = MP_EAGAIN;
return MP_STREAM_ERROR;
}
}
switch (self->state) {
case FRAME_HEADER: {
// TODO: Split frame handling below is untested so far, so conservatively disable it
assert(self->buf[0] & 0x80);
// "Control frames MAY be injected in the middle of a fragmented message."
// So, they must be processed before data frames (and not alter
// self->ws_flags)
byte frame_type = self->buf[0];
self->last_flags = frame_type;
frame_type &= FRAME_OPCODE_MASK;
if ((self->buf[0] & FRAME_OPCODE_MASK) == FRAME_CONT) {
// Preserve previous frame type
self->ws_flags = (self->ws_flags & FRAME_OPCODE_MASK) | (self->buf[0] & ~FRAME_OPCODE_MASK);
} else {
self->ws_flags = self->buf[0];
}
// Reset mask in case someone will use "simplified" protocol
// without masks.
memset(self->mask, 0, sizeof(self->mask));
int to_recv = 0;
size_t sz = self->buf[1] & 0x7f;
if (sz == 126) {
// Msg size is next 2 bytes
to_recv += 2;
} else if (sz == 127) {
// Msg size is next 8 bytes
assert(0);
}
if (self->buf[1] & 0x80) {
// Next 4 bytes is mask
to_recv += 4;
}
self->buf_pos = 0;
self->to_recv = to_recv;
self->msg_sz = sz; // May be overridden by FRAME_OPT
if (to_recv != 0) {
self->state = FRAME_OPT;
} else {
if (frame_type >= FRAME_CLOSE) {
self->state = CONTROL;
} else {
self->state = PAYLOAD;
}
}
continue;
}
case FRAME_OPT: {
if ((self->buf_pos & 3) == 2) {
// First two bytes are message length
self->msg_sz = (self->buf[0] << 8) | self->buf[1];
}
if (self->buf_pos >= 4) {
// Last 4 bytes is mask
memcpy(self->mask, self->buf + self->buf_pos - 4, 4);
}
self->buf_pos = 0;
if ((self->last_flags & FRAME_OPCODE_MASK) >= FRAME_CLOSE) {
self->state = CONTROL;
} else {
self->state = PAYLOAD;
}
continue;
}
case PAYLOAD:
case CONTROL: {
mp_uint_t out_sz = 0;
if (self->msg_sz == 0) {
// In case message had zero payload
goto no_payload;
}
size_t sz = MIN(size, self->msg_sz);
out_sz = stream_p->read(self->sock, buf, sz, errcode);
if (out_sz == 0 || out_sz == MP_STREAM_ERROR) {
return out_sz;
}
sz = out_sz;
for (byte *p = buf; sz--; p++) {
*p ^= self->mask[self->mask_pos++ & 3];
}
self->msg_sz -= out_sz;
if (self->msg_sz == 0) {
byte last_state;
no_payload:
last_state = self->state;
self->state = FRAME_HEADER;
self->to_recv = 2;
self->mask_pos = 0;
self->buf_pos = 0;
// Handle control frame
if (last_state == CONTROL) {
byte frame_type = self->last_flags & FRAME_OPCODE_MASK;
if (frame_type == FRAME_CLOSE) {
static const char close_resp[2] = {0x88, 0};
int err;
websocket_write(self_in, close_resp, sizeof(close_resp), &err);
return 0;
}
//DEBUG_printf("Finished receiving ctrl message %x, ignoring\n", self->last_flags);
continue;
}
}
if (out_sz != 0) {
return out_sz;
}
// Empty (data) frame received is not EOF
continue;
}
}
}
}
STATIC mp_uint_t websocket_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) {
mp_obj_websocket_t *self = MP_OBJ_TO_PTR(self_in);
assert(size < 0x10000);
byte header[4] = {0x80 | (self->opts & FRAME_OPCODE_MASK)};
int hdr_sz;
if (size < 126) {
header[1] = size;
hdr_sz = 2;
} else {
header[1] = 126;
header[2] = size >> 8;
header[3] = size & 0xff;
hdr_sz = 4;
}
mp_obj_t dest[3];
if (self->opts & BLOCKING_WRITE) {
mp_load_method(self->sock, MP_QSTR_setblocking, dest);
dest[2] = mp_const_true;
mp_call_method_n_kw(1, 0, dest);
}
mp_uint_t out_sz = mp_stream_write_exactly(self->sock, header, hdr_sz, errcode);
if (*errcode == 0) {
out_sz = mp_stream_write_exactly(self->sock, buf, size, errcode);
}
if (self->opts & BLOCKING_WRITE) {
dest[2] = mp_const_false;
mp_call_method_n_kw(1, 0, dest);
}
if (*errcode != 0) {
return MP_STREAM_ERROR;
}
return out_sz;
}
STATIC mp_uint_t websocket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) {
mp_obj_websocket_t *self = MP_OBJ_TO_PTR(self_in);
switch (request) {
case MP_STREAM_CLOSE:
// TODO: Send close signaling to the other side, otherwise it's
// abrupt close (connection abort).
mp_stream_close(self->sock);
return 0;
case MP_STREAM_GET_DATA_OPTS:
return self->ws_flags & FRAME_OPCODE_MASK;
case MP_STREAM_SET_DATA_OPTS: {
int cur = self->opts & FRAME_OPCODE_MASK;
self->opts = (self->opts & ~FRAME_OPCODE_MASK) | (arg & FRAME_OPCODE_MASK);
return cur;
}
default:
*errcode = MP_EINVAL;
return MP_STREAM_ERROR;
}
}
STATIC const mp_rom_map_elem_t websocket_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
{ MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
{ MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&mp_stream_ioctl_obj) },
{ MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) },
};
STATIC MP_DEFINE_CONST_DICT(websocket_locals_dict, websocket_locals_dict_table);
STATIC const mp_stream_p_t websocket_stream_p = {
.read = websocket_read,
.write = websocket_write,
.ioctl = websocket_ioctl,
};
STATIC const mp_obj_type_t websocket_type = {
{ &mp_type_type },
.name = MP_QSTR_websocket,
.make_new = websocket_make_new,
.protocol = &websocket_stream_p,
.locals_dict = (void*)&websocket_locals_dict,
};
STATIC const mp_rom_map_elem_t uwebsocket_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uwebsocket) },
{ MP_ROM_QSTR(MP_QSTR_websocket), MP_ROM_PTR(&websocket_type) },
};
STATIC MP_DEFINE_CONST_DICT(uwebsocket_module_globals, uwebsocket_module_globals_table);
const mp_obj_module_t mp_module_uwebsocket = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&uwebsocket_module_globals,
};
#endif // MICROPY_PY_UWEBSOCKET
@@ -0,0 +1,10 @@
#ifndef MICROPY_INCLUDED_EXTMOD_MODUWEBSOCKET_H
#define MICROPY_INCLUDED_EXTMOD_MODUWEBSOCKET_H
#define FRAME_OPCODE_MASK 0x0f
enum {
FRAME_CONT, FRAME_TXT, FRAME_BIN,
FRAME_CLOSE = 0x8, FRAME_PING, FRAME_PONG
};
#endif // MICROPY_INCLUDED_EXTMOD_MODUWEBSOCKET_H
@@ -0,0 +1,232 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2014-2016 Paul Sokolovsky
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdio.h>
#include <string.h>
#include "py/runtime.h"
#include "py/stream.h"
#include "py/mperrno.h"
#if MICROPY_PY_UZLIB
#include "uzlib/tinf.h"
#if 0 // print debugging info
#define DEBUG_printf DEBUG_printf
#else // don't print debugging info
#define DEBUG_printf(...) (void)0
#endif
typedef struct _mp_obj_decompio_t {
mp_obj_base_t base;
mp_obj_t src_stream;
TINF_DATA decomp;
bool eof;
} mp_obj_decompio_t;
STATIC int read_src_stream(TINF_DATA *data) {
byte *p = (void*)data;
p -= offsetof(mp_obj_decompio_t, decomp);
mp_obj_decompio_t *self = (mp_obj_decompio_t*)p;
const mp_stream_p_t *stream = mp_get_stream(self->src_stream);
int err;
byte c;
mp_uint_t out_sz = stream->read(self->src_stream, &c, 1, &err);
if (out_sz == MP_STREAM_ERROR) {
mp_raise_OSError(err);
}
if (out_sz == 0) {
nlr_raise(mp_obj_new_exception(&mp_type_EOFError));
}
return c;
}
STATIC mp_obj_t decompio_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 1, 2, false);
mp_get_stream_raise(args[0], MP_STREAM_OP_READ);
mp_obj_decompio_t *o = m_new_obj(mp_obj_decompio_t);
o->base.type = type;
memset(&o->decomp, 0, sizeof(o->decomp));
o->decomp.readSource = read_src_stream;
o->src_stream = args[0];
o->eof = false;
mp_int_t dict_opt = 0;
int dict_sz;
if (n_args > 1) {
dict_opt = mp_obj_get_int(args[1]);
}
if (dict_opt >= 16) {
int st = uzlib_gzip_parse_header(&o->decomp);
if (st != TINF_OK) {
goto header_error;
}
dict_sz = 1 << (dict_opt - 16);
} else if (dict_opt >= 0) {
dict_opt = uzlib_zlib_parse_header(&o->decomp);
if (dict_opt < 0) {
header_error:
mp_raise_ValueError("compression header");
}
dict_sz = 1 << dict_opt;
} else {
dict_sz = 1 << -dict_opt;
}
uzlib_uncompress_init(&o->decomp, m_new(byte, dict_sz), dict_sz);
return MP_OBJ_FROM_PTR(o);
}
STATIC mp_uint_t decompio_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) {
mp_obj_decompio_t *o = MP_OBJ_TO_PTR(o_in);
if (o->eof) {
return 0;
}
o->decomp.dest = buf;
o->decomp.dest_limit = (byte*)buf + size;
int st = uzlib_uncompress_chksum(&o->decomp);
if (st == TINF_DONE) {
o->eof = true;
}
if (st < 0) {
*errcode = MP_EINVAL;
return MP_STREAM_ERROR;
}
return o->decomp.dest - (byte*)buf;
}
#if !MICROPY_ENABLE_DYNRUNTIME
STATIC const mp_rom_map_elem_t decompio_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
{ MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
};
STATIC MP_DEFINE_CONST_DICT(decompio_locals_dict, decompio_locals_dict_table);
#endif
STATIC const mp_stream_p_t decompio_stream_p = {
.read = decompio_read,
};
#if !MICROPY_ENABLE_DYNRUNTIME
STATIC const mp_obj_type_t decompio_type = {
{ &mp_type_type },
.name = MP_QSTR_DecompIO,
.make_new = decompio_make_new,
.protocol = &decompio_stream_p,
.locals_dict = (void*)&decompio_locals_dict,
};
#endif
STATIC mp_obj_t mod_uzlib_decompress(size_t n_args, const mp_obj_t *args) {
mp_obj_t data = args[0];
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ);
TINF_DATA *decomp = m_new_obj(TINF_DATA);
memset(decomp, 0, sizeof(*decomp));
DEBUG_printf("sizeof(TINF_DATA)=" UINT_FMT "\n", sizeof(*decomp));
uzlib_uncompress_init(decomp, NULL, 0);
mp_uint_t dest_buf_size = (bufinfo.len + 15) & ~15;
byte *dest_buf = m_new(byte, dest_buf_size);
decomp->dest = dest_buf;
decomp->dest_limit = dest_buf + dest_buf_size;
DEBUG_printf("uzlib: Initial out buffer: " UINT_FMT " bytes\n", dest_buf_size);
decomp->source = bufinfo.buf;
decomp->source_limit = (byte*)bufinfo.buf + bufinfo.len;
int st;
bool is_zlib = true;
if (n_args > 1 && MP_OBJ_SMALL_INT_VALUE(args[1]) < 0) {
is_zlib = false;
}
if (is_zlib) {
st = uzlib_zlib_parse_header(decomp);
if (st < 0) {
goto error;
}
}
while (1) {
st = uzlib_uncompress_chksum(decomp);
if (st < 0) {
goto error;
}
if (st == TINF_DONE) {
break;
}
size_t offset = decomp->dest - dest_buf;
dest_buf = m_renew(byte, dest_buf, dest_buf_size, dest_buf_size + 256);
dest_buf_size += 256;
decomp->dest = dest_buf + offset;
decomp->dest_limit = decomp->dest + 256;
}
mp_uint_t final_sz = decomp->dest - dest_buf;
DEBUG_printf("uzlib: Resizing from " UINT_FMT " to final size: " UINT_FMT " bytes\n", dest_buf_size, final_sz);
dest_buf = (byte*)m_renew(byte, dest_buf, dest_buf_size, final_sz);
mp_obj_t res = mp_obj_new_bytearray_by_ref(final_sz, dest_buf);
m_del_obj(TINF_DATA, decomp);
return res;
error:
nlr_raise(mp_obj_new_exception_arg1(&mp_type_ValueError, MP_OBJ_NEW_SMALL_INT(st)));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_uzlib_decompress_obj, 1, 3, mod_uzlib_decompress);
#if !MICROPY_ENABLE_DYNRUNTIME
STATIC const mp_rom_map_elem_t mp_module_uzlib_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uzlib) },
{ MP_ROM_QSTR(MP_QSTR_decompress), MP_ROM_PTR(&mod_uzlib_decompress_obj) },
{ MP_ROM_QSTR(MP_QSTR_DecompIO), MP_ROM_PTR(&decompio_type) },
};
STATIC MP_DEFINE_CONST_DICT(mp_module_uzlib_globals, mp_module_uzlib_globals_table);
const mp_obj_module_t mp_module_uzlib = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_uzlib_globals,
};
#endif
// Source files #include'd here to make sure they're compiled in
// only if module is enabled by config setting.
#include "uzlib/tinflate.c"
#include "uzlib/tinfzlib.c"
#include "uzlib/tinfgzip.c"
#include "uzlib/adler32.c"
#include "uzlib/crc32.c"
#endif // MICROPY_PY_UZLIB
@@ -0,0 +1,367 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Paul Sokolovsky
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "py/runtime.h"
#include "py/stream.h"
#include "py/builtin.h"
#ifdef MICROPY_PY_WEBREPL_DELAY
#include "py/mphal.h"
#endif
#include "extmod/moduwebsocket.h"
#if MICROPY_PY_WEBREPL
#if 0 // print debugging info
#define DEBUG_printf DEBUG_printf
#else // don't print debugging info
#define DEBUG_printf(...) (void)0
#endif
struct webrepl_file {
char sig[2];
char type;
char flags;
uint64_t offset;
uint32_t size;
uint16_t fname_len;
char fname[64];
} __attribute__((packed));
enum { PUT_FILE = 1, GET_FILE, GET_VER };
enum { STATE_PASSWD, STATE_NORMAL };
typedef struct _mp_obj_webrepl_t {
mp_obj_base_t base;
mp_obj_t sock;
byte state;
byte hdr_to_recv;
uint32_t data_to_recv;
struct webrepl_file hdr;
mp_obj_t cur_file;
} mp_obj_webrepl_t;
STATIC const char passwd_prompt[] = "Password: ";
STATIC const char connected_prompt[] = "\r\nWebREPL connected\r\n>>> ";
STATIC const char denied_prompt[] = "\r\nAccess denied\r\n";
STATIC char webrepl_passwd[10];
STATIC void write_webrepl(mp_obj_t websock, const void *buf, size_t len) {
const mp_stream_p_t *sock_stream = mp_get_stream(websock);
int err;
int old_opts = sock_stream->ioctl(websock, MP_STREAM_SET_DATA_OPTS, FRAME_BIN, &err);
sock_stream->write(websock, buf, len, &err);
sock_stream->ioctl(websock, MP_STREAM_SET_DATA_OPTS, old_opts, &err);
}
#define SSTR(s) s, sizeof(s) - 1
STATIC void write_webrepl_str(mp_obj_t websock, const char *str, int sz) {
int err;
const mp_stream_p_t *sock_stream = mp_get_stream(websock);
sock_stream->write(websock, str, sz, &err);
}
STATIC void write_webrepl_resp(mp_obj_t websock, uint16_t code) {
char buf[4] = {'W', 'B', code & 0xff, code >> 8};
write_webrepl(websock, buf, sizeof(buf));
}
STATIC mp_obj_t webrepl_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 1, 2, false);
mp_get_stream_raise(args[0], MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL);
DEBUG_printf("sizeof(struct webrepl_file) = %lu\n", sizeof(struct webrepl_file));
mp_obj_webrepl_t *o = m_new_obj(mp_obj_webrepl_t);
o->base.type = type;
o->sock = args[0];
o->hdr_to_recv = sizeof(struct webrepl_file);
o->data_to_recv = 0;
o->state = STATE_PASSWD;
write_webrepl_str(args[0], SSTR(passwd_prompt));
return o;
}
STATIC void check_file_op_finished(mp_obj_webrepl_t *self) {
if (self->data_to_recv == 0) {
mp_stream_close(self->cur_file);
self->hdr_to_recv = sizeof(struct webrepl_file);
DEBUG_printf("webrepl: Finished file operation %d\n", self->hdr.type);
write_webrepl_resp(self->sock, 0);
}
}
STATIC int write_file_chunk(mp_obj_webrepl_t *self) {
const mp_stream_p_t *file_stream = mp_get_stream(self->cur_file);
byte readbuf[2 + 256];
int err;
mp_uint_t out_sz = file_stream->read(self->cur_file, readbuf + 2, sizeof(readbuf) - 2, &err);
if (out_sz == MP_STREAM_ERROR) {
return out_sz;
}
readbuf[0] = out_sz;
readbuf[1] = out_sz >> 8;
DEBUG_printf("webrepl: Sending %d bytes of file\n", out_sz);
write_webrepl(self->sock, readbuf, 2 + out_sz);
return out_sz;
}
STATIC void handle_op(mp_obj_webrepl_t *self) {
// Handle operations not requiring opened file
switch (self->hdr.type) {
case GET_VER: {
static const char ver[] = {MICROPY_VERSION_MAJOR, MICROPY_VERSION_MINOR, MICROPY_VERSION_MICRO};
write_webrepl(self->sock, ver, sizeof(ver));
self->hdr_to_recv = sizeof(struct webrepl_file);
return;
}
}
// Handle operations requiring opened file
mp_obj_t open_args[2] = {
mp_obj_new_str(self->hdr.fname, strlen(self->hdr.fname)),
MP_OBJ_NEW_QSTR(MP_QSTR_rb)
};
if (self->hdr.type == PUT_FILE) {
open_args[1] = MP_OBJ_NEW_QSTR(MP_QSTR_wb);
}
self->cur_file = mp_builtin_open(2, open_args, (mp_map_t*)&mp_const_empty_map);
#if 0
struct mp_stream_seek_t seek = { .offset = self->hdr.offset, .whence = 0 };
int err;
mp_uint_t res = file_stream->ioctl(self->cur_file, MP_STREAM_SEEK, (uintptr_t)&seek, &err);
assert(res != MP_STREAM_ERROR);
#endif
write_webrepl_resp(self->sock, 0);
if (self->hdr.type == PUT_FILE) {
self->data_to_recv = self->hdr.size;
check_file_op_finished(self);
} else if (self->hdr.type == GET_FILE) {
self->data_to_recv = 1;
}
}
STATIC mp_uint_t _webrepl_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode);
STATIC mp_uint_t webrepl_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) {
mp_uint_t out_sz;
do {
out_sz = _webrepl_read(self_in, buf, size, errcode);
} while (out_sz == -2);
return out_sz;
}
STATIC mp_uint_t _webrepl_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) {
// We know that os.dupterm always calls with size = 1
assert(size == 1);
mp_obj_webrepl_t *self = self_in;
const mp_stream_p_t *sock_stream = mp_get_stream(self->sock);
mp_uint_t out_sz = sock_stream->read(self->sock, buf, size, errcode);
//DEBUG_printf("webrepl: Read %d initial bytes from websocket\n", out_sz);
if (out_sz == 0 || out_sz == MP_STREAM_ERROR) {
return out_sz;
}
if (self->state == STATE_PASSWD) {
char c = *(char*)buf;
if (c == '\r' || c == '\n') {
self->hdr.fname[self->data_to_recv] = 0;
DEBUG_printf("webrepl: entered password: %s\n", self->hdr.fname);
if (strcmp(self->hdr.fname, webrepl_passwd) != 0) {
write_webrepl_str(self->sock, SSTR(denied_prompt));
return 0;
}
self->state = STATE_NORMAL;
self->data_to_recv = 0;
write_webrepl_str(self->sock, SSTR(connected_prompt));
} else if (self->data_to_recv < 10) {
self->hdr.fname[self->data_to_recv++] = c;
}
return -2;
}
// If last read data belonged to text record (== REPL)
int err;
if (sock_stream->ioctl(self->sock, MP_STREAM_GET_DATA_OPTS, 0, &err) == 1) {
return out_sz;
}
DEBUG_printf("webrepl: received bin data, hdr_to_recv: %d, data_to_recv=%d\n", self->hdr_to_recv, self->data_to_recv);
if (self->hdr_to_recv != 0) {
char *p = (char*)&self->hdr + sizeof(self->hdr) - self->hdr_to_recv;
*p++ = *(char*)buf;
if (--self->hdr_to_recv != 0) {
mp_uint_t hdr_sz = sock_stream->read(self->sock, p, self->hdr_to_recv, errcode);
if (hdr_sz == MP_STREAM_ERROR) {
return hdr_sz;
}
self->hdr_to_recv -= hdr_sz;
if (self->hdr_to_recv != 0) {
return -2;
}
}
DEBUG_printf("webrepl: op: %d, file: %s, chunk @%x, sz=%d\n", self->hdr.type, self->hdr.fname, (uint32_t)self->hdr.offset, self->hdr.size);
handle_op(self);
return -2;
}
if (self->data_to_recv != 0) {
// Ports that don't have much available stack can make this filebuf static
#if MICROPY_PY_WEBREPL_STATIC_FILEBUF
static
#endif
byte filebuf[512];
filebuf[0] = *(byte*)buf;
mp_uint_t buf_sz = 1;
if (--self->data_to_recv != 0) {
size_t to_read = MIN(sizeof(filebuf) - 1, self->data_to_recv);
mp_uint_t sz = sock_stream->read(self->sock, filebuf + 1, to_read, errcode);
if (sz == MP_STREAM_ERROR) {
return sz;
}
self->data_to_recv -= sz;
buf_sz += sz;
}
if (self->hdr.type == PUT_FILE) {
DEBUG_printf("webrepl: Writing %lu bytes to file\n", buf_sz);
int err;
mp_uint_t res = mp_stream_write_exactly(self->cur_file, filebuf, buf_sz, &err);
if (err != 0 || res != buf_sz) {
assert(0);
}
} else if (self->hdr.type == GET_FILE) {
assert(buf_sz == 1);
assert(self->data_to_recv == 0);
assert(filebuf[0] == 0);
mp_uint_t out_sz = write_file_chunk(self);
if (out_sz != 0) {
self->data_to_recv = 1;
}
}
check_file_op_finished(self);
#ifdef MICROPY_PY_WEBREPL_DELAY
// Some platforms may have broken drivers and easily gets
// overloaded with modest traffic WebREPL file transfers
// generate. The basic workaround is a crude rate control
// done in such way.
mp_hal_delay_ms(MICROPY_PY_WEBREPL_DELAY);
#endif
}
return -2;
}
STATIC mp_uint_t webrepl_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) {
mp_obj_webrepl_t *self = self_in;
if (self->state == STATE_PASSWD) {
// Don't forward output until passwd is entered
return size;
}
const mp_stream_p_t *stream_p = mp_get_stream(self->sock);
return stream_p->write(self->sock, buf, size, errcode);
}
STATIC mp_uint_t webrepl_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) {
mp_obj_webrepl_t *self = MP_OBJ_TO_PTR(o_in);
(void)arg;
switch (request) {
case MP_STREAM_CLOSE:
// TODO: This is a place to do cleanup
mp_stream_close(self->sock);
return 0;
default:
*errcode = MP_EINVAL;
return MP_STREAM_ERROR;
}
}
STATIC mp_obj_t webrepl_set_password(mp_obj_t passwd_in) {
size_t len;
const char *passwd = mp_obj_str_get_data(passwd_in, &len);
if (len > sizeof(webrepl_passwd) - 1) {
mp_raise_ValueError(NULL);
}
strcpy(webrepl_passwd, passwd);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(webrepl_set_password_obj, webrepl_set_password);
STATIC const mp_rom_map_elem_t webrepl_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
{ MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) },
};
STATIC MP_DEFINE_CONST_DICT(webrepl_locals_dict, webrepl_locals_dict_table);
STATIC const mp_stream_p_t webrepl_stream_p = {
.read = webrepl_read,
.write = webrepl_write,
.ioctl = webrepl_ioctl,
};
STATIC const mp_obj_type_t webrepl_type = {
{ &mp_type_type },
.name = MP_QSTR__webrepl,
.make_new = webrepl_make_new,
.protocol = &webrepl_stream_p,
.locals_dict = (mp_obj_dict_t*)&webrepl_locals_dict,
};
STATIC const mp_rom_map_elem_t webrepl_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__webrepl) },
{ MP_ROM_QSTR(MP_QSTR__webrepl), MP_ROM_PTR(&webrepl_type) },
{ MP_ROM_QSTR(MP_QSTR_password), MP_ROM_PTR(&webrepl_set_password_obj) },
};
STATIC MP_DEFINE_CONST_DICT(webrepl_module_globals, webrepl_module_globals_table);
const mp_obj_module_t mp_module_webrepl = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&webrepl_module_globals,
};
#endif // MICROPY_PY_WEBREPL
@@ -0,0 +1,314 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Paul Sokolovsky
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "py/runtime.h"
#include "py/stream.h"
#include "extmod/modwebsocket.h"
#if MICROPY_PY_WEBSOCKET
enum { FRAME_HEADER, FRAME_OPT, PAYLOAD, CONTROL };
enum { BLOCKING_WRITE = 0x80 };
typedef struct _mp_obj_websocket_t {
mp_obj_base_t base;
mp_obj_t sock;
uint32_t msg_sz;
byte mask[4];
byte state;
byte to_recv;
byte mask_pos;
byte buf_pos;
byte buf[6];
byte opts;
// Copy of last data frame flags
byte ws_flags;
// Copy of current frame flags
byte last_flags;
} mp_obj_websocket_t;
STATIC mp_uint_t websocket_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode);
STATIC mp_obj_t websocket_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 1, 2, false);
mp_get_stream_raise(args[0], MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL);
mp_obj_websocket_t *o = m_new_obj(mp_obj_websocket_t);
o->base.type = type;
o->sock = args[0];
o->state = FRAME_HEADER;
o->to_recv = 2;
o->mask_pos = 0;
o->buf_pos = 0;
o->opts = FRAME_TXT;
if (n_args > 1 && args[1] == mp_const_true) {
o->opts |= BLOCKING_WRITE;
}
return MP_OBJ_FROM_PTR(o);
}
STATIC mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) {
mp_obj_websocket_t *self = MP_OBJ_TO_PTR(self_in);
const mp_stream_p_t *stream_p = mp_get_stream(self->sock);
while (1) {
if (self->to_recv != 0) {
mp_uint_t out_sz = stream_p->read(self->sock, self->buf + self->buf_pos, self->to_recv, errcode);
if (out_sz == 0 || out_sz == MP_STREAM_ERROR) {
return out_sz;
}
self->buf_pos += out_sz;
self->to_recv -= out_sz;
if (self->to_recv != 0) {
*errcode = MP_EAGAIN;
return MP_STREAM_ERROR;
}
}
switch (self->state) {
case FRAME_HEADER: {
// TODO: Split frame handling below is untested so far, so conservatively disable it
assert(self->buf[0] & 0x80);
// "Control frames MAY be injected in the middle of a fragmented message."
// So, they must be processed before data frames (and not alter
// self->ws_flags)
byte frame_type = self->buf[0];
self->last_flags = frame_type;
frame_type &= FRAME_OPCODE_MASK;
if ((self->buf[0] & FRAME_OPCODE_MASK) == FRAME_CONT) {
// Preserve previous frame type
self->ws_flags = (self->ws_flags & FRAME_OPCODE_MASK) | (self->buf[0] & ~FRAME_OPCODE_MASK);
} else {
self->ws_flags = self->buf[0];
}
// Reset mask in case someone will use "simplified" protocol
// without masks.
memset(self->mask, 0, sizeof(self->mask));
int to_recv = 0;
size_t sz = self->buf[1] & 0x7f;
if (sz == 126) {
// Msg size is next 2 bytes
to_recv += 2;
} else if (sz == 127) {
// Msg size is next 8 bytes
assert(0);
}
if (self->buf[1] & 0x80) {
// Next 4 bytes is mask
to_recv += 4;
}
self->buf_pos = 0;
self->to_recv = to_recv;
self->msg_sz = sz; // May be overridden by FRAME_OPT
if (to_recv != 0) {
self->state = FRAME_OPT;
} else {
if (frame_type >= FRAME_CLOSE) {
self->state = CONTROL;
} else {
self->state = PAYLOAD;
}
}
continue;
}
case FRAME_OPT: {
if ((self->buf_pos & 3) == 2) {
// First two bytes are message length
self->msg_sz = (self->buf[0] << 8) | self->buf[1];
}
if (self->buf_pos >= 4) {
// Last 4 bytes is mask
memcpy(self->mask, self->buf + self->buf_pos - 4, 4);
}
self->buf_pos = 0;
if ((self->last_flags & FRAME_OPCODE_MASK) >= FRAME_CLOSE) {
self->state = CONTROL;
} else {
self->state = PAYLOAD;
}
continue;
}
case PAYLOAD:
case CONTROL: {
mp_uint_t out_sz = 0;
if (self->msg_sz == 0) {
// In case message had zero payload
goto no_payload;
}
size_t sz = MIN(size, self->msg_sz);
out_sz = stream_p->read(self->sock, buf, sz, errcode);
if (out_sz == 0 || out_sz == MP_STREAM_ERROR) {
return out_sz;
}
sz = out_sz;
for (byte *p = buf; sz--; p++) {
*p ^= self->mask[self->mask_pos++ & 3];
}
self->msg_sz -= out_sz;
if (self->msg_sz == 0) {
byte last_state;
no_payload:
last_state = self->state;
self->state = FRAME_HEADER;
self->to_recv = 2;
self->mask_pos = 0;
self->buf_pos = 0;
// Handle control frame
if (last_state == CONTROL) {
byte frame_type = self->last_flags & FRAME_OPCODE_MASK;
if (frame_type == FRAME_CLOSE) {
static char close_resp[2] = {0x88, 0};
int err;
websocket_write(self_in, close_resp, sizeof(close_resp), &err);
return 0;
}
//DEBUG_printf("Finished receiving ctrl message %x, ignoring\n", self->last_flags);
continue;
}
}
if (out_sz != 0) {
return out_sz;
}
// Empty (data) frame received is not EOF
continue;
}
}
}
}
STATIC mp_uint_t websocket_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) {
mp_obj_websocket_t *self = MP_OBJ_TO_PTR(self_in);
assert(size < 0x10000);
byte header[4] = {0x80 | (self->opts & FRAME_OPCODE_MASK)};
int hdr_sz;
if (size < 126) {
header[1] = size;
hdr_sz = 2;
} else {
header[1] = 126;
header[2] = size >> 8;
header[3] = size & 0xff;
hdr_sz = 4;
}
mp_obj_t dest[3];
if (self->opts & BLOCKING_WRITE) {
mp_load_method(self->sock, MP_QSTR_setblocking, dest);
dest[2] = mp_const_true;
mp_call_method_n_kw(1, 0, dest);
}
mp_uint_t out_sz = mp_stream_write_exactly(self->sock, header, hdr_sz, errcode);
if (*errcode == 0) {
out_sz = mp_stream_write_exactly(self->sock, buf, size, errcode);
}
if (self->opts & BLOCKING_WRITE) {
dest[2] = mp_const_false;
mp_call_method_n_kw(1, 0, dest);
}
if (*errcode != 0) {
return MP_STREAM_ERROR;
}
return out_sz;
}
STATIC mp_uint_t websocket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) {
mp_obj_websocket_t *self = MP_OBJ_TO_PTR(self_in);
switch (request) {
case MP_STREAM_CLOSE:
// TODO: Send close signaling to the other side, otherwise it's
// abrupt close (connection abort).
mp_stream_close(self->sock);
return 0;
case MP_STREAM_GET_DATA_OPTS:
return self->ws_flags & FRAME_OPCODE_MASK;
case MP_STREAM_SET_DATA_OPTS: {
int cur = self->opts & FRAME_OPCODE_MASK;
self->opts = (self->opts & ~FRAME_OPCODE_MASK) | (arg & FRAME_OPCODE_MASK);
return cur;
}
default:
*errcode = MP_EINVAL;
return MP_STREAM_ERROR;
}
}
STATIC const mp_rom_map_elem_t websocket_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
{ MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
{ MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&mp_stream_ioctl_obj) },
{ MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) },
};
STATIC MP_DEFINE_CONST_DICT(websocket_locals_dict, websocket_locals_dict_table);
STATIC const mp_stream_p_t websocket_stream_p = {
.read = websocket_read,
.write = websocket_write,
.ioctl = websocket_ioctl,
};
STATIC const mp_obj_type_t websocket_type = {
{ &mp_type_type },
.name = MP_QSTR_websocket,
.make_new = websocket_make_new,
.protocol = &websocket_stream_p,
.locals_dict = (void*)&websocket_locals_dict,
};
STATIC const mp_rom_map_elem_t websocket_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_websocket) },
{ MP_ROM_QSTR(MP_QSTR_websocket), MP_ROM_PTR(&websocket_type) },
};
STATIC MP_DEFINE_CONST_DICT(websocket_module_globals, websocket_module_globals_table);
const mp_obj_module_t mp_module_websocket = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&websocket_module_globals,
};
#endif // MICROPY_PY_WEBSOCKET
@@ -0,0 +1,10 @@
#ifndef MICROPY_INCLUDED_EXTMOD_MODWEBSOCKET_H
#define MICROPY_INCLUDED_EXTMOD_MODWEBSOCKET_H
#define FRAME_OPCODE_MASK 0x0f
enum {
FRAME_CONT, FRAME_TXT, FRAME_BIN,
FRAME_CLOSE = 0x8, FRAME_PING, FRAME_PONG
};
#endif // MICROPY_INCLUDED_EXTMOD_MODWEBSOCKET_H
@@ -0,0 +1,33 @@
#include "re1.5.h"
int _re1_5_classmatch(const char *pc, const char *sp)
{
// pc points to "cnt" byte after opcode
int is_positive = (pc[-1] == Class);
int cnt = *pc++;
while (cnt--) {
if (*sp >= *pc && *sp <= pc[1]) return is_positive;
pc += 2;
}
return !is_positive;
}
int _re1_5_namedclassmatch(const char *pc, const char *sp)
{
// pc points to name of class
int off = (*pc >> 5) & 1;
if ((*pc | 0x20) == 'd') {
if (!(*sp >= '0' && *sp <= '9')) {
off ^= 1;
}
} else if ((*pc | 0x20) == 's') {
if (!(*sp == ' ' || (*sp >= '\t' && *sp <= '\r'))) {
off ^= 1;
}
} else { // w
if (!((*sp >= 'A' && *sp <= 'Z') || (*sp >= 'a' && *sp <= 'z') || (*sp >= '0' && *sp <= '9') || *sp == '_')) {
off ^= 1;
}
}
return off;
}
@@ -0,0 +1,219 @@
// Copyright 2014 Paul Sokolovsky.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "re1.5.h"
#define INSERT_CODE(at, num, pc) \
((code ? memmove(code + at + num, code + at, pc - at) : 0), pc += num)
#define REL(at, to) (to - at - 2)
#define EMIT(at, byte) (code ? (code[at] = byte) : (at))
#define PC (prog->bytelen)
static const char *_compilecode(const char *re, ByteProg *prog, int sizecode)
{
char *code = sizecode ? NULL : prog->insts;
int start = PC;
int term = PC;
int alt_label = 0;
for (; *re && *re != ')'; re++) {
switch (*re) {
case '\\':
re++;
if (!*re) return NULL; // Trailing backslash
if ((*re | 0x20) == 'd' || (*re | 0x20) == 's' || (*re | 0x20) == 'w') {
term = PC;
EMIT(PC++, NamedClass);
EMIT(PC++, *re);
prog->len++;
break;
}
default:
term = PC;
EMIT(PC++, Char);
EMIT(PC++, *re);
prog->len++;
break;
case '.':
term = PC;
EMIT(PC++, Any);
prog->len++;
break;
case '[': {
int cnt;
term = PC;
re++;
if (*re == '^') {
EMIT(PC++, ClassNot);
re++;
} else {
EMIT(PC++, Class);
}
PC++; // Skip # of pair byte
prog->len++;
for (cnt = 0; *re != ']'; re++, cnt++) {
if (*re == '\\') {
++re;
}
if (!*re) return NULL;
EMIT(PC++, *re);
if (re[1] == '-' && re[2] != ']') {
re += 2;
}
EMIT(PC++, *re);
}
EMIT(term + 1, cnt);
break;
}
case '(': {
term = PC;
int sub = 0;
int capture = re[1] != '?' || re[2] != ':';
if (capture) {
sub = ++prog->sub;
EMIT(PC++, Save);
EMIT(PC++, 2 * sub);
prog->len++;
} else {
re += 2;
}
re = _compilecode(re + 1, prog, sizecode);
if (re == NULL || *re != ')') return NULL; // error, or no matching paren
if (capture) {
EMIT(PC++, Save);
EMIT(PC++, 2 * sub + 1);
prog->len++;
}
break;
}
case '?':
if (PC == term) return NULL; // nothing to repeat
INSERT_CODE(term, 2, PC);
if (re[1] == '?') {
EMIT(term, RSplit);
re++;
} else {
EMIT(term, Split);
}
EMIT(term + 1, REL(term, PC));
prog->len++;
term = PC;
break;
case '*':
if (PC == term) return NULL; // nothing to repeat
INSERT_CODE(term, 2, PC);
EMIT(PC, Jmp);
EMIT(PC + 1, REL(PC, term));
PC += 2;
if (re[1] == '?') {
EMIT(term, RSplit);
re++;
} else {
EMIT(term, Split);
}
EMIT(term + 1, REL(term, PC));
prog->len += 2;
term = PC;
break;
case '+':
if (PC == term) return NULL; // nothing to repeat
if (re[1] == '?') {
EMIT(PC, Split);
re++;
} else {
EMIT(PC, RSplit);
}
EMIT(PC + 1, REL(PC, term));
PC += 2;
prog->len++;
term = PC;
break;
case '|':
if (alt_label) {
EMIT(alt_label, REL(alt_label, PC) + 1);
}
INSERT_CODE(start, 2, PC);
EMIT(PC++, Jmp);
alt_label = PC++;
EMIT(start, Split);
EMIT(start + 1, REL(start, PC));
prog->len += 2;
term = PC;
break;
case '^':
EMIT(PC++, Bol);
prog->len++;
term = PC;
break;
case '$':
EMIT(PC++, Eol);
prog->len++;
term = PC;
break;
}
}
if (alt_label) {
EMIT(alt_label, REL(alt_label, PC) + 1);
}
return re;
}
int re1_5_sizecode(const char *re)
{
ByteProg dummyprog = {
// Save 0, Save 1, Match; more bytes for "search" (vs "match") prefix code
.bytelen = 5 + NON_ANCHORED_PREFIX
};
if (_compilecode(re, &dummyprog, /*sizecode*/1) == NULL) return -1;
return dummyprog.bytelen;
}
int re1_5_compilecode(ByteProg *prog, const char *re)
{
prog->len = 0;
prog->bytelen = 0;
prog->sub = 0;
// Add code to implement non-anchored operation ("search"),
// for anchored operation ("match"), this code will be just skipped.
// TODO: Implement search in much more efficient manner
prog->insts[prog->bytelen++] = RSplit;
prog->insts[prog->bytelen++] = 3;
prog->insts[prog->bytelen++] = Any;
prog->insts[prog->bytelen++] = Jmp;
prog->insts[prog->bytelen++] = -5;
prog->len += 3;
prog->insts[prog->bytelen++] = Save;
prog->insts[prog->bytelen++] = 0;
prog->len++;
re = _compilecode(re, prog, /*sizecode*/0);
if (re == NULL || *re) return 1;
prog->insts[prog->bytelen++] = Save;
prog->insts[prog->bytelen++] = 1;
prog->len++;
prog->insts[prog->bytelen++] = Match;
prog->len++;
return 0;
}
#if 0
int main(int argc, char *argv[])
{
int pc = 0;
ByteProg *code = re1_5_compilecode(argv[1]);
re1_5_dumpcode(code);
}
#endif
@@ -0,0 +1,65 @@
// Copyright 2014 Paul Sokolovsky.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "re1.5.h"
void re1_5_dumpcode(ByteProg *prog)
{
int pc = 0;
char *code = prog->insts;
while (pc < prog->bytelen) {
printf("%2d: ", pc);
switch(code[pc++]) {
default:
assert(0);
// re1_5_fatal("printprog");
case Split:
printf("split %d (%d)\n", pc + (signed char)code[pc] + 1, (signed char)code[pc]);
pc++;
break;
case RSplit:
printf("rsplit %d (%d)\n", pc + (signed char)code[pc] + 1, (signed char)code[pc]);
pc++;
break;
case Jmp:
printf("jmp %d (%d)\n", pc + (signed char)code[pc] + 1, (signed char)code[pc]);
pc++;
break;
case Char:
printf("char %c\n", code[pc++]);
break;
case Any:
printf("any\n");
break;
case Class:
case ClassNot: {
int num = code[pc];
printf("class%s %d", (code[pc - 1] == ClassNot ? "not" : ""), num);
pc++;
while (num--) {
printf(" 0x%02x-0x%02x", code[pc], code[pc + 1]);
pc += 2;
}
printf("\n");
break;
}
case NamedClass:
printf("namedclass %c\n", code[pc++]);
break;
case Match:
printf("match\n");
break;
case Save:
printf("save %d\n", (unsigned char)code[pc++]);
break;
case Bol:
printf("assert bol\n");
break;
case Eol:
printf("assert eol\n");
break;
}
}
printf("Bytes: %d, insts: %d\n", prog->bytelen, prog->len);
}
@@ -0,0 +1,154 @@
// Copyright 2007-2009 Russ Cox. All Rights Reserved.
// Copyright 2014 Paul Sokolovsky.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#ifndef _RE1_5_REGEXP__H
#define _RE1_5_REGEXP__H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <assert.h>
#define nil ((void*)0)
#define nelem(x) (sizeof(x)/sizeof((x)[0]))
typedef struct Regexp Regexp;
typedef struct Prog Prog;
typedef struct ByteProg ByteProg;
typedef struct Inst Inst;
typedef struct Subject Subject;
struct Regexp
{
int type;
int n;
int ch;
Regexp *left;
Regexp *right;
};
enum /* Regexp.type */
{
Alt = 1,
Cat,
Lit,
Dot,
Paren,
Quest,
Star,
Plus,
};
Regexp *parse(char*);
Regexp *reg(int type, Regexp *left, Regexp *right);
void printre(Regexp*);
#ifndef re1_5_fatal
void re1_5_fatal(char*);
#endif
#ifndef re1_5_stack_chk
#define re1_5_stack_chk()
#endif
void *mal(int);
struct Prog
{
Inst *start;
int len;
};
struct ByteProg
{
int bytelen;
int len;
int sub;
char insts[0];
};
struct Inst
{
int opcode;
int c;
int n;
Inst *x;
Inst *y;
int gen; // global state, oooh!
};
enum /* Inst.opcode */
{
// Instructions which consume input bytes (and thus fail if none left)
CONSUMERS = 1,
Char = CONSUMERS,
Any,
Class,
ClassNot,
NamedClass,
ASSERTS = 0x50,
Bol = ASSERTS,
Eol,
// Instructions which take relative offset as arg
JUMPS = 0x60,
Jmp = JUMPS,
Split,
RSplit,
// Other (special) instructions
Save = 0x7e,
Match = 0x7f,
};
#define inst_is_consumer(inst) ((inst) < ASSERTS)
#define inst_is_jump(inst) ((inst) & 0x70 == JUMPS)
Prog *compile(Regexp*);
void printprog(Prog*);
extern int gen;
enum {
MAXSUB = 20
};
typedef struct Sub Sub;
struct Sub
{
int ref;
int nsub;
const char *sub[MAXSUB];
};
Sub *newsub(int n);
Sub *incref(Sub*);
Sub *copy(Sub*);
Sub *update(Sub*, int, const char*);
void decref(Sub*);
struct Subject {
const char *begin;
const char *end;
};
#define NON_ANCHORED_PREFIX 5
#define HANDLE_ANCHORED(bytecode, is_anchored) ((is_anchored) ? (bytecode) + NON_ANCHORED_PREFIX : (bytecode))
int re1_5_backtrack(ByteProg*, Subject*, const char**, int, int);
int re1_5_pikevm(ByteProg*, Subject*, const char**, int, int);
int re1_5_recursiveloopprog(ByteProg*, Subject*, const char**, int, int);
int re1_5_recursiveprog(ByteProg*, Subject*, const char**, int, int);
int re1_5_thompsonvm(ByteProg*, Subject*, const char**, int, int);
int re1_5_sizecode(const char *re);
int re1_5_compilecode(ByteProg *prog, const char *re);
void re1_5_dumpcode(ByteProg *prog);
void cleanmarks(ByteProg *prog);
int _re1_5_classmatch(const char *pc, const char *sp);
int _re1_5_namedclassmatch(const char *pc, const char *sp);
#endif /*_RE1_5_REGEXP__H*/
@@ -0,0 +1,86 @@
// Copyright 2007-2009 Russ Cox. All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "re1.5.h"
static int
recursiveloop(char *pc, const char *sp, Subject *input, const char **subp, int nsubp)
{
const char *old;
int off;
re1_5_stack_chk();
for(;;) {
if(inst_is_consumer(*pc)) {
// If we need to match a character, but there's none left, it's fail
if(sp >= input->end)
return 0;
}
switch(*pc++) {
case Char:
if(*sp != *pc++)
return 0;
case Any:
sp++;
continue;
case Class:
case ClassNot:
if (!_re1_5_classmatch(pc, sp))
return 0;
pc += *(unsigned char*)pc * 2 + 1;
sp++;
continue;
case NamedClass:
if (!_re1_5_namedclassmatch(pc, sp))
return 0;
pc++;
sp++;
continue;
case Match:
return 1;
case Jmp:
off = (signed char)*pc++;
pc = pc + off;
continue;
case Split:
off = (signed char)*pc++;
if(recursiveloop(pc, sp, input, subp, nsubp))
return 1;
pc = pc + off;
continue;
case RSplit:
off = (signed char)*pc++;
if(recursiveloop(pc + off, sp, input, subp, nsubp))
return 1;
continue;
case Save:
off = (unsigned char)*pc++;
if(off >= nsubp) {
continue;
}
old = subp[off];
subp[off] = sp;
if(recursiveloop(pc, sp, input, subp, nsubp))
return 1;
subp[off] = old;
return 0;
case Bol:
if(sp != input->begin)
return 0;
continue;
case Eol:
if(sp != input->end)
return 0;
continue;
}
re1_5_fatal("recursiveloop");
}
}
int
re1_5_recursiveloopprog(ByteProg *prog, Subject *input, const char **subp, int nsubp, int is_anchored)
{
return recursiveloop(HANDLE_ANCHORED(prog->insts, is_anchored), input->begin, input, subp, nsubp);
}
@@ -0,0 +1,200 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Paul Sokolovsky
* Copyright (c) 2017-2019 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <string.h>
#include "py/mpconfig.h"
#include "py/runtime.h"
#include "py/objtuple.h"
#include "py/objarray.h"
#include "py/stream.h"
#include "extmod/misc.h"
#include "lib/utils/interrupt_char.h"
#if MICROPY_PY_OS_DUPTERM
void mp_uos_deactivate(size_t dupterm_idx, const char *msg, mp_obj_t exc) {
mp_obj_t term = MP_STATE_VM(dupterm_objs[dupterm_idx]);
MP_STATE_VM(dupterm_objs[dupterm_idx]) = MP_OBJ_NULL;
mp_printf(&mp_plat_print, msg);
if (exc != MP_OBJ_NULL) {
mp_obj_print_exception(&mp_plat_print, exc);
}
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
mp_stream_close(term);
nlr_pop();
} else {
// Ignore any errors during stream closing
}
}
uintptr_t mp_uos_dupterm_poll(uintptr_t poll_flags) {
uintptr_t poll_flags_out = 0;
for (size_t idx = 0; idx < MICROPY_PY_OS_DUPTERM; ++idx) {
mp_obj_t s = MP_STATE_VM(dupterm_objs[idx]);
if (s == MP_OBJ_NULL) {
continue;
}
int errcode = 0;
mp_uint_t ret = 0;
const mp_stream_p_t *stream_p = mp_get_stream(s);
#if MICROPY_PY_UOS_DUPTERM_BUILTIN_STREAM
if (mp_uos_dupterm_is_builtin_stream(s)) {
ret = stream_p->ioctl(s, MP_STREAM_POLL, poll_flags, &errcode);
} else
#endif
{
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
ret = stream_p->ioctl(s, MP_STREAM_POLL, poll_flags, &errcode);
nlr_pop();
} else {
// Ignore error with ioctl
}
}
if (ret != MP_STREAM_ERROR) {
poll_flags_out |= ret;
if (poll_flags_out == poll_flags) {
// Finish early if all requested flags are set
break;
}
}
}
return poll_flags_out;
}
int mp_uos_dupterm_rx_chr(void) {
for (size_t idx = 0; idx < MICROPY_PY_OS_DUPTERM; ++idx) {
if (MP_STATE_VM(dupterm_objs[idx]) == MP_OBJ_NULL) {
continue;
}
#if MICROPY_PY_UOS_DUPTERM_BUILTIN_STREAM
if (mp_uos_dupterm_is_builtin_stream(MP_STATE_VM(dupterm_objs[idx]))) {
byte buf[1];
int errcode = 0;
const mp_stream_p_t *stream_p = mp_get_stream(MP_STATE_VM(dupterm_objs[idx]));
mp_uint_t out_sz = stream_p->read(MP_STATE_VM(dupterm_objs[idx]), buf, 1, &errcode);
if (errcode == 0 && out_sz != 0) {
return buf[0];
} else {
continue;
}
}
#endif
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
byte buf[1];
int errcode;
const mp_stream_p_t *stream_p = mp_get_stream(MP_STATE_VM(dupterm_objs[idx]));
mp_uint_t out_sz = stream_p->read(MP_STATE_VM(dupterm_objs[idx]), buf, 1, &errcode);
if (out_sz == 0) {
nlr_pop();
mp_uos_deactivate(idx, "dupterm: EOF received, deactivating\n", MP_OBJ_NULL);
} else if (out_sz == MP_STREAM_ERROR) {
// errcode is valid
if (mp_is_nonblocking_error(errcode)) {
nlr_pop();
} else {
mp_raise_OSError(errcode);
}
} else {
// read 1 byte
nlr_pop();
if (buf[0] == mp_interrupt_char) {
// Signal keyboard interrupt to be raised as soon as the VM resumes
mp_keyboard_interrupt();
return -2;
}
return buf[0];
}
} else {
mp_uos_deactivate(idx, "dupterm: Exception in read() method, deactivating: ", MP_OBJ_FROM_PTR(nlr.ret_val));
}
}
// No chars available
return -1;
}
void mp_uos_dupterm_tx_strn(const char *str, size_t len) {
for (size_t idx = 0; idx < MICROPY_PY_OS_DUPTERM; ++idx) {
if (MP_STATE_VM(dupterm_objs[idx]) == MP_OBJ_NULL) {
continue;
}
#if MICROPY_PY_UOS_DUPTERM_BUILTIN_STREAM
if (mp_uos_dupterm_is_builtin_stream(MP_STATE_VM(dupterm_objs[idx]))) {
int errcode = 0;
const mp_stream_p_t *stream_p = mp_get_stream(MP_STATE_VM(dupterm_objs[idx]));
stream_p->write(MP_STATE_VM(dupterm_objs[idx]), str, len, &errcode);
continue;
}
#endif
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
mp_stream_write(MP_STATE_VM(dupterm_objs[idx]), str, len, MP_STREAM_RW_WRITE);
nlr_pop();
} else {
mp_uos_deactivate(idx, "dupterm: Exception in write() method, deactivating: ", MP_OBJ_FROM_PTR(nlr.ret_val));
}
}
}
STATIC mp_obj_t mp_uos_dupterm(size_t n_args, const mp_obj_t *args) {
mp_int_t idx = 0;
if (n_args == 2) {
idx = mp_obj_get_int(args[1]);
}
if (idx < 0 || idx >= MICROPY_PY_OS_DUPTERM) {
mp_raise_ValueError("invalid dupterm index");
}
mp_obj_t previous_obj = MP_STATE_VM(dupterm_objs[idx]);
if (previous_obj == MP_OBJ_NULL) {
previous_obj = mp_const_none;
}
if (args[0] == mp_const_none) {
MP_STATE_VM(dupterm_objs[idx]) = MP_OBJ_NULL;
} else {
mp_get_stream_raise(args[0], MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL);
MP_STATE_VM(dupterm_objs[idx]) = args[0];
}
return previous_obj;
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_uos_dupterm_obj, 1, 2, mp_uos_dupterm);
#endif
@@ -0,0 +1,102 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2013-2016 Damien P. George
* Copyright (c) 2016 Paul Sokolovsky
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/mpconfig.h"
#if MICROPY_PY_UTIME_MP_HAL
#include <string.h>
#include "py/obj.h"
#include "py/mphal.h"
#include "py/smallint.h"
#include "py/runtime.h"
#include "extmod/utime_mphal.h"
STATIC mp_obj_t time_sleep(mp_obj_t seconds_o) {
#if MICROPY_PY_BUILTINS_FLOAT
mp_hal_delay_ms((mp_uint_t)(1000 * mp_obj_get_float(seconds_o)));
#else
mp_hal_delay_ms(1000 * mp_obj_get_int(seconds_o));
#endif
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(mp_utime_sleep_obj, time_sleep);
STATIC mp_obj_t time_sleep_ms(mp_obj_t arg) {
mp_int_t ms = mp_obj_get_int(arg);
if (ms > 0) {
mp_hal_delay_ms(ms);
}
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(mp_utime_sleep_ms_obj, time_sleep_ms);
STATIC mp_obj_t time_sleep_us(mp_obj_t arg) {
mp_int_t us = mp_obj_get_int(arg);
if (us > 0) {
mp_hal_delay_us(us);
}
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(mp_utime_sleep_us_obj, time_sleep_us);
STATIC mp_obj_t time_ticks_ms(void) {
return MP_OBJ_NEW_SMALL_INT(mp_hal_ticks_ms() & (MICROPY_PY_UTIME_TICKS_PERIOD - 1));
}
MP_DEFINE_CONST_FUN_OBJ_0(mp_utime_ticks_ms_obj, time_ticks_ms);
STATIC mp_obj_t time_ticks_us(void) {
return MP_OBJ_NEW_SMALL_INT(mp_hal_ticks_us() & (MICROPY_PY_UTIME_TICKS_PERIOD - 1));
}
MP_DEFINE_CONST_FUN_OBJ_0(mp_utime_ticks_us_obj, time_ticks_us);
STATIC mp_obj_t time_ticks_cpu(void) {
return MP_OBJ_NEW_SMALL_INT(mp_hal_ticks_cpu() & (MICROPY_PY_UTIME_TICKS_PERIOD - 1));
}
MP_DEFINE_CONST_FUN_OBJ_0(mp_utime_ticks_cpu_obj, time_ticks_cpu);
STATIC mp_obj_t time_ticks_diff(mp_obj_t end_in, mp_obj_t start_in) {
// we assume that the arguments come from ticks_xx so are small ints
mp_uint_t start = MP_OBJ_SMALL_INT_VALUE(start_in);
mp_uint_t end = MP_OBJ_SMALL_INT_VALUE(end_in);
// Optimized formula avoiding if conditions. We adjust difference "forward",
// wrap it around and adjust back.
mp_int_t diff = ((end - start + MICROPY_PY_UTIME_TICKS_PERIOD / 2) & (MICROPY_PY_UTIME_TICKS_PERIOD - 1))
- MICROPY_PY_UTIME_TICKS_PERIOD / 2;
return MP_OBJ_NEW_SMALL_INT(diff);
}
MP_DEFINE_CONST_FUN_OBJ_2(mp_utime_ticks_diff_obj, time_ticks_diff);
STATIC mp_obj_t time_ticks_add(mp_obj_t ticks_in, mp_obj_t delta_in) {
// we assume that first argument come from ticks_xx so is small int
mp_uint_t ticks = MP_OBJ_SMALL_INT_VALUE(ticks_in);
mp_uint_t delta = mp_obj_get_int(delta_in);
return MP_OBJ_NEW_SMALL_INT((ticks + delta) & (MICROPY_PY_UTIME_TICKS_PERIOD - 1));
}
MP_DEFINE_CONST_FUN_OBJ_2(mp_utime_ticks_add_obj, time_ticks_add);
#endif // MICROPY_PY_UTIME_MP_HAL
@@ -0,0 +1,41 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2013-2016 Damien P. George
* Copyright (c) 2016 Paul Sokolovsky
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_EXTMOD_UTIME_MPHAL_H
#define MICROPY_INCLUDED_EXTMOD_UTIME_MPHAL_H
#include "py/obj.h"
MP_DECLARE_CONST_FUN_OBJ_1(mp_utime_sleep_obj);
MP_DECLARE_CONST_FUN_OBJ_1(mp_utime_sleep_ms_obj);
MP_DECLARE_CONST_FUN_OBJ_1(mp_utime_sleep_us_obj);
MP_DECLARE_CONST_FUN_OBJ_0(mp_utime_ticks_ms_obj);
MP_DECLARE_CONST_FUN_OBJ_0(mp_utime_ticks_us_obj);
MP_DECLARE_CONST_FUN_OBJ_0(mp_utime_ticks_cpu_obj);
MP_DECLARE_CONST_FUN_OBJ_2(mp_utime_ticks_diff_obj);
MP_DECLARE_CONST_FUN_OBJ_2(mp_utime_ticks_add_obj);
#endif // MICROPY_INCLUDED_EXTMOD_UTIME_MPHAL_H
@@ -0,0 +1,78 @@
/*
* Adler-32 checksum
*
* Copyright (c) 2003 by Joergen Ibsen / Jibz
* All Rights Reserved
*
* http://www.ibsensoftware.com/
*
* This software is provided 'as-is', without any express
* or implied warranty. In no event will the authors be
* held liable for any damages arising from the use of
* this software.
*
* Permission is granted to anyone to use this software
* for any purpose, including commercial applications,
* and to alter it and redistribute it freely, subject to
* the following restrictions:
*
* 1. The origin of this software must not be
* misrepresented; you must not claim that you
* wrote the original software. If you use this
* software in a product, an acknowledgment in
* the product documentation would be appreciated
* but is not required.
*
* 2. Altered source versions must be plainly marked
* as such, and must not be misrepresented as
* being the original software.
*
* 3. This notice may not be removed or altered from
* any source distribution.
*/
/*
* Adler-32 algorithm taken from the zlib source, which is
* Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler
*/
#include "tinf.h"
#define A32_BASE 65521
#define A32_NMAX 5552
uint32_t uzlib_adler32(const void *data, unsigned int length, uint32_t prev_sum /* 1 */)
{
const unsigned char *buf = (const unsigned char *)data;
unsigned int s1 = prev_sum & 0xffff;
unsigned int s2 = prev_sum >> 16;
while (length > 0)
{
int k = length < A32_NMAX ? length : A32_NMAX;
int i;
for (i = k / 16; i; --i, buf += 16)
{
s1 += buf[0]; s2 += s1; s1 += buf[1]; s2 += s1;
s1 += buf[2]; s2 += s1; s1 += buf[3]; s2 += s1;
s1 += buf[4]; s2 += s1; s1 += buf[5]; s2 += s1;
s1 += buf[6]; s2 += s1; s1 += buf[7]; s2 += s1;
s1 += buf[8]; s2 += s1; s1 += buf[9]; s2 += s1;
s1 += buf[10]; s2 += s1; s1 += buf[11]; s2 += s1;
s1 += buf[12]; s2 += s1; s1 += buf[13]; s2 += s1;
s1 += buf[14]; s2 += s1; s1 += buf[15]; s2 += s1;
}
for (i = k % 16; i; --i) { s1 += *buf++; s2 += s1; }
s1 %= A32_BASE;
s2 %= A32_BASE;
length -= k;
}
return (s2 << 16) | s1;
}
@@ -0,0 +1,63 @@
/*
* CRC32 checksum
*
* Copyright (c) 1998-2003 by Joergen Ibsen / Jibz
* All Rights Reserved
*
* http://www.ibsensoftware.com/
*
* This software is provided 'as-is', without any express
* or implied warranty. In no event will the authors be
* held liable for any damages arising from the use of
* this software.
*
* Permission is granted to anyone to use this software
* for any purpose, including commercial applications,
* and to alter it and redistribute it freely, subject to
* the following restrictions:
*
* 1. The origin of this software must not be
* misrepresented; you must not claim that you
* wrote the original software. If you use this
* software in a product, an acknowledgment in
* the product documentation would be appreciated
* but is not required.
*
* 2. Altered source versions must be plainly marked
* as such, and must not be misrepresented as
* being the original software.
*
* 3. This notice may not be removed or altered from
* any source distribution.
*/
/*
* CRC32 algorithm taken from the zlib source, which is
* Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler
*/
#include "tinf.h"
static const unsigned int tinf_crc32tab[16] = {
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190,
0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344,
0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278,
0xbdbdf21c
};
/* crc is previous value for incremental computation, 0xffffffff initially */
uint32_t uzlib_crc32(const void *data, unsigned int length, uint32_t crc)
{
const unsigned char *buf = (const unsigned char *)data;
unsigned int i;
for (i = 0; i < length; ++i)
{
crc ^= buf[i];
crc = tinf_crc32tab[crc & 0x0f] ^ (crc >> 4);
crc = tinf_crc32tab[crc & 0x0f] ^ (crc >> 4);
}
// return value suitable for passing in next time, for final value invert it
return crc/* ^ 0xffffffff*/;
}
@@ -0,0 +1,45 @@
/*
* Copyright (c) uzlib authors
*
* This software is provided 'as-is', without any express
* or implied warranty. In no event will the authors be
* held liable for any damages arising from the use of
* this software.
*
* Permission is granted to anyone to use this software
* for any purpose, including commercial applications,
* and to alter it and redistribute it freely, subject to
* the following restrictions:
*
* 1. The origin of this software must not be
* misrepresented; you must not claim that you
* wrote the original software. If you use this
* software in a product, an acknowledgment in
* the product documentation would be appreciated
* but is not required.
*
* 2. Altered source versions must be plainly marked
* as such, and must not be misrepresented as
* being the original software.
*
* 3. This notice may not be removed or altered from
* any source distribution.
*/
/* This files contains type declaration and prototypes for defl_static.c.
They may be altered/distinct from the originals used in PuTTY source
code. */
struct Outbuf {
unsigned char *outbuf;
int outlen, outsize;
unsigned long outbits;
int noutbits;
int comp_disabled;
};
void outbits(struct Outbuf *out, unsigned long bits, int nbits);
void zlib_start_block(struct Outbuf *ctx);
void zlib_finish_block(struct Outbuf *ctx);
void zlib_literal(struct Outbuf *ectx, unsigned char c);
void zlib_match(struct Outbuf *ectx, int distance, int len);
@@ -0,0 +1,3 @@
/* Compatibility header for the original tinf lib/older versions of uzlib.
Note: may be removed in the future, please migrate to uzlib.h. */
#include "uzlib.h"
@@ -0,0 +1,9 @@
/* This header contains compatibility defines for the original tinf API
and uzlib 2.x and below API. These defines are deprecated and going
to be removed in the future, so applications should migrate to new
uzlib API. */
#define TINF_DATA struct uzlib_uncomp
#define destSize dest_size
#define destStart dest_start
#define readSource source_read_cb
@@ -0,0 +1,110 @@
/*
* uzlib - tiny deflate/inflate library (deflate, gzip, zlib)
*
* Copyright (c) 2003 by Joergen Ibsen / Jibz
* All Rights Reserved
*
* http://www.ibsensoftware.com/
*
* Copyright (c) 2014-2018 by Paul Sokolovsky
*
* This software is provided 'as-is', without any express
* or implied warranty. In no event will the authors be
* held liable for any damages arising from the use of
* this software.
*
* Permission is granted to anyone to use this software
* for any purpose, including commercial applications,
* and to alter it and redistribute it freely, subject to
* the following restrictions:
*
* 1. The origin of this software must not be
* misrepresented; you must not claim that you
* wrote the original software. If you use this
* software in a product, an acknowledgment in
* the product documentation would be appreciated
* but is not required.
*
* 2. Altered source versions must be plainly marked
* as such, and must not be misrepresented as
* being the original software.
*
* 3. This notice may not be removed or altered from
* any source distribution.
*/
#include "tinf.h"
#define FTEXT 1
#define FHCRC 2
#define FEXTRA 4
#define FNAME 8
#define FCOMMENT 16
void tinf_skip_bytes(TINF_DATA *d, int num);
uint16_t tinf_get_uint16(TINF_DATA *d);
void tinf_skip_bytes(TINF_DATA *d, int num)
{
while (num--) uzlib_get_byte(d);
}
uint16_t tinf_get_uint16(TINF_DATA *d)
{
unsigned int v = uzlib_get_byte(d);
v = (uzlib_get_byte(d) << 8) | v;
return v;
}
int uzlib_gzip_parse_header(TINF_DATA *d)
{
unsigned char flg;
/* -- check format -- */
/* check id bytes */
if (uzlib_get_byte(d) != 0x1f || uzlib_get_byte(d) != 0x8b) return TINF_DATA_ERROR;
/* check method is deflate */
if (uzlib_get_byte(d) != 8) return TINF_DATA_ERROR;
/* get flag byte */
flg = uzlib_get_byte(d);
/* check that reserved bits are zero */
if (flg & 0xe0) return TINF_DATA_ERROR;
/* -- find start of compressed data -- */
/* skip rest of base header of 10 bytes */
tinf_skip_bytes(d, 6);
/* skip extra data if present */
if (flg & FEXTRA)
{
unsigned int xlen = tinf_get_uint16(d);
tinf_skip_bytes(d, xlen);
}
/* skip file name if present */
if (flg & FNAME) { while (uzlib_get_byte(d)); }
/* skip file comment if present */
if (flg & FCOMMENT) { while (uzlib_get_byte(d)); }
/* check header crc if present */
if (flg & FHCRC)
{
/*unsigned int hcrc =*/ tinf_get_uint16(d);
// TODO: Check!
// if (hcrc != (tinf_crc32(src, start - src) & 0x0000ffff))
// return TINF_DATA_ERROR;
}
/* initialize for crc32 checksum */
d->checksum_type = TINF_CHKSUM_CRC;
d->checksum = ~0;
return TINF_OK;
}
@@ -0,0 +1,659 @@
/*
* uzlib - tiny deflate/inflate library (deflate, gzip, zlib)
*
* Copyright (c) 2003 by Joergen Ibsen / Jibz
* All Rights Reserved
* http://www.ibsensoftware.com/
*
* Copyright (c) 2014-2018 by Paul Sokolovsky
*
* This software is provided 'as-is', without any express
* or implied warranty. In no event will the authors be
* held liable for any damages arising from the use of
* this software.
*
* Permission is granted to anyone to use this software
* for any purpose, including commercial applications,
* and to alter it and redistribute it freely, subject to
* the following restrictions:
*
* 1. The origin of this software must not be
* misrepresented; you must not claim that you
* wrote the original software. If you use this
* software in a product, an acknowledgment in
* the product documentation would be appreciated
* but is not required.
*
* 2. Altered source versions must be plainly marked
* as such, and must not be misrepresented as
* being the original software.
*
* 3. This notice may not be removed or altered from
* any source distribution.
*/
#include <assert.h>
#include "tinf.h"
#define UZLIB_DUMP_ARRAY(heading, arr, size) \
{ \
printf("%s", heading); \
for (int i = 0; i < size; ++i) { \
printf(" %d", (arr)[i]); \
} \
printf("\n"); \
}
uint32_t tinf_get_le_uint32(TINF_DATA *d);
uint32_t tinf_get_be_uint32(TINF_DATA *d);
/* --------------------------------------------------- *
* -- uninitialized global data (static structures) -- *
* --------------------------------------------------- */
#ifdef RUNTIME_BITS_TABLES
/* extra bits and base tables for length codes */
unsigned char length_bits[30];
unsigned short length_base[30];
/* extra bits and base tables for distance codes */
unsigned char dist_bits[30];
unsigned short dist_base[30];
#else
const unsigned char length_bits[30] = {
0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 2, 2, 2, 2,
3, 3, 3, 3, 4, 4, 4, 4,
5, 5, 5, 5
};
const unsigned short length_base[30] = {
3, 4, 5, 6, 7, 8, 9, 10,
11, 13, 15, 17, 19, 23, 27, 31,
35, 43, 51, 59, 67, 83, 99, 115,
131, 163, 195, 227, 258
};
const unsigned char dist_bits[30] = {
0, 0, 0, 0, 1, 1, 2, 2,
3, 3, 4, 4, 5, 5, 6, 6,
7, 7, 8, 8, 9, 9, 10, 10,
11, 11, 12, 12, 13, 13
};
const unsigned short dist_base[30] = {
1, 2, 3, 4, 5, 7, 9, 13,
17, 25, 33, 49, 65, 97, 129, 193,
257, 385, 513, 769, 1025, 1537, 2049, 3073,
4097, 6145, 8193, 12289, 16385, 24577
};
#endif
/* special ordering of code length codes */
const unsigned char clcidx[] = {
16, 17, 18, 0, 8, 7, 9, 6,
10, 5, 11, 4, 12, 3, 13, 2,
14, 1, 15
};
/* ----------------------- *
* -- utility functions -- *
* ----------------------- */
#ifdef RUNTIME_BITS_TABLES
/* build extra bits and base tables */
static void tinf_build_bits_base(unsigned char *bits, unsigned short *base, int delta, int first)
{
int i, sum;
/* build bits table */
for (i = 0; i < delta; ++i) bits[i] = 0;
for (i = 0; i < 30 - delta; ++i) bits[i + delta] = i / delta;
/* build base table */
for (sum = first, i = 0; i < 30; ++i)
{
base[i] = sum;
sum += 1 << bits[i];
}
}
#endif
/* build the fixed huffman trees */
static void tinf_build_fixed_trees(TINF_TREE *lt, TINF_TREE *dt)
{
int i;
/* build fixed length tree */
for (i = 0; i < 7; ++i) lt->table[i] = 0;
lt->table[7] = 24;
lt->table[8] = 152;
lt->table[9] = 112;
for (i = 0; i < 24; ++i) lt->trans[i] = 256 + i;
for (i = 0; i < 144; ++i) lt->trans[24 + i] = i;
for (i = 0; i < 8; ++i) lt->trans[24 + 144 + i] = 280 + i;
for (i = 0; i < 112; ++i) lt->trans[24 + 144 + 8 + i] = 144 + i;
/* build fixed distance tree */
for (i = 0; i < 5; ++i) dt->table[i] = 0;
dt->table[5] = 32;
for (i = 0; i < 32; ++i) dt->trans[i] = i;
}
/* given an array of code lengths, build a tree */
static void tinf_build_tree(TINF_TREE *t, const unsigned char *lengths, unsigned int num)
{
unsigned short offs[16];
unsigned int i, sum;
/* clear code length count table */
for (i = 0; i < 16; ++i) t->table[i] = 0;
/* scan symbol lengths, and sum code length counts */
for (i = 0; i < num; ++i) t->table[lengths[i]]++;
#if UZLIB_CONF_DEBUG_LOG >= 2
UZLIB_DUMP_ARRAY("codelen counts:", t->table, TINF_ARRAY_SIZE(t->table));
#endif
/* In the lengths array, 0 means unused code. So, t->table[0] now contains
number of unused codes. But table's purpose is to contain # of codes of
particular length, and there're 0 codes of length 0. */
t->table[0] = 0;
/* compute offset table for distribution sort */
for (sum = 0, i = 0; i < 16; ++i)
{
offs[i] = sum;
sum += t->table[i];
}
#if UZLIB_CONF_DEBUG_LOG >= 2
UZLIB_DUMP_ARRAY("codelen offsets:", offs, TINF_ARRAY_SIZE(offs));
#endif
/* create code->symbol translation table (symbols sorted by code) */
for (i = 0; i < num; ++i)
{
if (lengths[i]) t->trans[offs[lengths[i]]++] = i;
}
}
/* ---------------------- *
* -- decode functions -- *
* ---------------------- */
unsigned char uzlib_get_byte(TINF_DATA *d)
{
/* If end of source buffer is not reached, return next byte from source
buffer. */
if (d->source < d->source_limit) {
return *d->source++;
}
/* Otherwise if there's callback and we haven't seen EOF yet, try to
read next byte using it. (Note: the callback can also update ->source
and ->source_limit). */
if (d->readSource && !d->eof) {
int val = d->readSource(d);
if (val >= 0) {
return (unsigned char)val;
}
}
/* Otherwise, we hit EOF (either from ->readSource() or from exhaustion
of the buffer), and it will be "sticky", i.e. further calls to this
function will end up here too. */
d->eof = true;
return 0;
}
uint32_t tinf_get_le_uint32(TINF_DATA *d)
{
uint32_t val = 0;
int i;
for (i = 4; i--;) {
val = val >> 8 | ((uint32_t)uzlib_get_byte(d)) << 24;
}
return val;
}
uint32_t tinf_get_be_uint32(TINF_DATA *d)
{
uint32_t val = 0;
int i;
for (i = 4; i--;) {
val = val << 8 | uzlib_get_byte(d);
}
return val;
}
/* get one bit from source stream */
static int tinf_getbit(TINF_DATA *d)
{
unsigned int bit;
/* check if tag is empty */
if (!d->bitcount--)
{
/* load next tag */
d->tag = uzlib_get_byte(d);
d->bitcount = 7;
}
/* shift bit out of tag */
bit = d->tag & 0x01;
d->tag >>= 1;
return bit;
}
/* read a num bit value from a stream and add base */
static unsigned int tinf_read_bits(TINF_DATA *d, int num, int base)
{
unsigned int val = 0;
/* read num bits */
if (num)
{
unsigned int limit = 1 << (num);
unsigned int mask;
for (mask = 1; mask < limit; mask *= 2)
if (tinf_getbit(d)) val += mask;
}
return val + base;
}
/* given a data stream and a tree, decode a symbol */
static int tinf_decode_symbol(TINF_DATA *d, TINF_TREE *t)
{
int sum = 0, cur = 0, len = 0;
/* get more bits while code value is above sum */
do {
cur = 2*cur + tinf_getbit(d);
if (++len == TINF_ARRAY_SIZE(t->table)) {
return TINF_DATA_ERROR;
}
sum += t->table[len];
cur -= t->table[len];
} while (cur >= 0);
sum += cur;
#if UZLIB_CONF_PARANOID_CHECKS
if (sum < 0 || sum >= TINF_ARRAY_SIZE(t->trans)) {
return TINF_DATA_ERROR;
}
#endif
return t->trans[sum];
}
/* given a data stream, decode dynamic trees from it */
static int tinf_decode_trees(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt)
{
/* code lengths for 288 literal/len symbols and 32 dist symbols */
unsigned char lengths[288+32];
unsigned int hlit, hdist, hclen, hlimit;
unsigned int i, num, length;
/* get 5 bits HLIT (257-286) */
hlit = tinf_read_bits(d, 5, 257);
/* get 5 bits HDIST (1-32) */
hdist = tinf_read_bits(d, 5, 1);
/* get 4 bits HCLEN (4-19) */
hclen = tinf_read_bits(d, 4, 4);
for (i = 0; i < 19; ++i) lengths[i] = 0;
/* read code lengths for code length alphabet */
for (i = 0; i < hclen; ++i)
{
/* get 3 bits code length (0-7) */
unsigned int clen = tinf_read_bits(d, 3, 0);
lengths[clcidx[i]] = clen;
}
/* build code length tree, temporarily use length tree */
tinf_build_tree(lt, lengths, 19);
/* decode code lengths for the dynamic trees */
hlimit = hlit + hdist;
for (num = 0; num < hlimit; )
{
int sym = tinf_decode_symbol(d, lt);
unsigned char fill_value = 0;
int lbits, lbase = 3;
/* error decoding */
if (sym < 0) return sym;
switch (sym)
{
case 16:
/* copy previous code length 3-6 times (read 2 bits) */
if (num == 0) return TINF_DATA_ERROR;
fill_value = lengths[num - 1];
lbits = 2;
break;
case 17:
/* repeat code length 0 for 3-10 times (read 3 bits) */
lbits = 3;
break;
case 18:
/* repeat code length 0 for 11-138 times (read 7 bits) */
lbits = 7;
lbase = 11;
break;
default:
/* values 0-15 represent the actual code lengths */
lengths[num++] = sym;
/* continue the for loop */
continue;
}
/* special code length 16-18 are handled here */
length = tinf_read_bits(d, lbits, lbase);
if (num + length > hlimit) return TINF_DATA_ERROR;
for (; length; --length)
{
lengths[num++] = fill_value;
}
}
#if UZLIB_CONF_DEBUG_LOG >= 2
printf("lit code lengths (%d):", hlit);
UZLIB_DUMP_ARRAY("", lengths, hlit);
printf("dist code lengths (%d):", hdist);
UZLIB_DUMP_ARRAY("", lengths + hlit, hdist);
#endif
#if UZLIB_CONF_PARANOID_CHECKS
/* Check that there's "end of block" symbol */
if (lengths[256] == 0) {
return TINF_DATA_ERROR;
}
#endif
/* build dynamic trees */
tinf_build_tree(lt, lengths, hlit);
tinf_build_tree(dt, lengths + hlit, hdist);
return TINF_OK;
}
/* ----------------------------- *
* -- block inflate functions -- *
* ----------------------------- */
/* given a stream and two trees, inflate next byte of output */
static int tinf_inflate_block_data(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt)
{
if (d->curlen == 0) {
unsigned int offs;
int dist;
int sym = tinf_decode_symbol(d, lt);
//printf("huff sym: %02x\n", sym);
if (d->eof) {
return TINF_DATA_ERROR;
}
/* literal byte */
if (sym < 256) {
TINF_PUT(d, sym);
return TINF_OK;
}
/* end of block */
if (sym == 256) {
return TINF_DONE;
}
/* substring from sliding dictionary */
sym -= 257;
if (sym >= 29) {
return TINF_DATA_ERROR;
}
/* possibly get more bits from length code */
d->curlen = tinf_read_bits(d, length_bits[sym], length_base[sym]);
dist = tinf_decode_symbol(d, dt);
if (dist >= 30) {
return TINF_DATA_ERROR;
}
/* possibly get more bits from distance code */
offs = tinf_read_bits(d, dist_bits[dist], dist_base[dist]);
/* calculate and validate actual LZ offset to use */
if (d->dict_ring) {
if (offs > d->dict_size) {
return TINF_DICT_ERROR;
}
/* Note: unlike full-dest-in-memory case below, we don't
try to catch offset which points to not yet filled
part of the dictionary here. Doing so would require
keeping another variable to track "filled in" size
of the dictionary. Appearance of such an offset cannot
lead to accessing memory outside of the dictionary
buffer, and clients which don't want to leak unrelated
information, should explicitly initialize dictionary
buffer passed to uzlib. */
d->lzOff = d->dict_idx - offs;
if (d->lzOff < 0) {
d->lzOff += d->dict_size;
}
} else {
/* catch trying to point before the start of dest buffer */
if (offs > d->dest - d->destStart) {
return TINF_DATA_ERROR;
}
d->lzOff = -offs;
}
}
/* copy next byte from dict substring */
if (d->dict_ring) {
TINF_PUT(d, d->dict_ring[d->lzOff]);
if ((unsigned)++d->lzOff == d->dict_size) {
d->lzOff = 0;
}
} else {
d->dest[0] = d->dest[d->lzOff];
d->dest++;
}
d->curlen--;
return TINF_OK;
}
/* inflate next byte from uncompressed block of data */
static int tinf_inflate_uncompressed_block(TINF_DATA *d)
{
if (d->curlen == 0) {
unsigned int length, invlength;
/* get length */
length = uzlib_get_byte(d);
length += 256 * uzlib_get_byte(d);
/* get one's complement of length */
invlength = uzlib_get_byte(d);
invlength += 256 * uzlib_get_byte(d);
/* check length */
if (length != (~invlength & 0x0000ffff)) return TINF_DATA_ERROR;
/* increment length to properly return TINF_DONE below, without
producing data at the same time */
d->curlen = length + 1;
/* make sure we start next block on a byte boundary */
d->bitcount = 0;
}
if (--d->curlen == 0) {
return TINF_DONE;
}
unsigned char c = uzlib_get_byte(d);
TINF_PUT(d, c);
return TINF_OK;
}
/* ---------------------- *
* -- public functions -- *
* ---------------------- */
/* initialize global (static) data */
void uzlib_init(void)
{
#ifdef RUNTIME_BITS_TABLES
/* build extra bits and base tables */
tinf_build_bits_base(length_bits, length_base, 4, 3);
tinf_build_bits_base(dist_bits, dist_base, 2, 1);
/* fix a special case */
length_bits[28] = 0;
length_base[28] = 258;
#endif
}
/* initialize decompression structure */
void uzlib_uncompress_init(TINF_DATA *d, void *dict, unsigned int dictLen)
{
d->eof = 0;
d->bitcount = 0;
d->bfinal = 0;
d->btype = -1;
d->dict_size = dictLen;
d->dict_ring = dict;
d->dict_idx = 0;
d->curlen = 0;
}
/* inflate next output bytes from compressed stream */
int uzlib_uncompress(TINF_DATA *d)
{
do {
int res;
/* start a new block */
if (d->btype == -1) {
next_blk:
/* read final block flag */
d->bfinal = tinf_getbit(d);
/* read block type (2 bits) */
d->btype = tinf_read_bits(d, 2, 0);
#if UZLIB_CONF_DEBUG_LOG >= 1
printf("Started new block: type=%d final=%d\n", d->btype, d->bfinal);
#endif
if (d->btype == 1) {
/* build fixed huffman trees */
tinf_build_fixed_trees(&d->ltree, &d->dtree);
} else if (d->btype == 2) {
/* decode trees from stream */
res = tinf_decode_trees(d, &d->ltree, &d->dtree);
if (res != TINF_OK) {
return res;
}
}
}
/* process current block */
switch (d->btype)
{
case 0:
/* decompress uncompressed block */
res = tinf_inflate_uncompressed_block(d);
break;
case 1:
case 2:
/* decompress block with fixed/dynamic huffman trees */
/* trees were decoded previously, so it's the same routine for both */
res = tinf_inflate_block_data(d, &d->ltree, &d->dtree);
break;
default:
return TINF_DATA_ERROR;
}
if (res == TINF_DONE && !d->bfinal) {
/* the block has ended (without producing more data), but we
can't return without data, so start procesing next block */
goto next_blk;
}
if (res != TINF_OK) {
return res;
}
} while (d->dest < d->dest_limit);
return TINF_OK;
}
/* inflate next output bytes from compressed stream, updating
checksum, and at the end of stream, verify it */
int uzlib_uncompress_chksum(TINF_DATA *d)
{
int res;
unsigned char *data = d->dest;
res = uzlib_uncompress(d);
if (res < 0) return res;
switch (d->checksum_type) {
case TINF_CHKSUM_ADLER:
d->checksum = uzlib_adler32(data, d->dest - data, d->checksum);
break;
case TINF_CHKSUM_CRC:
d->checksum = uzlib_crc32(data, d->dest - data, d->checksum);
break;
}
if (res == TINF_DONE) {
unsigned int val;
switch (d->checksum_type) {
case TINF_CHKSUM_ADLER:
val = tinf_get_be_uint32(d);
if (d->checksum != val) {
return TINF_CHKSUM_ERROR;
}
break;
case TINF_CHKSUM_CRC:
val = tinf_get_le_uint32(d);
if (~d->checksum != val) {
return TINF_CHKSUM_ERROR;
}
// Uncompressed size. TODO: Check
val = tinf_get_le_uint32(d);
break;
}
}
return res;
}
@@ -0,0 +1,66 @@
/*
* uzlib - tiny deflate/inflate library (deflate, gzip, zlib)
*
* Copyright (c) 2003 by Joergen Ibsen / Jibz
* All Rights Reserved
*
* http://www.ibsensoftware.com/
*
* Copyright (c) 2014-2018 by Paul Sokolovsky
*
* This software is provided 'as-is', without any express
* or implied warranty. In no event will the authors be
* held liable for any damages arising from the use of
* this software.
*
* Permission is granted to anyone to use this software
* for any purpose, including commercial applications,
* and to alter it and redistribute it freely, subject to
* the following restrictions:
*
* 1. The origin of this software must not be
* misrepresented; you must not claim that you
* wrote the original software. If you use this
* software in a product, an acknowledgment in
* the product documentation would be appreciated
* but is not required.
*
* 2. Altered source versions must be plainly marked
* as such, and must not be misrepresented as
* being the original software.
*
* 3. This notice may not be removed or altered from
* any source distribution.
*/
#include "tinf.h"
int uzlib_zlib_parse_header(TINF_DATA *d)
{
unsigned char cmf, flg;
/* -- get header bytes -- */
cmf = uzlib_get_byte(d);
flg = uzlib_get_byte(d);
/* -- check format -- */
/* check checksum */
if ((256*cmf + flg) % 31) return TINF_DATA_ERROR;
/* check method is deflate */
if ((cmf & 0x0f) != 8) return TINF_DATA_ERROR;
/* check window size is valid */
if ((cmf >> 4) > 7) return TINF_DATA_ERROR;
/* check there is no preset dictionary */
if (flg & 0x20) return TINF_DATA_ERROR;
/* initialize for adler32 checksum */
d->checksum_type = TINF_CHKSUM_ADLER;
d->checksum = 1;
return cmf >> 4;
}
@@ -0,0 +1,169 @@
/*
* uzlib - tiny deflate/inflate library (deflate, gzip, zlib)
*
* Copyright (c) 2003 by Joergen Ibsen / Jibz
* All Rights Reserved
* http://www.ibsensoftware.com/
*
* Copyright (c) 2014-2018 by Paul Sokolovsky
*
* This software is provided 'as-is', without any express
* or implied warranty. In no event will the authors be
* held liable for any damages arising from the use of
* this software.
*
* Permission is granted to anyone to use this software
* for any purpose, including commercial applications,
* and to alter it and redistribute it freely, subject to
* the following restrictions:
*
* 1. The origin of this software must not be
* misrepresented; you must not claim that you
* wrote the original software. If you use this
* software in a product, an acknowledgment in
* the product documentation would be appreciated
* but is not required.
*
* 2. Altered source versions must be plainly marked
* as such, and must not be misrepresented as
* being the original software.
*
* 3. This notice may not be removed or altered from
* any source distribution.
*/
#ifndef UZLIB_H_INCLUDED
#define UZLIB_H_INCLUDED
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "defl_static.h"
#include "uzlib_conf.h"
#if UZLIB_CONF_DEBUG_LOG
#include <stdio.h>
#endif
/* calling convention */
#ifndef TINFCC
#ifdef __WATCOMC__
#define TINFCC __cdecl
#else
#define TINFCC
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* ok status, more data produced */
#define TINF_OK 0
/* end of compressed stream reached */
#define TINF_DONE 1
#define TINF_DATA_ERROR (-3)
#define TINF_CHKSUM_ERROR (-4)
#define TINF_DICT_ERROR (-5)
/* checksum types */
#define TINF_CHKSUM_NONE 0
#define TINF_CHKSUM_ADLER 1
#define TINF_CHKSUM_CRC 2
/* helper macros */
#define TINF_ARRAY_SIZE(arr) (sizeof(arr) / sizeof(*(arr)))
/* data structures */
typedef struct {
unsigned short table[16]; /* table of code length counts */
unsigned short trans[288]; /* code -> symbol translation table */
} TINF_TREE;
struct uzlib_uncomp {
/* Pointer to the next byte in the input buffer */
const unsigned char *source;
/* Pointer to the next byte past the input buffer (source_limit = source + len) */
const unsigned char *source_limit;
/* If source_limit == NULL, or source >= source_limit, this function
will be used to read next byte from source stream. The function may
also return -1 in case of EOF (or irrecoverable error). Note that
besides returning the next byte, it may also update source and
source_limit fields, thus allowing for buffered operation. */
int (*source_read_cb)(struct uzlib_uncomp *uncomp);
unsigned int tag;
unsigned int bitcount;
/* Destination (output) buffer start */
unsigned char *dest_start;
/* Current pointer in dest buffer */
unsigned char *dest;
/* Pointer past the end of the dest buffer, similar to source_limit */
unsigned char *dest_limit;
/* Accumulating checksum */
unsigned int checksum;
char checksum_type;
bool eof;
int btype;
int bfinal;
unsigned int curlen;
int lzOff;
unsigned char *dict_ring;
unsigned int dict_size;
unsigned int dict_idx;
TINF_TREE ltree; /* dynamic length/symbol tree */
TINF_TREE dtree; /* dynamic distance tree */
};
#include "tinf_compat.h"
#define TINF_PUT(d, c) \
{ \
*d->dest++ = c; \
if (d->dict_ring) { d->dict_ring[d->dict_idx++] = c; if (d->dict_idx == d->dict_size) d->dict_idx = 0; } \
}
unsigned char TINFCC uzlib_get_byte(TINF_DATA *d);
/* Decompression API */
void TINFCC uzlib_init(void);
void TINFCC uzlib_uncompress_init(TINF_DATA *d, void *dict, unsigned int dictLen);
int TINFCC uzlib_uncompress(TINF_DATA *d);
int TINFCC uzlib_uncompress_chksum(TINF_DATA *d);
int TINFCC uzlib_zlib_parse_header(TINF_DATA *d);
int TINFCC uzlib_gzip_parse_header(TINF_DATA *d);
/* Compression API */
typedef const uint8_t *uzlib_hash_entry_t;
struct uzlib_comp {
struct Outbuf out;
uzlib_hash_entry_t *hash_table;
unsigned int hash_bits;
unsigned int dict_size;
};
void TINFCC uzlib_compress(struct uzlib_comp *c, const uint8_t *src, unsigned slen);
/* Checksum API */
/* prev_sum is previous value for incremental computation, 1 initially */
uint32_t TINFCC uzlib_adler32(const void *data, unsigned int length, uint32_t prev_sum);
/* crc is previous value for incremental computation, 0xffffffff initially */
uint32_t TINFCC uzlib_crc32(const void *data, unsigned int length, uint32_t crc);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* UZLIB_H_INCLUDED */
@@ -0,0 +1,22 @@
/*
* uzlib - tiny deflate/inflate library (deflate, gzip, zlib)
*
* Copyright (c) 2014-2018 by Paul Sokolovsky
*/
#ifndef UZLIB_CONF_H_INCLUDED
#define UZLIB_CONF_H_INCLUDED
#ifndef UZLIB_CONF_DEBUG_LOG
/* Debug logging level 0, 1, 2, etc. */
#define UZLIB_CONF_DEBUG_LOG 0
#endif
#ifndef UZLIB_CONF_PARANOID_CHECKS
/* Perform extra checks on the input stream, even if they aren't proven
to be strictly required (== lack of them wasn't proven to lead to
crashes). */
#define UZLIB_CONF_PARANOID_CHECKS 0
#endif
#endif /* UZLIB_CONF_H_INCLUDED */
@@ -0,0 +1,527 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2017 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdint.h>
#include <string.h>
#include "py/runtime.h"
#include "py/objstr.h"
#include "py/mperrno.h"
#include "extmod/vfs.h"
#if MICROPY_VFS
#if MICROPY_VFS_FAT
#include "extmod/vfs_fat.h"
#endif
#if MICROPY_VFS_LFS1 || MICROPY_VFS_LFS2
#include "extmod/vfs_lfs.h"
#endif
#if MICROPY_VFS_POSIX
#include "extmod/vfs_posix.h"
#endif
// For mp_vfs_proxy_call, the maximum number of additional args that can be passed.
// A fixed maximum size is used to avoid the need for a costly variable array.
#define PROXY_MAX_ARGS (2)
// path is the path to lookup and *path_out holds the path within the VFS
// object (starts with / if an absolute path).
// Returns MP_VFS_ROOT for root dir (and then path_out is undefined) and
// MP_VFS_NONE for path not found.
mp_vfs_mount_t *mp_vfs_lookup_path(const char *path, const char **path_out) {
if (*path == '/' || MP_STATE_VM(vfs_cur) == MP_VFS_ROOT) {
// an absolute path, or the current volume is root, so search root dir
bool is_abs = 0;
if (*path == '/') {
++path;
is_abs = 1;
}
if (*path == '\0') {
// path is "" or "/" so return virtual root
return MP_VFS_ROOT;
}
for (mp_vfs_mount_t *vfs = MP_STATE_VM(vfs_mount_table); vfs != NULL; vfs = vfs->next) {
size_t len = vfs->len - 1;
if (len == 0) {
*path_out = path - is_abs;
return vfs;
}
if (strncmp(path, vfs->str + 1, len) == 0) {
if (path[len] == '/') {
*path_out = path + len;
return vfs;
} else if (path[len] == '\0') {
*path_out = "/";
return vfs;
}
}
}
// if we get here then there's nothing mounted on /
if (is_abs) {
// path began with / and was not found
return MP_VFS_NONE;
}
}
// a relative path within a mounted device
*path_out = path;
return MP_STATE_VM(vfs_cur);
}
// Version of mp_vfs_lookup_path that takes and returns uPy string objects.
STATIC mp_vfs_mount_t *lookup_path(mp_obj_t path_in, mp_obj_t *path_out) {
const char *path = mp_obj_str_get_str(path_in);
const char *p_out;
mp_vfs_mount_t *vfs = mp_vfs_lookup_path(path, &p_out);
if (vfs != MP_VFS_NONE && vfs != MP_VFS_ROOT) {
*path_out = mp_obj_new_str_of_type(mp_obj_get_type(path_in),
(const byte*)p_out, strlen(p_out));
}
return vfs;
}
STATIC mp_obj_t mp_vfs_proxy_call(mp_vfs_mount_t *vfs, qstr meth_name, size_t n_args, const mp_obj_t *args) {
assert(n_args <= PROXY_MAX_ARGS);
if (vfs == MP_VFS_NONE) {
// mount point not found
mp_raise_OSError(MP_ENODEV);
}
if (vfs == MP_VFS_ROOT) {
// can't do operation on root dir
mp_raise_OSError(MP_EPERM);
}
mp_obj_t meth[2 + PROXY_MAX_ARGS];
mp_load_method(vfs->obj, meth_name, meth);
if (args != NULL) {
memcpy(meth + 2, args, n_args * sizeof(*args));
}
return mp_call_method_n_kw(n_args, 0, meth);
}
mp_import_stat_t mp_vfs_import_stat(const char *path) {
const char *path_out;
mp_vfs_mount_t *vfs = mp_vfs_lookup_path(path, &path_out);
if (vfs == MP_VFS_NONE || vfs == MP_VFS_ROOT) {
return MP_IMPORT_STAT_NO_EXIST;
}
// If the mounted object has the VFS protocol, call its import_stat helper
const mp_vfs_proto_t *proto = mp_obj_get_type(vfs->obj)->protocol;
if (proto != NULL) {
return proto->import_stat(MP_OBJ_TO_PTR(vfs->obj), path_out);
}
// delegate to vfs.stat() method
mp_obj_t path_o = mp_obj_new_str(path_out, strlen(path_out));
mp_obj_t stat;
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
stat = mp_vfs_proxy_call(vfs, MP_QSTR_stat, 1, &path_o);
nlr_pop();
} else {
// assume an exception means that the path is not found
return MP_IMPORT_STAT_NO_EXIST;
}
mp_obj_t *items;
mp_obj_get_array_fixed_n(stat, 10, &items);
mp_int_t st_mode = mp_obj_get_int(items[0]);
if (st_mode & MP_S_IFDIR) {
return MP_IMPORT_STAT_DIR;
} else {
return MP_IMPORT_STAT_FILE;
}
}
STATIC mp_obj_t mp_vfs_autodetect(mp_obj_t bdev_obj) {
#if MICROPY_VFS_LFS1 || MICROPY_VFS_LFS2
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
mp_obj_t vfs = MP_OBJ_NULL;
mp_vfs_blockdev_t blockdev;
mp_vfs_blockdev_init(&blockdev, bdev_obj);
uint8_t buf[44];
mp_vfs_blockdev_read_ext(&blockdev, 0, 8, sizeof(buf), buf);
#if MICROPY_VFS_LFS1
if (memcmp(&buf[32], "littlefs", 8) == 0) {
// LFS1
vfs = mp_type_vfs_lfs1.make_new(&mp_type_vfs_lfs1, 1, 0, &bdev_obj);
nlr_pop();
return vfs;
}
#endif
#if MICROPY_VFS_LFS2
if (memcmp(&buf[0], "littlefs", 8) == 0) {
// LFS2
vfs = mp_type_vfs_lfs2.make_new(&mp_type_vfs_lfs2, 1, 0, &bdev_obj);
nlr_pop();
return vfs;
}
#endif
nlr_pop();
} else {
// Ignore exception (eg block device doesn't support extended readblocks)
}
#endif
#if MICROPY_VFS_FAT
return mp_fat_vfs_type.make_new(&mp_fat_vfs_type, 1, 0, &bdev_obj);
#endif
return bdev_obj;
}
mp_obj_t mp_vfs_mount(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_readonly, ARG_mkfs };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_readonly, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_false_obj)} },
{ MP_QSTR_mkfs, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_false_obj)} },
};
// parse args
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args - 2, pos_args + 2, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
// get the mount point
size_t mnt_len;
const char *mnt_str = mp_obj_str_get_data(pos_args[1], &mnt_len);
// see if we need to auto-detect and create the filesystem
mp_obj_t vfs_obj = pos_args[0];
mp_obj_t dest[2];
mp_load_method_maybe(vfs_obj, MP_QSTR_mount, dest);
if (dest[0] == MP_OBJ_NULL) {
// Input object has no mount method, assume it's a block device and try to
// auto-detect the filesystem and create the corresponding VFS entity.
vfs_obj = mp_vfs_autodetect(vfs_obj);
}
// create new object
mp_vfs_mount_t *vfs = m_new_obj(mp_vfs_mount_t);
vfs->str = mnt_str;
vfs->len = mnt_len;
vfs->obj = vfs_obj;
vfs->next = NULL;
// call the underlying object to do any mounting operation
mp_vfs_proxy_call(vfs, MP_QSTR_mount, 2, (mp_obj_t*)&args);
// check that the destination mount point is unused
const char *path_out;
mp_vfs_mount_t *existing_mount = mp_vfs_lookup_path(mp_obj_str_get_str(pos_args[1]), &path_out);
if (existing_mount != MP_VFS_NONE && existing_mount != MP_VFS_ROOT) {
if (vfs->len != 1 && existing_mount->len == 1) {
// if root dir is mounted, still allow to mount something within a subdir of root
} else {
// mount point in use
mp_raise_OSError(MP_EPERM);
}
}
// insert the vfs into the mount table
mp_vfs_mount_t **vfsp = &MP_STATE_VM(vfs_mount_table);
while (*vfsp != NULL) {
if ((*vfsp)->len == 1) {
// make sure anything mounted at the root stays at the end of the list
vfs->next = *vfsp;
break;
}
vfsp = &(*vfsp)->next;
}
*vfsp = vfs;
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_KW(mp_vfs_mount_obj, 2, mp_vfs_mount);
mp_obj_t mp_vfs_umount(mp_obj_t mnt_in) {
// remove vfs from the mount table
mp_vfs_mount_t *vfs = NULL;
size_t mnt_len;
const char *mnt_str = NULL;
if (mp_obj_is_str(mnt_in)) {
mnt_str = mp_obj_str_get_data(mnt_in, &mnt_len);
}
for (mp_vfs_mount_t **vfsp = &MP_STATE_VM(vfs_mount_table); *vfsp != NULL; vfsp = &(*vfsp)->next) {
if ((mnt_str != NULL && !memcmp(mnt_str, (*vfsp)->str, mnt_len + 1)) || (*vfsp)->obj == mnt_in) {
vfs = *vfsp;
*vfsp = (*vfsp)->next;
break;
}
}
if (vfs == NULL) {
mp_raise_OSError(MP_EINVAL);
}
// if we unmounted the current device then set current to root
if (MP_STATE_VM(vfs_cur) == vfs) {
MP_STATE_VM(vfs_cur) = MP_VFS_ROOT;
}
// call the underlying object to do any unmounting operation
mp_vfs_proxy_call(vfs, MP_QSTR_umount, 0, NULL);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_umount_obj, mp_vfs_umount);
// Note: buffering and encoding args are currently ignored
mp_obj_t mp_vfs_open(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_file, ARG_mode, ARG_encoding };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_file, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_mode, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_QSTR(MP_QSTR_r)} },
{ MP_QSTR_buffering, MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_encoding, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
};
// parse args
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
#if MICROPY_VFS_POSIX
// If the file is an integer then delegate straight to the POSIX handler
if (mp_obj_is_small_int(args[ARG_file].u_obj)) {
return mp_vfs_posix_file_open(&mp_type_textio, args[ARG_file].u_obj, args[ARG_mode].u_obj);
}
#endif
mp_vfs_mount_t *vfs = lookup_path(args[ARG_file].u_obj, &args[ARG_file].u_obj);
return mp_vfs_proxy_call(vfs, MP_QSTR_open, 2, (mp_obj_t*)&args);
}
MP_DEFINE_CONST_FUN_OBJ_KW(mp_vfs_open_obj, 0, mp_vfs_open);
mp_obj_t mp_vfs_chdir(mp_obj_t path_in) {
mp_obj_t path_out;
mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out);
MP_STATE_VM(vfs_cur) = vfs;
if (vfs == MP_VFS_ROOT) {
// If we change to the root dir and a VFS is mounted at the root then
// we must change that VFS's current dir to the root dir so that any
// subsequent relative paths begin at the root of that VFS.
for (vfs = MP_STATE_VM(vfs_mount_table); vfs != NULL; vfs = vfs->next) {
if (vfs->len == 1) {
mp_obj_t root = MP_OBJ_NEW_QSTR(MP_QSTR__slash_);
mp_vfs_proxy_call(vfs, MP_QSTR_chdir, 1, &root);
break;
}
}
} else {
mp_vfs_proxy_call(vfs, MP_QSTR_chdir, 1, &path_out);
}
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_chdir_obj, mp_vfs_chdir);
mp_obj_t mp_vfs_getcwd(void) {
if (MP_STATE_VM(vfs_cur) == MP_VFS_ROOT) {
return MP_OBJ_NEW_QSTR(MP_QSTR__slash_);
}
mp_obj_t cwd_o = mp_vfs_proxy_call(MP_STATE_VM(vfs_cur), MP_QSTR_getcwd, 0, NULL);
if (MP_STATE_VM(vfs_cur)->len == 1) {
// don't prepend "/" for vfs mounted at root
return cwd_o;
}
const char *cwd = mp_obj_str_get_str(cwd_o);
vstr_t vstr;
vstr_init(&vstr, MP_STATE_VM(vfs_cur)->len + strlen(cwd) + 1);
vstr_add_strn(&vstr, MP_STATE_VM(vfs_cur)->str, MP_STATE_VM(vfs_cur)->len);
if (!(cwd[0] == '/' && cwd[1] == 0)) {
vstr_add_str(&vstr, cwd);
}
return mp_obj_new_str_from_vstr(&mp_type_str, &vstr);
}
MP_DEFINE_CONST_FUN_OBJ_0(mp_vfs_getcwd_obj, mp_vfs_getcwd);
typedef struct _mp_vfs_ilistdir_it_t {
mp_obj_base_t base;
mp_fun_1_t iternext;
union {
mp_vfs_mount_t *vfs;
mp_obj_t iter;
} cur;
bool is_str;
bool is_iter;
} mp_vfs_ilistdir_it_t;
STATIC mp_obj_t mp_vfs_ilistdir_it_iternext(mp_obj_t self_in) {
mp_vfs_ilistdir_it_t *self = MP_OBJ_TO_PTR(self_in);
if (self->is_iter) {
// continue delegating to root dir
return mp_iternext(self->cur.iter);
} else if (self->cur.vfs == NULL) {
// finished iterating mount points and no root dir is mounted
return MP_OBJ_STOP_ITERATION;
} else {
// continue iterating mount points
mp_vfs_mount_t *vfs = self->cur.vfs;
self->cur.vfs = vfs->next;
if (vfs->len == 1) {
// vfs is mounted at root dir, delegate to it
mp_obj_t root = MP_OBJ_NEW_QSTR(MP_QSTR__slash_);
self->is_iter = true;
self->cur.iter = mp_vfs_proxy_call(vfs, MP_QSTR_ilistdir, 1, &root);
return mp_iternext(self->cur.iter);
} else {
// a mounted directory
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL));
t->items[0] = mp_obj_new_str_of_type(
self->is_str ? &mp_type_str : &mp_type_bytes,
(const byte*)vfs->str + 1, vfs->len - 1);
t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR);
t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // no inode number
return MP_OBJ_FROM_PTR(t);
}
}
}
mp_obj_t mp_vfs_ilistdir(size_t n_args, const mp_obj_t *args) {
mp_obj_t path_in;
if (n_args == 1) {
path_in = args[0];
} else {
path_in = MP_OBJ_NEW_QSTR(MP_QSTR_);
}
mp_obj_t path_out;
mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out);
if (vfs == MP_VFS_ROOT) {
// list the root directory
mp_vfs_ilistdir_it_t *iter = m_new_obj(mp_vfs_ilistdir_it_t);
iter->base.type = &mp_type_polymorph_iter;
iter->iternext = mp_vfs_ilistdir_it_iternext;
iter->cur.vfs = MP_STATE_VM(vfs_mount_table);
iter->is_str = mp_obj_get_type(path_in) == &mp_type_str;
iter->is_iter = false;
return MP_OBJ_FROM_PTR(iter);
}
return mp_vfs_proxy_call(vfs, MP_QSTR_ilistdir, 1, &path_out);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_ilistdir_obj, 0, 1, mp_vfs_ilistdir);
mp_obj_t mp_vfs_listdir(size_t n_args, const mp_obj_t *args) {
mp_obj_t iter = mp_vfs_ilistdir(n_args, args);
mp_obj_t dir_list = mp_obj_new_list(0, NULL);
mp_obj_t next;
while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
mp_obj_list_append(dir_list, mp_obj_subscr(next, MP_OBJ_NEW_SMALL_INT(0), MP_OBJ_SENTINEL));
}
return dir_list;
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_listdir_obj, 0, 1, mp_vfs_listdir);
mp_obj_t mp_vfs_mkdir(mp_obj_t path_in) {
mp_obj_t path_out;
mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out);
if (vfs == MP_VFS_ROOT || (vfs != MP_VFS_NONE && !strcmp(mp_obj_str_get_str(path_out), "/"))) {
mp_raise_OSError(MP_EEXIST);
}
return mp_vfs_proxy_call(vfs, MP_QSTR_mkdir, 1, &path_out);
}
MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_mkdir_obj, mp_vfs_mkdir);
mp_obj_t mp_vfs_remove(mp_obj_t path_in) {
mp_obj_t path_out;
mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out);
return mp_vfs_proxy_call(vfs, MP_QSTR_remove, 1, &path_out);
}
MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_remove_obj, mp_vfs_remove);
mp_obj_t mp_vfs_rename(mp_obj_t old_path_in, mp_obj_t new_path_in) {
mp_obj_t args[2];
mp_vfs_mount_t *old_vfs = lookup_path(old_path_in, &args[0]);
mp_vfs_mount_t *new_vfs = lookup_path(new_path_in, &args[1]);
if (old_vfs != new_vfs) {
// can't rename across filesystems
mp_raise_OSError(MP_EPERM);
}
return mp_vfs_proxy_call(old_vfs, MP_QSTR_rename, 2, args);
}
MP_DEFINE_CONST_FUN_OBJ_2(mp_vfs_rename_obj, mp_vfs_rename);
mp_obj_t mp_vfs_rmdir(mp_obj_t path_in) {
mp_obj_t path_out;
mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out);
return mp_vfs_proxy_call(vfs, MP_QSTR_rmdir, 1, &path_out);
}
MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_rmdir_obj, mp_vfs_rmdir);
mp_obj_t mp_vfs_stat(mp_obj_t path_in) {
mp_obj_t path_out;
mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out);
if (vfs == MP_VFS_ROOT) {
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
t->items[0] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR); // st_mode
for (int i = 1; i <= 9; ++i) {
t->items[i] = MP_OBJ_NEW_SMALL_INT(0); // dev, nlink, uid, gid, size, atime, mtime, ctime
}
return MP_OBJ_FROM_PTR(t);
}
return mp_vfs_proxy_call(vfs, MP_QSTR_stat, 1, &path_out);
}
MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_stat_obj, mp_vfs_stat);
mp_obj_t mp_vfs_statvfs(mp_obj_t path_in) {
mp_obj_t path_out;
mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out);
if (vfs == MP_VFS_ROOT) {
// statvfs called on the root directory, see if there's anything mounted there
for (vfs = MP_STATE_VM(vfs_mount_table); vfs != NULL; vfs = vfs->next) {
if (vfs->len == 1) {
break;
}
}
// If there's nothing mounted at root then return a mostly-empty tuple
if (vfs == NULL) {
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
// fill in: bsize, frsize, blocks, bfree, bavail, files, ffree, favail, flags
for (int i = 0; i <= 8; ++i) {
t->items[i] = MP_OBJ_NEW_SMALL_INT(0);
}
// Put something sensible in f_namemax
t->items[9] = MP_OBJ_NEW_SMALL_INT(MICROPY_ALLOC_PATH_MAX);
return MP_OBJ_FROM_PTR(t);
}
// VFS mounted at root so delegate the call to it
path_out = MP_OBJ_NEW_QSTR(MP_QSTR__slash_);
}
return mp_vfs_proxy_call(vfs, MP_QSTR_statvfs, 1, &path_out);
}
MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_statvfs_obj, mp_vfs_statvfs);
#endif // MICROPY_VFS
@@ -0,0 +1,119 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2017 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_EXTMOD_VFS_H
#define MICROPY_INCLUDED_EXTMOD_VFS_H
#include "py/lexer.h"
#include "py/obj.h"
// return values of mp_vfs_lookup_path
// ROOT is 0 so that the default current directory is the root directory
#define MP_VFS_NONE ((mp_vfs_mount_t*)1)
#define MP_VFS_ROOT ((mp_vfs_mount_t*)0)
// MicroPython's port-standardized versions of stat constants
#define MP_S_IFDIR (0x4000)
#define MP_S_IFREG (0x8000)
// these are the values for mp_vfs_blockdev_t.flags
#define MP_BLOCKDEV_FLAG_NATIVE (0x0001) // readblocks[2]/writeblocks[2] contain native func
#define MP_BLOCKDEV_FLAG_FREE_OBJ (0x0002) // fs_user_mount_t obj should be freed on umount
#define MP_BLOCKDEV_FLAG_HAVE_IOCTL (0x0004) // new protocol with ioctl
#define MP_BLOCKDEV_FLAG_NO_FILESYSTEM (0x0008) // the block device has no filesystem on it
// constants for block protocol ioctl
#define MP_BLOCKDEV_IOCTL_INIT (1)
#define MP_BLOCKDEV_IOCTL_DEINIT (2)
#define MP_BLOCKDEV_IOCTL_SYNC (3)
#define MP_BLOCKDEV_IOCTL_BLOCK_COUNT (4)
#define MP_BLOCKDEV_IOCTL_BLOCK_SIZE (5)
#define MP_BLOCKDEV_IOCTL_BLOCK_ERASE (6)
// At the moment the VFS protocol just has import_stat, but could be extended to other methods
typedef struct _mp_vfs_proto_t {
mp_import_stat_t (*import_stat)(void *self, const char *path);
} mp_vfs_proto_t;
typedef struct _mp_vfs_blockdev_t {
uint16_t flags;
size_t block_size;
mp_obj_t readblocks[5];
mp_obj_t writeblocks[5];
// new protocol uses just ioctl, old uses sync (optional) and count
union {
mp_obj_t ioctl[4];
struct {
mp_obj_t sync[2];
mp_obj_t count[2];
} old;
} u;
} mp_vfs_blockdev_t;
typedef struct _mp_vfs_mount_t {
const char *str; // mount point with leading /
size_t len;
mp_obj_t obj;
struct _mp_vfs_mount_t *next;
} mp_vfs_mount_t;
void mp_vfs_blockdev_init(mp_vfs_blockdev_t *self, mp_obj_t bdev);
int mp_vfs_blockdev_read(mp_vfs_blockdev_t *self, size_t block_num, size_t num_blocks, uint8_t *buf);
int mp_vfs_blockdev_read_ext(mp_vfs_blockdev_t *self, size_t block_num, size_t block_off, size_t len, uint8_t *buf);
int mp_vfs_blockdev_write(mp_vfs_blockdev_t *self, size_t block_num, size_t num_blocks, const uint8_t *buf);
int mp_vfs_blockdev_write_ext(mp_vfs_blockdev_t *self, size_t block_num, size_t block_off, size_t len, const uint8_t *buf);
mp_obj_t mp_vfs_blockdev_ioctl(mp_vfs_blockdev_t *self, uintptr_t cmd, uintptr_t arg);
mp_vfs_mount_t *mp_vfs_lookup_path(const char *path, const char **path_out);
mp_import_stat_t mp_vfs_import_stat(const char *path);
mp_obj_t mp_vfs_mount(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
mp_obj_t mp_vfs_umount(mp_obj_t mnt_in);
mp_obj_t mp_vfs_open(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
mp_obj_t mp_vfs_chdir(mp_obj_t path_in);
mp_obj_t mp_vfs_getcwd(void);
mp_obj_t mp_vfs_ilistdir(size_t n_args, const mp_obj_t *args);
mp_obj_t mp_vfs_listdir(size_t n_args, const mp_obj_t *args);
mp_obj_t mp_vfs_mkdir(mp_obj_t path_in);
mp_obj_t mp_vfs_remove(mp_obj_t path_in);
mp_obj_t mp_vfs_rename(mp_obj_t old_path_in, mp_obj_t new_path_in);
mp_obj_t mp_vfs_rmdir(mp_obj_t path_in);
mp_obj_t mp_vfs_stat(mp_obj_t path_in);
mp_obj_t mp_vfs_statvfs(mp_obj_t path_in);
MP_DECLARE_CONST_FUN_OBJ_KW(mp_vfs_mount_obj);
MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_umount_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(mp_vfs_open_obj);
MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_chdir_obj);
MP_DECLARE_CONST_FUN_OBJ_0(mp_vfs_getcwd_obj);
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_ilistdir_obj);
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_listdir_obj);
MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_mkdir_obj);
MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_remove_obj);
MP_DECLARE_CONST_FUN_OBJ_2(mp_vfs_rename_obj);
MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_rmdir_obj);
MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_stat_obj);
MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_statvfs_obj);
#endif // MICROPY_INCLUDED_EXTMOD_VFS_H
@@ -0,0 +1,432 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2014 Damien P. George
* Copyright (c) 2016 Paul Sokolovsky
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/mpconfig.h"
#if MICROPY_VFS_FAT
#if !MICROPY_VFS
#error "with MICROPY_VFS_FAT enabled, must also enable MICROPY_VFS"
#endif
#include <string.h>
#include "py/runtime.h"
#include "py/mperrno.h"
#include "lib/oofatfs/ff.h"
#include "extmod/vfs_fat.h"
#include "lib/timeutils/timeutils.h"
#if FF_MAX_SS == FF_MIN_SS
#define SECSIZE(fs) (FF_MIN_SS)
#else
#define SECSIZE(fs) ((fs)->ssize)
#endif
#define mp_obj_fat_vfs_t fs_user_mount_t
STATIC mp_import_stat_t fat_vfs_import_stat(void *vfs_in, const char *path) {
fs_user_mount_t *vfs = vfs_in;
FILINFO fno;
assert(vfs != NULL);
FRESULT res = f_stat(&vfs->fatfs, path, &fno);
if (res == FR_OK) {
if ((fno.fattrib & AM_DIR) != 0) {
return MP_IMPORT_STAT_DIR;
} else {
return MP_IMPORT_STAT_FILE;
}
}
return MP_IMPORT_STAT_NO_EXIST;
}
STATIC mp_obj_t fat_vfs_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 1, 1, false);
// create new object
fs_user_mount_t *vfs = m_new_obj(fs_user_mount_t);
vfs->base.type = type;
vfs->fatfs.drv = vfs;
// Initialise underlying block device
vfs->blockdev.flags = MP_BLOCKDEV_FLAG_FREE_OBJ;
vfs->blockdev.block_size = FF_MIN_SS; // default, will be populated by call to MP_BLOCKDEV_IOCTL_BLOCK_SIZE
mp_vfs_blockdev_init(&vfs->blockdev, args[0]);
// mount the block device so the VFS methods can be used
FRESULT res = f_mount(&vfs->fatfs);
if (res == FR_NO_FILESYSTEM) {
// don't error out if no filesystem, to let mkfs()/mount() create one if wanted
vfs->blockdev.flags |= MP_BLOCKDEV_FLAG_NO_FILESYSTEM;
} else if (res != FR_OK) {
mp_raise_OSError(fresult_to_errno_table[res]);
}
return MP_OBJ_FROM_PTR(vfs);
}
#if _FS_REENTRANT
STATIC mp_obj_t fat_vfs_del(mp_obj_t self_in) {
mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(self_in);
// f_umount only needs to be called to release the sync object
f_umount(&self->fatfs);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_del_obj, fat_vfs_del);
#endif
STATIC mp_obj_t fat_vfs_mkfs(mp_obj_t bdev_in) {
// create new object
fs_user_mount_t *vfs = MP_OBJ_TO_PTR(fat_vfs_make_new(&mp_fat_vfs_type, 1, 0, &bdev_in));
// make the filesystem
uint8_t working_buf[FF_MAX_SS];
FRESULT res = f_mkfs(&vfs->fatfs, FM_FAT | FM_SFD, 0, working_buf, sizeof(working_buf));
if (res == FR_MKFS_ABORTED) { // Probably doesn't support FAT16
res = f_mkfs(&vfs->fatfs, FM_FAT32, 0, working_buf, sizeof(working_buf));
}
if (res != FR_OK) {
mp_raise_OSError(fresult_to_errno_table[res]);
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_mkfs_fun_obj, fat_vfs_mkfs);
STATIC MP_DEFINE_CONST_STATICMETHOD_OBJ(fat_vfs_mkfs_obj, MP_ROM_PTR(&fat_vfs_mkfs_fun_obj));
typedef struct _mp_vfs_fat_ilistdir_it_t {
mp_obj_base_t base;
mp_fun_1_t iternext;
bool is_str;
FF_DIR dir;
} mp_vfs_fat_ilistdir_it_t;
STATIC mp_obj_t mp_vfs_fat_ilistdir_it_iternext(mp_obj_t self_in) {
mp_vfs_fat_ilistdir_it_t *self = MP_OBJ_TO_PTR(self_in);
for (;;) {
FILINFO fno;
FRESULT res = f_readdir(&self->dir, &fno);
char *fn = fno.fname;
if (res != FR_OK || fn[0] == 0) {
// stop on error or end of dir
break;
}
// Note that FatFS already filters . and .., so we don't need to
// make 4-tuple with info about this entry
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(4, NULL));
if (self->is_str) {
t->items[0] = mp_obj_new_str(fn, strlen(fn));
} else {
t->items[0] = mp_obj_new_bytes((const byte*)fn, strlen(fn));
}
if (fno.fattrib & AM_DIR) {
// dir
t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR);
} else {
// file
t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFREG);
}
t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // no inode number
t->items[3] = mp_obj_new_int_from_uint(fno.fsize);
return MP_OBJ_FROM_PTR(t);
}
// ignore error because we may be closing a second time
f_closedir(&self->dir);
return MP_OBJ_STOP_ITERATION;
}
STATIC mp_obj_t fat_vfs_ilistdir_func(size_t n_args, const mp_obj_t *args) {
mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(args[0]);
bool is_str_type = true;
const char *path;
if (n_args == 2) {
if (mp_obj_get_type(args[1]) == &mp_type_bytes) {
is_str_type = false;
}
path = mp_obj_str_get_str(args[1]);
} else {
path = "";
}
// Create a new iterator object to list the dir
mp_vfs_fat_ilistdir_it_t *iter = m_new_obj(mp_vfs_fat_ilistdir_it_t);
iter->base.type = &mp_type_polymorph_iter;
iter->iternext = mp_vfs_fat_ilistdir_it_iternext;
iter->is_str = is_str_type;
FRESULT res = f_opendir(&self->fatfs, &iter->dir, path);
if (res != FR_OK) {
mp_raise_OSError(fresult_to_errno_table[res]);
}
return MP_OBJ_FROM_PTR(iter);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fat_vfs_ilistdir_obj, 1, 2, fat_vfs_ilistdir_func);
STATIC mp_obj_t fat_vfs_remove_internal(mp_obj_t vfs_in, mp_obj_t path_in, mp_int_t attr) {
mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in);
const char *path = mp_obj_str_get_str(path_in);
FILINFO fno;
FRESULT res = f_stat(&self->fatfs, path, &fno);
if (res != FR_OK) {
mp_raise_OSError(fresult_to_errno_table[res]);
}
// check if path is a file or directory
if ((fno.fattrib & AM_DIR) == attr) {
res = f_unlink(&self->fatfs, path);
if (res != FR_OK) {
mp_raise_OSError(fresult_to_errno_table[res]);
}
return mp_const_none;
} else {
mp_raise_OSError(attr ? MP_ENOTDIR : MP_EISDIR);
}
}
STATIC mp_obj_t fat_vfs_remove(mp_obj_t vfs_in, mp_obj_t path_in) {
return fat_vfs_remove_internal(vfs_in, path_in, 0); // 0 == file attribute
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_remove_obj, fat_vfs_remove);
STATIC mp_obj_t fat_vfs_rmdir(mp_obj_t vfs_in, mp_obj_t path_in) {
return fat_vfs_remove_internal(vfs_in, path_in, AM_DIR);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_rmdir_obj, fat_vfs_rmdir);
STATIC mp_obj_t fat_vfs_rename(mp_obj_t vfs_in, mp_obj_t path_in, mp_obj_t path_out) {
mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in);
const char *old_path = mp_obj_str_get_str(path_in);
const char *new_path = mp_obj_str_get_str(path_out);
FRESULT res = f_rename(&self->fatfs, old_path, new_path);
if (res == FR_EXIST) {
// if new_path exists then try removing it (but only if it's a file)
fat_vfs_remove_internal(vfs_in, path_out, 0); // 0 == file attribute
// try to rename again
res = f_rename(&self->fatfs, old_path, new_path);
}
if (res == FR_OK) {
return mp_const_none;
} else {
mp_raise_OSError(fresult_to_errno_table[res]);
}
}
STATIC MP_DEFINE_CONST_FUN_OBJ_3(fat_vfs_rename_obj, fat_vfs_rename);
STATIC mp_obj_t fat_vfs_mkdir(mp_obj_t vfs_in, mp_obj_t path_o) {
mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in);
const char *path = mp_obj_str_get_str(path_o);
FRESULT res = f_mkdir(&self->fatfs, path);
if (res == FR_OK) {
return mp_const_none;
} else {
mp_raise_OSError(fresult_to_errno_table[res]);
}
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_mkdir_obj, fat_vfs_mkdir);
/// Change current directory.
STATIC mp_obj_t fat_vfs_chdir(mp_obj_t vfs_in, mp_obj_t path_in) {
mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in);
const char *path;
path = mp_obj_str_get_str(path_in);
FRESULT res = f_chdir(&self->fatfs, path);
if (res != FR_OK) {
mp_raise_OSError(fresult_to_errno_table[res]);
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_chdir_obj, fat_vfs_chdir);
/// Get the current directory.
STATIC mp_obj_t fat_vfs_getcwd(mp_obj_t vfs_in) {
mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in);
char buf[MICROPY_ALLOC_PATH_MAX + 1];
FRESULT res = f_getcwd(&self->fatfs, buf, sizeof(buf));
if (res != FR_OK) {
mp_raise_OSError(fresult_to_errno_table[res]);
}
return mp_obj_new_str(buf, strlen(buf));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_getcwd_obj, fat_vfs_getcwd);
/// \function stat(path)
/// Get the status of a file or directory.
STATIC mp_obj_t fat_vfs_stat(mp_obj_t vfs_in, mp_obj_t path_in) {
mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in);
const char *path = mp_obj_str_get_str(path_in);
FILINFO fno;
if (path[0] == 0 || (path[0] == '/' && path[1] == 0)) {
// stat root directory
fno.fsize = 0;
fno.fdate = 0x2821; // Jan 1, 2000
fno.ftime = 0;
fno.fattrib = AM_DIR;
} else {
FRESULT res = f_stat(&self->fatfs, path, &fno);
if (res != FR_OK) {
mp_raise_OSError(fresult_to_errno_table[res]);
}
}
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
mp_int_t mode = 0;
if (fno.fattrib & AM_DIR) {
mode |= MP_S_IFDIR;
} else {
mode |= MP_S_IFREG;
}
mp_int_t seconds = timeutils_seconds_since_2000(
1980 + ((fno.fdate >> 9) & 0x7f),
(fno.fdate >> 5) & 0x0f,
fno.fdate & 0x1f,
(fno.ftime >> 11) & 0x1f,
(fno.ftime >> 5) & 0x3f,
2 * (fno.ftime & 0x1f)
);
t->items[0] = MP_OBJ_NEW_SMALL_INT(mode); // st_mode
t->items[1] = MP_OBJ_NEW_SMALL_INT(0); // st_ino
t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // st_dev
t->items[3] = MP_OBJ_NEW_SMALL_INT(0); // st_nlink
t->items[4] = MP_OBJ_NEW_SMALL_INT(0); // st_uid
t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // st_gid
t->items[6] = mp_obj_new_int_from_uint(fno.fsize); // st_size
t->items[7] = MP_OBJ_NEW_SMALL_INT(seconds); // st_atime
t->items[8] = MP_OBJ_NEW_SMALL_INT(seconds); // st_mtime
t->items[9] = MP_OBJ_NEW_SMALL_INT(seconds); // st_ctime
return MP_OBJ_FROM_PTR(t);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_stat_obj, fat_vfs_stat);
// Get the status of a VFS.
STATIC mp_obj_t fat_vfs_statvfs(mp_obj_t vfs_in, mp_obj_t path_in) {
mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in);
(void)path_in;
DWORD nclst;
FATFS *fatfs = &self->fatfs;
FRESULT res = f_getfree(fatfs, &nclst);
if (FR_OK != res) {
mp_raise_OSError(fresult_to_errno_table[res]);
}
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
t->items[0] = MP_OBJ_NEW_SMALL_INT(fatfs->csize * SECSIZE(fatfs)); // f_bsize
t->items[1] = t->items[0]; // f_frsize
t->items[2] = MP_OBJ_NEW_SMALL_INT((fatfs->n_fatent - 2)); // f_blocks
t->items[3] = MP_OBJ_NEW_SMALL_INT(nclst); // f_bfree
t->items[4] = t->items[3]; // f_bavail
t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // f_files
t->items[6] = MP_OBJ_NEW_SMALL_INT(0); // f_ffree
t->items[7] = MP_OBJ_NEW_SMALL_INT(0); // f_favail
t->items[8] = MP_OBJ_NEW_SMALL_INT(0); // f_flags
t->items[9] = MP_OBJ_NEW_SMALL_INT(FF_MAX_LFN); // f_namemax
return MP_OBJ_FROM_PTR(t);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_statvfs_obj, fat_vfs_statvfs);
STATIC mp_obj_t vfs_fat_mount(mp_obj_t self_in, mp_obj_t readonly, mp_obj_t mkfs) {
fs_user_mount_t *self = MP_OBJ_TO_PTR(self_in);
// Read-only device indicated by writeblocks[0] == MP_OBJ_NULL.
// User can specify read-only device by:
// 1. readonly=True keyword argument
// 2. nonexistent writeblocks method (then writeblocks[0] == MP_OBJ_NULL already)
if (mp_obj_is_true(readonly)) {
self->blockdev.writeblocks[0] = MP_OBJ_NULL;
}
// check if we need to make the filesystem
FRESULT res = (self->blockdev.flags & MP_BLOCKDEV_FLAG_NO_FILESYSTEM) ? FR_NO_FILESYSTEM : FR_OK;
if (res == FR_NO_FILESYSTEM && mp_obj_is_true(mkfs)) {
uint8_t working_buf[FF_MAX_SS];
res = f_mkfs(&self->fatfs, FM_FAT | FM_SFD, 0, working_buf, sizeof(working_buf));
}
if (res != FR_OK) {
mp_raise_OSError(fresult_to_errno_table[res]);
}
self->blockdev.flags &= ~MP_BLOCKDEV_FLAG_NO_FILESYSTEM;
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_3(vfs_fat_mount_obj, vfs_fat_mount);
STATIC mp_obj_t vfs_fat_umount(mp_obj_t self_in) {
(void)self_in;
// keep the FAT filesystem mounted internally so the VFS methods can still be used
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_umount_obj, vfs_fat_umount);
STATIC const mp_rom_map_elem_t fat_vfs_locals_dict_table[] = {
#if _FS_REENTRANT
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&fat_vfs_del_obj) },
#endif
{ MP_ROM_QSTR(MP_QSTR_mkfs), MP_ROM_PTR(&fat_vfs_mkfs_obj) },
{ MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&fat_vfs_open_obj) },
{ MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&fat_vfs_ilistdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&fat_vfs_mkdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&fat_vfs_rmdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&fat_vfs_chdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&fat_vfs_getcwd_obj) },
{ MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&fat_vfs_remove_obj) },
{ MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&fat_vfs_rename_obj) },
{ MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&fat_vfs_stat_obj) },
{ MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&fat_vfs_statvfs_obj) },
{ MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&vfs_fat_mount_obj) },
{ MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&fat_vfs_umount_obj) },
};
STATIC MP_DEFINE_CONST_DICT(fat_vfs_locals_dict, fat_vfs_locals_dict_table);
STATIC const mp_vfs_proto_t fat_vfs_proto = {
.import_stat = fat_vfs_import_stat,
};
const mp_obj_type_t mp_fat_vfs_type = {
{ &mp_type_type },
.name = MP_QSTR_VfsFat,
.make_new = fat_vfs_make_new,
.protocol = &fat_vfs_proto,
.locals_dict = (mp_obj_dict_t*)&fat_vfs_locals_dict,
};
#endif // MICROPY_VFS_FAT
@@ -0,0 +1,46 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2013, 2014 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_EXTMOD_VFS_FAT_H
#define MICROPY_INCLUDED_EXTMOD_VFS_FAT_H
#include "py/obj.h"
#include "lib/oofatfs/ff.h"
#include "extmod/vfs.h"
typedef struct _fs_user_mount_t {
mp_obj_base_t base;
mp_vfs_blockdev_t blockdev;
FATFS fatfs;
} fs_user_mount_t;
extern const byte fresult_to_errno_table[20];
extern const mp_obj_type_t mp_fat_vfs_type;
extern const mp_obj_type_t mp_type_vfs_fat_fileio;
extern const mp_obj_type_t mp_type_vfs_fat_textio;
MP_DECLARE_CONST_FUN_OBJ_3(fat_vfs_open_obj);
#endif // MICROPY_INCLUDED_EXTMOD_VFS_FAT_H
@@ -0,0 +1,173 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* Original template for this file comes from:
* Low level disk I/O module skeleton for FatFs, (C)ChaN, 2013
*
* The MIT License (MIT)
*
* Copyright (c) 2013, 2014 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/mpconfig.h"
#if MICROPY_VFS && MICROPY_VFS_FAT
#include <stdint.h>
#include <stdio.h>
#include "py/mphal.h"
#include "py/runtime.h"
#include "py/binary.h"
#include "py/objarray.h"
#include "py/mperrno.h"
#include "lib/oofatfs/ff.h"
#include "lib/oofatfs/diskio.h"
#include "extmod/vfs_fat.h"
typedef void *bdev_t;
STATIC fs_user_mount_t *disk_get_device(void *bdev) {
return (fs_user_mount_t*)bdev;
}
/*-----------------------------------------------------------------------*/
/* Read Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_read (
bdev_t pdrv, /* Physical drive nmuber (0..) */
BYTE *buff, /* Data buffer to store read data */
DWORD sector, /* Sector address (LBA) */
UINT count /* Number of sectors to read (1..128) */
)
{
fs_user_mount_t *vfs = disk_get_device(pdrv);
if (vfs == NULL) {
return RES_PARERR;
}
int ret = mp_vfs_blockdev_read(&vfs->blockdev, sector, count, buff);
return ret == 0 ? RES_OK : RES_ERROR;
}
/*-----------------------------------------------------------------------*/
/* Write Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_write (
bdev_t pdrv, /* Physical drive nmuber (0..) */
const BYTE *buff, /* Data to be written */
DWORD sector, /* Sector address (LBA) */
UINT count /* Number of sectors to write (1..128) */
)
{
fs_user_mount_t *vfs = disk_get_device(pdrv);
if (vfs == NULL) {
return RES_PARERR;
}
int ret = mp_vfs_blockdev_write(&vfs->blockdev, sector, count, buff);
if (ret == -MP_EROFS) {
// read-only block device
return RES_WRPRT;
}
return ret == 0 ? RES_OK : RES_ERROR;
}
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions */
/*-----------------------------------------------------------------------*/
DRESULT disk_ioctl (
bdev_t pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
fs_user_mount_t *vfs = disk_get_device(pdrv);
if (vfs == NULL) {
return RES_PARERR;
}
// First part: call the relevant method of the underlying block device
static const uint8_t op_map[8] = {
[CTRL_SYNC] = MP_BLOCKDEV_IOCTL_SYNC,
[GET_SECTOR_COUNT] = MP_BLOCKDEV_IOCTL_BLOCK_COUNT,
[GET_SECTOR_SIZE] = MP_BLOCKDEV_IOCTL_BLOCK_SIZE,
[IOCTL_INIT] = MP_BLOCKDEV_IOCTL_INIT,
};
uint8_t bp_op = op_map[cmd & 7];
mp_obj_t ret = mp_const_none;
if (bp_op != 0) {
ret = mp_vfs_blockdev_ioctl(&vfs->blockdev, bp_op, 0);
}
// Second part: convert the result for return
switch (cmd) {
case CTRL_SYNC:
return RES_OK;
case GET_SECTOR_COUNT: {
*((DWORD*)buff) = mp_obj_get_int(ret);
return RES_OK;
}
case GET_SECTOR_SIZE: {
if (ret == mp_const_none) {
// Default sector size
*((WORD*)buff) = 512;
} else {
*((WORD*)buff) = mp_obj_get_int(ret);
}
// need to store ssize because we use it in disk_read/disk_write
vfs->blockdev.block_size = *((WORD*)buff);
return RES_OK;
}
case GET_BLOCK_SIZE:
*((DWORD*)buff) = 1; // erase block size in units of sector size
return RES_OK;
case IOCTL_INIT:
case IOCTL_STATUS: {
DSTATUS stat;
if (ret != mp_const_none && MP_OBJ_SMALL_INT_VALUE(ret) != 0) {
// error initialising
stat = STA_NOINIT;
} else if (vfs->blockdev.writeblocks[0] == MP_OBJ_NULL) {
stat = STA_PROTECT;
} else {
stat = 0;
}
*((DSTATUS*)buff) = stat;
return RES_OK;
}
default:
return RES_PARERR;
}
}
#endif // MICROPY_VFS && MICROPY_VFS_FAT
@@ -0,0 +1,288 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2013, 2014 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/mpconfig.h"
#if MICROPY_VFS && MICROPY_VFS_FAT
#include <stdio.h>
#include "py/runtime.h"
#include "py/stream.h"
#include "py/mperrno.h"
#include "lib/oofatfs/ff.h"
#include "extmod/vfs_fat.h"
// this table converts from FRESULT to POSIX errno
const byte fresult_to_errno_table[20] = {
[FR_OK] = 0,
[FR_DISK_ERR] = MP_EIO,
[FR_INT_ERR] = MP_EIO,
[FR_NOT_READY] = MP_EBUSY,
[FR_NO_FILE] = MP_ENOENT,
[FR_NO_PATH] = MP_ENOENT,
[FR_INVALID_NAME] = MP_EINVAL,
[FR_DENIED] = MP_EACCES,
[FR_EXIST] = MP_EEXIST,
[FR_INVALID_OBJECT] = MP_EINVAL,
[FR_WRITE_PROTECTED] = MP_EROFS,
[FR_INVALID_DRIVE] = MP_ENODEV,
[FR_NOT_ENABLED] = MP_ENODEV,
[FR_NO_FILESYSTEM] = MP_ENODEV,
[FR_MKFS_ABORTED] = MP_EIO,
[FR_TIMEOUT] = MP_EIO,
[FR_LOCKED] = MP_EIO,
[FR_NOT_ENOUGH_CORE] = MP_ENOMEM,
[FR_TOO_MANY_OPEN_FILES] = MP_EMFILE,
[FR_INVALID_PARAMETER] = MP_EINVAL,
};
typedef struct _pyb_file_obj_t {
mp_obj_base_t base;
FIL fp;
} pyb_file_obj_t;
STATIC void file_obj_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
(void)kind;
mp_printf(print, "<io.%s %p>", mp_obj_get_type_str(self_in), MP_OBJ_TO_PTR(self_in));
}
STATIC mp_uint_t file_obj_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) {
pyb_file_obj_t *self = MP_OBJ_TO_PTR(self_in);
UINT sz_out;
FRESULT res = f_read(&self->fp, buf, size, &sz_out);
if (res != FR_OK) {
*errcode = fresult_to_errno_table[res];
return MP_STREAM_ERROR;
}
return sz_out;
}
STATIC mp_uint_t file_obj_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) {
pyb_file_obj_t *self = MP_OBJ_TO_PTR(self_in);
UINT sz_out;
FRESULT res = f_write(&self->fp, buf, size, &sz_out);
if (res != FR_OK) {
*errcode = fresult_to_errno_table[res];
return MP_STREAM_ERROR;
}
if (sz_out != size) {
// The FatFS documentation says that this means disk full.
*errcode = MP_ENOSPC;
return MP_STREAM_ERROR;
}
return sz_out;
}
STATIC mp_obj_t file_obj___exit__(size_t n_args, const mp_obj_t *args) {
(void)n_args;
return mp_stream_close(args[0]);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(file_obj___exit___obj, 4, 4, file_obj___exit__);
STATIC mp_uint_t file_obj_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) {
pyb_file_obj_t *self = MP_OBJ_TO_PTR(o_in);
if (request == MP_STREAM_SEEK) {
struct mp_stream_seek_t *s = (struct mp_stream_seek_t*)(uintptr_t)arg;
switch (s->whence) {
case 0: // SEEK_SET
f_lseek(&self->fp, s->offset);
break;
case 1: // SEEK_CUR
f_lseek(&self->fp, f_tell(&self->fp) + s->offset);
break;
case 2: // SEEK_END
f_lseek(&self->fp, f_size(&self->fp) + s->offset);
break;
}
s->offset = f_tell(&self->fp);
return 0;
} else if (request == MP_STREAM_FLUSH) {
FRESULT res = f_sync(&self->fp);
if (res != FR_OK) {
*errcode = fresult_to_errno_table[res];
return MP_STREAM_ERROR;
}
return 0;
} else if (request == MP_STREAM_CLOSE) {
// if fs==NULL then the file is closed and in that case this method is a no-op
if (self->fp.obj.fs != NULL) {
FRESULT res = f_close(&self->fp);
if (res != FR_OK) {
*errcode = fresult_to_errno_table[res];
return MP_STREAM_ERROR;
}
}
return 0;
} else {
*errcode = MP_EINVAL;
return MP_STREAM_ERROR;
}
}
// Note: encoding is ignored for now; it's also not a valid kwarg for CPython's FileIO,
// but by adding it here we can use one single mp_arg_t array for open() and FileIO's constructor
STATIC const mp_arg_t file_open_args[] = {
{ MP_QSTR_file, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_mode, MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_QSTR(MP_QSTR_r)} },
{ MP_QSTR_encoding, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_NONE} },
};
#define FILE_OPEN_NUM_ARGS MP_ARRAY_SIZE(file_open_args)
STATIC mp_obj_t file_open(fs_user_mount_t *vfs, const mp_obj_type_t *type, mp_arg_val_t *args) {
int mode = 0;
const char *mode_s = mp_obj_str_get_str(args[1].u_obj);
// TODO make sure only one of r, w, x, a, and b, t are specified
while (*mode_s) {
switch (*mode_s++) {
case 'r':
mode |= FA_READ;
break;
case 'w':
mode |= FA_WRITE | FA_CREATE_ALWAYS;
break;
case 'x':
mode |= FA_WRITE | FA_CREATE_NEW;
break;
case 'a':
mode |= FA_WRITE | FA_OPEN_ALWAYS;
break;
case '+':
mode |= FA_READ | FA_WRITE;
break;
#if MICROPY_PY_IO_FILEIO
case 'b':
type = &mp_type_vfs_fat_fileio;
break;
#endif
case 't':
type = &mp_type_vfs_fat_textio;
break;
}
}
pyb_file_obj_t *o = m_new_obj_with_finaliser(pyb_file_obj_t);
o->base.type = type;
const char *fname = mp_obj_str_get_str(args[0].u_obj);
assert(vfs != NULL);
FRESULT res = f_open(&vfs->fatfs, &o->fp, fname, mode);
if (res != FR_OK) {
m_del_obj(pyb_file_obj_t, o);
mp_raise_OSError(fresult_to_errno_table[res]);
}
// for 'a' mode, we must begin at the end of the file
if ((mode & FA_OPEN_ALWAYS) != 0) {
f_lseek(&o->fp, f_size(&o->fp));
}
return MP_OBJ_FROM_PTR(o);
}
STATIC mp_obj_t file_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_val_t arg_vals[FILE_OPEN_NUM_ARGS];
mp_arg_parse_all_kw_array(n_args, n_kw, args, FILE_OPEN_NUM_ARGS, file_open_args, arg_vals);
return file_open(NULL, type, arg_vals);
}
// TODO gc hook to close the file if not already closed
STATIC const mp_rom_map_elem_t vfs_fat_rawfile_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
{ MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
{ MP_ROM_QSTR(MP_QSTR_readlines), MP_ROM_PTR(&mp_stream_unbuffered_readlines_obj) },
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
{ MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&mp_stream_flush_obj) },
{ MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) },
{ MP_ROM_QSTR(MP_QSTR_seek), MP_ROM_PTR(&mp_stream_seek_obj) },
{ MP_ROM_QSTR(MP_QSTR_tell), MP_ROM_PTR(&mp_stream_tell_obj) },
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_stream_close_obj) },
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) },
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&file_obj___exit___obj) },
};
STATIC MP_DEFINE_CONST_DICT(vfs_fat_rawfile_locals_dict, vfs_fat_rawfile_locals_dict_table);
#if MICROPY_PY_IO_FILEIO
STATIC const mp_stream_p_t vfs_fat_fileio_stream_p = {
.read = file_obj_read,
.write = file_obj_write,
.ioctl = file_obj_ioctl,
};
const mp_obj_type_t mp_type_vfs_fat_fileio = {
{ &mp_type_type },
.name = MP_QSTR_FileIO,
.print = file_obj_print,
.make_new = file_obj_make_new,
.getiter = mp_identity_getiter,
.iternext = mp_stream_unbuffered_iter,
.protocol = &vfs_fat_fileio_stream_p,
.locals_dict = (mp_obj_dict_t*)&vfs_fat_rawfile_locals_dict,
};
#endif
STATIC const mp_stream_p_t vfs_fat_textio_stream_p = {
.read = file_obj_read,
.write = file_obj_write,
.ioctl = file_obj_ioctl,
.is_text = true,
};
const mp_obj_type_t mp_type_vfs_fat_textio = {
{ &mp_type_type },
.name = MP_QSTR_TextIOWrapper,
.print = file_obj_print,
.make_new = file_obj_make_new,
.getiter = mp_identity_getiter,
.iternext = mp_stream_unbuffered_iter,
.protocol = &vfs_fat_textio_stream_p,
.locals_dict = (mp_obj_dict_t*)&vfs_fat_rawfile_locals_dict,
};
// Factory function for I/O stream classes
STATIC mp_obj_t fatfs_builtin_open_self(mp_obj_t self_in, mp_obj_t path, mp_obj_t mode) {
// TODO: analyze buffering args and instantiate appropriate type
fs_user_mount_t *self = MP_OBJ_TO_PTR(self_in);
mp_arg_val_t arg_vals[FILE_OPEN_NUM_ARGS];
arg_vals[0].u_obj = path;
arg_vals[1].u_obj = mode;
arg_vals[2].u_obj = mp_const_none;
return file_open(self, &mp_type_vfs_fat_textio, arg_vals);
}
MP_DEFINE_CONST_FUN_OBJ_3(fat_vfs_open_obj, fatfs_builtin_open_self);
#endif // MICROPY_VFS && MICROPY_VFS_FAT
@@ -0,0 +1,372 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2017-2018 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/runtime.h"
#include "py/mperrno.h"
#include "extmod/vfs.h"
#include "extmod/vfs_posix.h"
#if MICROPY_VFS_POSIX
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <dirent.h>
typedef struct _mp_obj_vfs_posix_t {
mp_obj_base_t base;
vstr_t root;
size_t root_len;
bool readonly;
} mp_obj_vfs_posix_t;
STATIC const char *vfs_posix_get_path_str(mp_obj_vfs_posix_t *self, mp_obj_t path) {
if (self->root_len == 0) {
return mp_obj_str_get_str(path);
} else {
self->root.len = self->root_len;
vstr_add_str(&self->root, mp_obj_str_get_str(path));
return vstr_null_terminated_str(&self->root);
}
}
STATIC mp_obj_t vfs_posix_get_path_obj(mp_obj_vfs_posix_t *self, mp_obj_t path) {
if (self->root_len == 0) {
return path;
} else {
self->root.len = self->root_len;
vstr_add_str(&self->root, mp_obj_str_get_str(path));
return mp_obj_new_str(self->root.buf, self->root.len);
}
}
STATIC mp_obj_t vfs_posix_fun1_helper(mp_obj_t self_in, mp_obj_t path_in, int (*f)(const char*)) {
mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
int ret = f(vfs_posix_get_path_str(self, path_in));
if (ret != 0) {
mp_raise_OSError(errno);
}
return mp_const_none;
}
STATIC mp_import_stat_t mp_vfs_posix_import_stat(void *self_in, const char *path) {
mp_obj_vfs_posix_t *self = self_in;
if (self->root_len != 0) {
self->root.len = self->root_len;
vstr_add_str(&self->root, path);
path = vstr_null_terminated_str(&self->root);
}
struct stat st;
if (stat(path, &st) == 0) {
if (S_ISDIR(st.st_mode)) {
return MP_IMPORT_STAT_DIR;
} else if (S_ISREG(st.st_mode)) {
return MP_IMPORT_STAT_FILE;
}
}
return MP_IMPORT_STAT_NO_EXIST;
}
STATIC mp_obj_t vfs_posix_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 0, 1, false);
mp_obj_vfs_posix_t *vfs = m_new_obj(mp_obj_vfs_posix_t);
vfs->base.type = type;
vstr_init(&vfs->root, 0);
if (n_args == 1) {
vstr_add_str(&vfs->root, mp_obj_str_get_str(args[0]));
vstr_add_char(&vfs->root, '/');
}
vfs->root_len = vfs->root.len;
vfs->readonly = false;
return MP_OBJ_FROM_PTR(vfs);
}
STATIC mp_obj_t vfs_posix_mount(mp_obj_t self_in, mp_obj_t readonly, mp_obj_t mkfs) {
mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
if (mp_obj_is_true(readonly)) {
self->readonly = true;
}
if (mp_obj_is_true(mkfs)) {
mp_raise_OSError(MP_EPERM);
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_3(vfs_posix_mount_obj, vfs_posix_mount);
STATIC mp_obj_t vfs_posix_umount(mp_obj_t self_in) {
(void)self_in;
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(vfs_posix_umount_obj, vfs_posix_umount);
STATIC mp_obj_t vfs_posix_open(mp_obj_t self_in, mp_obj_t path_in, mp_obj_t mode_in) {
mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
const char *mode = mp_obj_str_get_str(mode_in);
if (self->readonly
&& (strchr(mode, 'w') != NULL || strchr(mode, 'a') != NULL || strchr(mode, '+') != NULL)) {
mp_raise_OSError(MP_EROFS);
}
if (!mp_obj_is_small_int(path_in)) {
path_in = vfs_posix_get_path_obj(self, path_in);
}
return mp_vfs_posix_file_open(&mp_type_textio, path_in, mode_in);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_3(vfs_posix_open_obj, vfs_posix_open);
STATIC mp_obj_t vfs_posix_chdir(mp_obj_t self_in, mp_obj_t path_in) {
return vfs_posix_fun1_helper(self_in, path_in, chdir);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_chdir_obj, vfs_posix_chdir);
STATIC mp_obj_t vfs_posix_getcwd(mp_obj_t self_in) {
mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
char buf[MICROPY_ALLOC_PATH_MAX + 1];
const char *ret = getcwd(buf, sizeof(buf));
if (ret == NULL) {
mp_raise_OSError(errno);
}
ret += self->root_len;
return mp_obj_new_str(ret, strlen(ret));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(vfs_posix_getcwd_obj, vfs_posix_getcwd);
typedef struct _vfs_posix_ilistdir_it_t {
mp_obj_base_t base;
mp_fun_1_t iternext;
bool is_str;
DIR *dir;
} vfs_posix_ilistdir_it_t;
STATIC mp_obj_t vfs_posix_ilistdir_it_iternext(mp_obj_t self_in) {
vfs_posix_ilistdir_it_t *self = MP_OBJ_TO_PTR(self_in);
if (self->dir == NULL) {
return MP_OBJ_STOP_ITERATION;
}
for (;;) {
struct dirent *dirent = readdir(self->dir);
if (dirent == NULL) {
closedir(self->dir);
self->dir = NULL;
return MP_OBJ_STOP_ITERATION;
}
const char *fn = dirent->d_name;
if (fn[0] == '.' && (fn[1] == 0 || fn[1] == '.')) {
// skip . and ..
continue;
}
// make 3-tuple with info about this entry
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL));
if (self->is_str) {
t->items[0] = mp_obj_new_str(fn, strlen(fn));
} else {
t->items[0] = mp_obj_new_bytes((const byte*)fn, strlen(fn));
}
#ifdef _DIRENT_HAVE_D_TYPE
#ifdef DTTOIF
t->items[1] = MP_OBJ_NEW_SMALL_INT(DTTOIF(dirent->d_type));
#else
if (dirent->d_type == DT_DIR) {
t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR);
} else if (dirent->d_type == DT_REG) {
t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFREG);
} else {
t->items[1] = MP_OBJ_NEW_SMALL_INT(dirent->d_type);
}
#endif
#else
// DT_UNKNOWN should have 0 value on any reasonable system
t->items[1] = MP_OBJ_NEW_SMALL_INT(0);
#endif
#ifdef _DIRENT_HAVE_D_INO
t->items[2] = MP_OBJ_NEW_SMALL_INT(dirent->d_ino);
#else
t->items[2] = MP_OBJ_NEW_SMALL_INT(0);
#endif
return MP_OBJ_FROM_PTR(t);
}
}
STATIC mp_obj_t vfs_posix_ilistdir(mp_obj_t self_in, mp_obj_t path_in) {
mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
vfs_posix_ilistdir_it_t *iter = m_new_obj(vfs_posix_ilistdir_it_t);
iter->base.type = &mp_type_polymorph_iter;
iter->iternext = vfs_posix_ilistdir_it_iternext;
iter->is_str = mp_obj_get_type(path_in) == &mp_type_str;
const char *path = vfs_posix_get_path_str(self, path_in);
if (path[0] == '\0') {
path = ".";
}
iter->dir = opendir(path);
if (iter->dir == NULL) {
mp_raise_OSError(errno);
}
return MP_OBJ_FROM_PTR(iter);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_ilistdir_obj, vfs_posix_ilistdir);
typedef struct _mp_obj_listdir_t {
mp_obj_base_t base;
mp_fun_1_t iternext;
DIR *dir;
} mp_obj_listdir_t;
STATIC mp_obj_t vfs_posix_mkdir(mp_obj_t self_in, mp_obj_t path_in) {
mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
int ret = mkdir(vfs_posix_get_path_str(self, path_in), 0777);
if (ret != 0) {
mp_raise_OSError(errno);
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_mkdir_obj, vfs_posix_mkdir);
STATIC mp_obj_t vfs_posix_remove(mp_obj_t self_in, mp_obj_t path_in) {
return vfs_posix_fun1_helper(self_in, path_in, unlink);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_remove_obj, vfs_posix_remove);
STATIC mp_obj_t vfs_posix_rename(mp_obj_t self_in, mp_obj_t old_path_in, mp_obj_t new_path_in) {
mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
const char *old_path = vfs_posix_get_path_str(self, old_path_in);
const char *new_path = vfs_posix_get_path_str(self, new_path_in);
int ret = rename(old_path, new_path);
if (ret != 0) {
mp_raise_OSError(errno);
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_3(vfs_posix_rename_obj, vfs_posix_rename);
STATIC mp_obj_t vfs_posix_rmdir(mp_obj_t self_in, mp_obj_t path_in) {
return vfs_posix_fun1_helper(self_in, path_in, rmdir);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_rmdir_obj, vfs_posix_rmdir);
STATIC mp_obj_t vfs_posix_stat(mp_obj_t self_in, mp_obj_t path_in) {
mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
struct stat sb;
int ret = stat(vfs_posix_get_path_str(self, path_in), &sb);
if (ret != 0) {
mp_raise_OSError(errno);
}
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
t->items[0] = MP_OBJ_NEW_SMALL_INT(sb.st_mode);
t->items[1] = MP_OBJ_NEW_SMALL_INT(sb.st_ino);
t->items[2] = MP_OBJ_NEW_SMALL_INT(sb.st_dev);
t->items[3] = MP_OBJ_NEW_SMALL_INT(sb.st_nlink);
t->items[4] = MP_OBJ_NEW_SMALL_INT(sb.st_uid);
t->items[5] = MP_OBJ_NEW_SMALL_INT(sb.st_gid);
t->items[6] = MP_OBJ_NEW_SMALL_INT(sb.st_size);
t->items[7] = MP_OBJ_NEW_SMALL_INT(sb.st_atime);
t->items[8] = MP_OBJ_NEW_SMALL_INT(sb.st_mtime);
t->items[9] = MP_OBJ_NEW_SMALL_INT(sb.st_ctime);
return MP_OBJ_FROM_PTR(t);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_stat_obj, vfs_posix_stat);
#ifdef __ANDROID__
#define USE_STATFS 1
#endif
#if USE_STATFS
#include <sys/vfs.h>
#define STRUCT_STATVFS struct statfs
#define STATVFS statfs
#define F_FAVAIL sb.f_ffree
#define F_NAMEMAX sb.f_namelen
#define F_FLAG sb.f_flags
#else
#include <sys/statvfs.h>
#define STRUCT_STATVFS struct statvfs
#define STATVFS statvfs
#define F_FAVAIL sb.f_favail
#define F_NAMEMAX sb.f_namemax
#define F_FLAG sb.f_flag
#endif
STATIC mp_obj_t vfs_posix_statvfs(mp_obj_t self_in, mp_obj_t path_in) {
mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
STRUCT_STATVFS sb;
const char *path = vfs_posix_get_path_str(self, path_in);
int ret = STATVFS(path, &sb);
if (ret != 0) {
mp_raise_OSError(errno);
}
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
t->items[0] = MP_OBJ_NEW_SMALL_INT(sb.f_bsize);
t->items[1] = MP_OBJ_NEW_SMALL_INT(sb.f_frsize);
t->items[2] = MP_OBJ_NEW_SMALL_INT(sb.f_blocks);
t->items[3] = MP_OBJ_NEW_SMALL_INT(sb.f_bfree);
t->items[4] = MP_OBJ_NEW_SMALL_INT(sb.f_bavail);
t->items[5] = MP_OBJ_NEW_SMALL_INT(sb.f_files);
t->items[6] = MP_OBJ_NEW_SMALL_INT(sb.f_ffree);
t->items[7] = MP_OBJ_NEW_SMALL_INT(F_FAVAIL);
t->items[8] = MP_OBJ_NEW_SMALL_INT(F_FLAG);
t->items[9] = MP_OBJ_NEW_SMALL_INT(F_NAMEMAX);
return MP_OBJ_FROM_PTR(t);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_statvfs_obj, vfs_posix_statvfs);
STATIC const mp_rom_map_elem_t vfs_posix_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&vfs_posix_mount_obj) },
{ MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&vfs_posix_umount_obj) },
{ MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&vfs_posix_open_obj) },
{ MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&vfs_posix_chdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&vfs_posix_getcwd_obj) },
{ MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&vfs_posix_ilistdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&vfs_posix_mkdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&vfs_posix_remove_obj) },
{ MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&vfs_posix_rename_obj) },
{ MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&vfs_posix_rmdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&vfs_posix_stat_obj) },
{ MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&vfs_posix_statvfs_obj) },
};
STATIC MP_DEFINE_CONST_DICT(vfs_posix_locals_dict, vfs_posix_locals_dict_table);
STATIC const mp_vfs_proto_t vfs_posix_proto = {
.import_stat = mp_vfs_posix_import_stat,
};
const mp_obj_type_t mp_type_vfs_posix = {
{ &mp_type_type },
.name = MP_QSTR_VfsPosix,
.make_new = vfs_posix_make_new,
.protocol = &vfs_posix_proto,
.locals_dict = (mp_obj_dict_t*)&vfs_posix_locals_dict,
};
#endif // MICROPY_VFS_POSIX
@@ -0,0 +1,38 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2018 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_EXTMOD_VFS_POSIX_H
#define MICROPY_INCLUDED_EXTMOD_VFS_POSIX_H
#include "py/lexer.h"
#include "py/obj.h"
extern const mp_obj_type_t mp_type_vfs_posix;
extern const mp_obj_type_t mp_type_vfs_posix_fileio;
extern const mp_obj_type_t mp_type_vfs_posix_textio;
mp_obj_t mp_vfs_posix_file_open(const mp_obj_type_t *type, mp_obj_t file_in, mp_obj_t mode_in);
#endif // MICROPY_INCLUDED_EXTMOD_VFS_POSIX_H
@@ -0,0 +1,261 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2013-2018 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/runtime.h"
#include "py/stream.h"
#include "extmod/vfs_posix.h"
#if MICROPY_VFS_POSIX
#include <fcntl.h>
#ifdef _WIN32
#define fsync _commit
#endif
typedef struct _mp_obj_vfs_posix_file_t {
mp_obj_base_t base;
int fd;
} mp_obj_vfs_posix_file_t;
#ifdef MICROPY_CPYTHON_COMPAT
STATIC void check_fd_is_open(const mp_obj_vfs_posix_file_t *o) {
if (o->fd < 0) {
mp_raise_msg(&mp_type_ValueError, "I/O operation on closed file");
}
}
#else
#define check_fd_is_open(o)
#endif
STATIC void vfs_posix_file_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
(void)kind;
mp_obj_vfs_posix_file_t *self = MP_OBJ_TO_PTR(self_in);
mp_printf(print, "<io.%s %d>", mp_obj_get_type_str(self_in), self->fd);
}
mp_obj_t mp_vfs_posix_file_open(const mp_obj_type_t *type, mp_obj_t file_in, mp_obj_t mode_in) {
mp_obj_vfs_posix_file_t *o = m_new_obj(mp_obj_vfs_posix_file_t);
const char *mode_s = mp_obj_str_get_str(mode_in);
int mode_rw = 0, mode_x = 0;
while (*mode_s) {
switch (*mode_s++) {
case 'r':
mode_rw = O_RDONLY;
break;
case 'w':
mode_rw = O_WRONLY;
mode_x = O_CREAT | O_TRUNC;
break;
case 'a':
mode_rw = O_WRONLY;
mode_x = O_CREAT | O_APPEND;
break;
case '+':
mode_rw = O_RDWR;
break;
#if MICROPY_PY_IO_FILEIO
// If we don't have io.FileIO, then files are in text mode implicitly
case 'b':
type = &mp_type_vfs_posix_fileio;
break;
case 't':
type = &mp_type_vfs_posix_textio;
break;
#endif
}
}
o->base.type = type;
mp_obj_t fid = file_in;
if (mp_obj_is_small_int(fid)) {
o->fd = MP_OBJ_SMALL_INT_VALUE(fid);
return MP_OBJ_FROM_PTR(o);
}
const char *fname = mp_obj_str_get_str(fid);
int fd = open(fname, mode_x | mode_rw, 0644);
if (fd == -1) {
mp_raise_OSError(errno);
}
o->fd = fd;
return MP_OBJ_FROM_PTR(o);
}
STATIC mp_obj_t vfs_posix_file_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_file, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_mode, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_QSTR(MP_QSTR_r)} },
};
mp_arg_val_t arg_vals[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, args, MP_ARRAY_SIZE(allowed_args), allowed_args, arg_vals);
return mp_vfs_posix_file_open(type, arg_vals[0].u_obj, arg_vals[1].u_obj);
}
STATIC mp_obj_t vfs_posix_file_fileno(mp_obj_t self_in) {
mp_obj_vfs_posix_file_t *self = MP_OBJ_TO_PTR(self_in);
check_fd_is_open(self);
return MP_OBJ_NEW_SMALL_INT(self->fd);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(vfs_posix_file_fileno_obj, vfs_posix_file_fileno);
STATIC mp_obj_t vfs_posix_file___exit__(size_t n_args, const mp_obj_t *args) {
(void)n_args;
return mp_stream_close(args[0]);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(vfs_posix_file___exit___obj, 4, 4, vfs_posix_file___exit__);
STATIC mp_uint_t vfs_posix_file_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) {
mp_obj_vfs_posix_file_t *o = MP_OBJ_TO_PTR(o_in);
check_fd_is_open(o);
mp_int_t r = read(o->fd, buf, size);
if (r == -1) {
*errcode = errno;
return MP_STREAM_ERROR;
}
return r;
}
STATIC mp_uint_t vfs_posix_file_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) {
mp_obj_vfs_posix_file_t *o = MP_OBJ_TO_PTR(o_in);
check_fd_is_open(o);
#if MICROPY_PY_OS_DUPTERM
if (o->fd <= STDERR_FILENO) {
mp_hal_stdout_tx_strn(buf, size);
return size;
}
#endif
mp_int_t r = write(o->fd, buf, size);
while (r == -1 && errno == EINTR) {
if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) {
mp_obj_t obj = MP_STATE_VM(mp_pending_exception);
MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL;
nlr_raise(obj);
}
r = write(o->fd, buf, size);
}
if (r == -1) {
*errcode = errno;
return MP_STREAM_ERROR;
}
return r;
}
STATIC mp_uint_t vfs_posix_file_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) {
mp_obj_vfs_posix_file_t *o = MP_OBJ_TO_PTR(o_in);
check_fd_is_open(o);
switch (request) {
case MP_STREAM_FLUSH:
if (fsync(o->fd) < 0) {
*errcode = errno;
return MP_STREAM_ERROR;
}
return 0;
case MP_STREAM_SEEK: {
struct mp_stream_seek_t *s = (struct mp_stream_seek_t*)arg;
off_t off = lseek(o->fd, s->offset, s->whence);
if (off == (off_t)-1) {
*errcode = errno;
return MP_STREAM_ERROR;
}
s->offset = off;
return 0;
}
case MP_STREAM_CLOSE:
close(o->fd);
#ifdef MICROPY_CPYTHON_COMPAT
o->fd = -1;
#endif
return 0;
default:
*errcode = EINVAL;
return MP_STREAM_ERROR;
}
}
STATIC const mp_rom_map_elem_t vfs_posix_rawfile_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_fileno), MP_ROM_PTR(&vfs_posix_file_fileno_obj) },
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
{ MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
{ MP_ROM_QSTR(MP_QSTR_readlines), MP_ROM_PTR(&mp_stream_unbuffered_readlines_obj) },
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
{ MP_ROM_QSTR(MP_QSTR_seek), MP_ROM_PTR(&mp_stream_seek_obj) },
{ MP_ROM_QSTR(MP_QSTR_tell), MP_ROM_PTR(&mp_stream_tell_obj) },
{ MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&mp_stream_flush_obj) },
{ MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) },
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) },
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&vfs_posix_file___exit___obj) },
};
STATIC MP_DEFINE_CONST_DICT(vfs_posix_rawfile_locals_dict, vfs_posix_rawfile_locals_dict_table);
#if MICROPY_PY_IO_FILEIO
STATIC const mp_stream_p_t vfs_posix_fileio_stream_p = {
.read = vfs_posix_file_read,
.write = vfs_posix_file_write,
.ioctl = vfs_posix_file_ioctl,
};
const mp_obj_type_t mp_type_vfs_posix_fileio = {
{ &mp_type_type },
.name = MP_QSTR_FileIO,
.print = vfs_posix_file_print,
.make_new = vfs_posix_file_make_new,
.getiter = mp_identity_getiter,
.iternext = mp_stream_unbuffered_iter,
.protocol = &vfs_posix_fileio_stream_p,
.locals_dict = (mp_obj_dict_t*)&vfs_posix_rawfile_locals_dict,
};
#endif
STATIC const mp_stream_p_t vfs_posix_textio_stream_p = {
.read = vfs_posix_file_read,
.write = vfs_posix_file_write,
.ioctl = vfs_posix_file_ioctl,
.is_text = true,
};
const mp_obj_type_t mp_type_vfs_posix_textio = {
{ &mp_type_type },
.name = MP_QSTR_TextIOWrapper,
.print = vfs_posix_file_print,
.make_new = vfs_posix_file_make_new,
.getiter = mp_identity_getiter,
.iternext = mp_stream_unbuffered_iter,
.protocol = &vfs_posix_textio_stream_p,
.locals_dict = (mp_obj_dict_t*)&vfs_posix_rawfile_locals_dict,
};
const mp_obj_vfs_posix_file_t mp_sys_stdin_obj = {{&mp_type_textio}, STDIN_FILENO};
const mp_obj_vfs_posix_file_t mp_sys_stdout_obj = {{&mp_type_textio}, STDOUT_FILENO};
const mp_obj_vfs_posix_file_t mp_sys_stderr_obj = {{&mp_type_textio}, STDERR_FILENO};
#endif // MICROPY_VFS_POSIX
@@ -0,0 +1,87 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2013-2017 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdio.h>
#include <string.h>
#include "py/runtime.h"
#include "py/stream.h"
#include "py/reader.h"
#include "extmod/vfs.h"
#if MICROPY_READER_VFS
typedef struct _mp_reader_vfs_t {
mp_obj_t file;
uint16_t len;
uint16_t pos;
byte buf[24];
} mp_reader_vfs_t;
STATIC mp_uint_t mp_reader_vfs_readbyte(void *data) {
mp_reader_vfs_t *reader = (mp_reader_vfs_t*)data;
if (reader->pos >= reader->len) {
if (reader->len < sizeof(reader->buf)) {
return MP_READER_EOF;
} else {
int errcode;
reader->len = mp_stream_rw(reader->file, reader->buf, sizeof(reader->buf),
&errcode, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE);
if (errcode != 0) {
// TODO handle errors properly
return MP_READER_EOF;
}
if (reader->len == 0) {
return MP_READER_EOF;
}
reader->pos = 0;
}
}
return reader->buf[reader->pos++];
}
STATIC void mp_reader_vfs_close(void *data) {
mp_reader_vfs_t *reader = (mp_reader_vfs_t*)data;
mp_stream_close(reader->file);
m_del_obj(mp_reader_vfs_t, reader);
}
void mp_reader_new_file(mp_reader_t *reader, const char *filename) {
mp_reader_vfs_t *rf = m_new_obj(mp_reader_vfs_t);
mp_obj_t arg = mp_obj_new_str(filename, strlen(filename));
rf->file = mp_vfs_open(1, &arg, (mp_map_t*)&mp_const_empty_map);
int errcode;
rf->len = mp_stream_rw(rf->file, rf->buf, sizeof(rf->buf), &errcode, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE);
if (errcode != 0) {
mp_raise_OSError(errcode);
}
rf->pos = 0;
reader->data = rf;
reader->readbyte = mp_reader_vfs_readbyte;
reader->close = mp_reader_vfs_close;
}
#endif // MICROPY_READER_VFS
@@ -0,0 +1,39 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Paul Sokolovsky
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "extmod/virtpin.h"
int mp_virtual_pin_read(mp_obj_t pin) {
mp_obj_base_t* s = (mp_obj_base_t*)MP_OBJ_TO_PTR(pin);
mp_pin_p_t *pin_p = (mp_pin_p_t*)s->type->protocol;
return pin_p->ioctl(pin, MP_PIN_READ, 0, NULL);
}
void mp_virtual_pin_write(mp_obj_t pin, int value) {
mp_obj_base_t* s = (mp_obj_base_t*)MP_OBJ_TO_PTR(pin);
mp_pin_p_t *pin_p = (mp_pin_p_t*)s->type->protocol;
pin_p->ioctl(pin, MP_PIN_WRITE, value, NULL);
}
@@ -0,0 +1,47 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Paul Sokolovsky
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_EXTMOD_VIRTPIN_H
#define MICROPY_INCLUDED_EXTMOD_VIRTPIN_H
#include "py/obj.h"
#define MP_PIN_READ (1)
#define MP_PIN_WRITE (2)
#define MP_PIN_INPUT (3)
#define MP_PIN_OUTPUT (4)
// Pin protocol
typedef struct _mp_pin_p_t {
mp_uint_t (*ioctl)(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode);
} mp_pin_p_t;
int mp_virtual_pin_read(mp_obj_t pin);
void mp_virtual_pin_write(mp_obj_t pin, int value);
// If a port exposes a Pin object, it's constructor should be like this
mp_obj_t mp_pin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args);
#endif // MICROPY_INCLUDED_EXTMOD_VIRTPIN_H
@@ -0,0 +1 @@
freeze('.', ('webrepl.py', 'webrepl_setup.py', 'websocket_helper.py',))
@@ -0,0 +1,80 @@
# This module should be imported from REPL, not run from command line.
import socket
import uos
import network
import uwebsocket
import websocket_helper
import _webrepl
listen_s = None
client_s = None
def setup_conn(port, accept_handler):
global listen_s
listen_s = socket.socket()
listen_s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
ai = socket.getaddrinfo("0.0.0.0", port)
addr = ai[0][4]
listen_s.bind(addr)
listen_s.listen(1)
if accept_handler:
listen_s.setsockopt(socket.SOL_SOCKET, 20, accept_handler)
for i in (network.AP_IF, network.STA_IF):
iface = network.WLAN(i)
if iface.active():
print("WebREPL daemon started on ws://%s:%d" % (iface.ifconfig()[0], port))
return listen_s
def accept_conn(listen_sock):
global client_s
cl, remote_addr = listen_sock.accept()
prev = uos.dupterm(None)
uos.dupterm(prev)
if prev:
print("\nConcurrent WebREPL connection from", remote_addr, "rejected")
cl.close()
return
print("\nWebREPL connection from:", remote_addr)
client_s = cl
websocket_helper.server_handshake(cl)
ws = uwebsocket.websocket(cl, True)
ws = _webrepl._webrepl(ws)
cl.setblocking(False)
# notify REPL on socket incoming data (ESP32/ESP8266-only)
if hasattr(uos, 'dupterm_notify'):
cl.setsockopt(socket.SOL_SOCKET, 20, uos.dupterm_notify)
uos.dupterm(ws)
def stop():
global listen_s, client_s
uos.dupterm(None)
if client_s:
client_s.close()
if listen_s:
listen_s.close()
def start(port=8266, password=None):
stop()
if password is None:
try:
import webrepl_cfg
_webrepl.password(webrepl_cfg.PASS)
setup_conn(port, accept_conn)
print("Started webrepl in normal mode")
except:
print("WebREPL is not configured, run 'import webrepl_setup'")
else:
_webrepl.password(password)
setup_conn(port, accept_conn)
print("Started webrepl in manual override mode")
def start_foreground(port=8266):
stop()
s = setup_conn(port, None)
accept_conn(s)
@@ -0,0 +1,102 @@
import sys
#import uos as os
import os
import machine
RC = "./boot.py"
CONFIG = "./webrepl_cfg.py"
def input_choice(prompt, choices):
while 1:
resp = input(prompt)
if resp in choices:
return resp
def getpass(prompt):
return input(prompt)
def input_pass():
while 1:
passwd1 = getpass("New password (4-9 chars): ")
if len(passwd1) < 4 or len(passwd1) > 9:
print("Invalid password length")
continue
passwd2 = getpass("Confirm password: ")
if passwd1 == passwd2:
return passwd1
print("Passwords do not match")
def exists(fname):
try:
with open(fname):
pass
return True
except OSError:
return False
def get_daemon_status():
with open(RC) as f:
for l in f:
if "webrepl" in l:
if l.startswith("#"):
return False
return True
return None
def change_daemon(action):
LINES = ("import webrepl", "webrepl.start()")
with open(RC) as old_f, open(RC + ".tmp", "w") as new_f:
found = False
for l in old_f:
for patt in LINES:
if patt in l:
found = True
if action and l.startswith("#"):
l = l[1:]
elif not action and not l.startswith("#"):
l = "#" + l
new_f.write(l)
if not found:
new_f.write("import webrepl\nwebrepl.start()\n")
# FatFs rename() is not POSIX compliant, will raise OSError if
# dest file exists.
os.remove(RC)
os.rename(RC + ".tmp", RC)
def main():
status = get_daemon_status()
print("WebREPL daemon auto-start status:", "enabled" if status else "disabled")
print("\nWould you like to (E)nable or (D)isable it running on boot?")
print("(Empty line to quit)")
resp = input("> ").upper()
if resp == "E":
if exists(CONFIG):
resp2 = input_choice("Would you like to change WebREPL password? (y/n) ", ("y", "n", ""))
else:
print("To enable WebREPL, you must set password for it")
resp2 = "y"
if resp2 == "y":
passwd = input_pass()
with open(CONFIG, "w") as f:
f.write("PASS = %r\n" % passwd)
if resp not in ("D", "E") or (resp == "D" and not status) or (resp == "E" and status):
print("No further action required")
sys.exit()
change_daemon(resp == "E")
print("Changes will be activated after reboot")
resp = input_choice("Would you like to reboot now? (y/n) ", ("y", "n", ""))
if resp == "y":
machine.reset()
main()
@@ -0,0 +1,74 @@
import sys
try:
import ubinascii as binascii
except:
import binascii
try:
import uhashlib as hashlib
except:
import hashlib
DEBUG = 0
def server_handshake(sock):
clr = sock.makefile("rwb", 0)
l = clr.readline()
#sys.stdout.write(repr(l))
webkey = None
while 1:
l = clr.readline()
if not l:
raise OSError("EOF in headers")
if l == b"\r\n":
break
# sys.stdout.write(l)
h, v = [x.strip() for x in l.split(b":", 1)]
if DEBUG:
print((h, v))
if h == b'Sec-WebSocket-Key':
webkey = v
if not webkey:
raise OSError("Not a websocket request")
if DEBUG:
print("Sec-WebSocket-Key:", webkey, len(webkey))
d = hashlib.sha1(webkey)
d.update(b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
respkey = d.digest()
respkey = binascii.b2a_base64(respkey)[:-1]
if DEBUG:
print("respkey:", respkey)
sock.send(b"""\
HTTP/1.1 101 Switching Protocols\r
Upgrade: websocket\r
Connection: Upgrade\r
Sec-WebSocket-Accept: """)
sock.send(respkey)
sock.send("\r\n\r\n")
# Very simplified client handshake, works for MicroPython's
# websocket server implementation, but probably not for other
# servers.
def client_handshake(sock):
cl = sock.makefile("rwb", 0)
cl.write(b"""\
GET / HTTP/1.1\r
Host: echo.websocket.org\r
Connection: Upgrade\r
Upgrade: websocket\r
Sec-WebSocket-Key: foo\r
\r
""")
l = cl.readline()
# print(l)
while 1:
l = cl.readline()
if l == b"\r\n":
break
# sys.stdout.write(l)