Compare commits

..

8 Commits

Author SHA1 Message Date
4c45ab8701 Add more debug 2017-07-26 16:17:59 +01:00
f4c9e85924 Bring back read page alignmnet 2017-07-26 11:52:51 +01:00
33863b8b96 Fix warning 2017-07-26 11:38:31 +01:00
b167d4f9df Apply a283c03a3a 2017-07-26 11:36:52 +01:00
f00b4d432e Apply b894f60a23 2017-07-26 11:36:26 +01:00
f361b6ecc1 Apply e5fd39d9b8 2017-07-26 11:32:49 +01:00
b46e18b807 Apply aae86e8adb 2017-07-26 11:27:15 +01:00
067f8727a6 Apply b00ce11f00 2017-07-26 11:27:01 +01:00
24 changed files with 239 additions and 598 deletions

View File

@ -9,12 +9,12 @@ This repo contains the editor target hosted at https://lego.makecode.com
These instructions assume familiarity with dev tools and languages.
* install Node.js 6+
* install Docker; make sure `docker` command is in your `PATH`
* install [yotta](http://docs.yottabuild.org/#installing)
* (optional) install [Visual Studio Code](https://code.visualstudio.com/)
In a common folder,
* clone https://github.com/Microsoft/pxt to ``pxt`` folder (currently, build against `freshcoat` branch)
* clone https://github.com/Microsoft/pxt to ``pxt`` folder
* clone https://github.com/Microsoft/pxt-common-packages to ``pxt-common-packages`` folder
* clone https://github.com/Microsoft/pxt-ev3 to ``pxt-ev3`` folder
* go to ``pxt`` and run

View File

@ -11,9 +11,8 @@ they are derived from GPLv2 code).
* the `d_usbdev` uses the composite framework to register an additional mass storage function in addtion
to the pre-existing custom USB HID function
* the `g_mass_storage` module has the following changes:
* a bug fixed, where page-misaligned writes would hang
* additional `/sys/.../lun0/active` entry is added, which allows for signaling drive eject to the host
* `d_usbdev` has an additional `ioctl()` to pretend data came from the USB host - this can be used to direct
the VM to do stuff
### Kernel modifications

View File

@ -5,6 +5,6 @@ cd /mnt/ramdisk/prjs/ko
#echo 3 > /proc/sys/kernel/printk
insmod ./nbd.ko
sleep 1
./uf2d /dev/nbd1 > /tmp/uf2d.log 2> /tmp/uf2derr.log
./uf2d > /tmp/uf2d.log 2> /tmp/uf2derr.log
sleep 1
insmod ./d_usbdev.ko file=/dev/nbd1 HostStr=EV3 SerialStr=0016535543af
insmod ./d_usbdev.ko file=/dev/nbd0 HostStr=EV3 SerialStr=0016535543af

View File

@ -543,27 +543,12 @@ static int Device1Mmap(struct file *filp, struct vm_area_struct *vma)
return (ret);
}
#define FEED_DATA _IOC(_IOC_WRITE, 't', 108, 1024)
static int Device1Ioctl(struct inode *pNode, struct file *File, unsigned int Request, unsigned long Pointer)
{
if (Request != FEED_DATA)
return -EINVAL;
copy_from_user(usb_char_buffer_out,(void*)Pointer,1024);
usb_char_out_length = 1024;
return 0;
}
static const struct file_operations Device1Entries =
{
.owner = THIS_MODULE,
.read = Device1Read,
.write = Device1Write,
.mmap = Device1Mmap,
.ioctl = Device1Ioctl
.mmap = Device1Mmap
};

View File

@ -288,6 +288,7 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/composite.h>
#include "gadget_chips.h"
@ -302,7 +303,6 @@ static const char fsg_string_interface[] = "Mass Storage";
#define FSG_NO_INTR_EP 1
#define FSG_BUFFHD_STATIC_BUFFER 1
#define FSG_NO_DEVICE_STRINGS 1
#define FSG_NO_OTG 1
#define FSG_NO_INTR_EP 1
@ -318,8 +318,8 @@ struct fsg_dev;
/* Data shared by all the FSG instances. */
struct fsg_common {
struct usb_gadget *gadget;
struct fsg_dev *fsg;
struct fsg_dev *prev_fsg;
struct fsg_dev *fsg, *new_fsg;
wait_queue_head_t fsg_wait;
/* filesem protects: backing files in use */
struct rw_semaphore filesem;
@ -348,7 +348,6 @@ struct fsg_common {
enum fsg_state state; /* For exception handling */
unsigned int exception_req_tag;
u8 config, new_config;
enum data_direction data_dir;
u32 data_size;
u32 data_size_from_cmnd;
@ -588,7 +587,7 @@ static int fsg_setup(struct usb_function *f,
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
if (!fsg->common->config)
if (!fsg_is_set(fsg->common))
return -EOPNOTSUPP;
switch (ctrl->bRequest) {
@ -614,7 +613,12 @@ static int fsg_setup(struct usb_function *f,
return -EDOM;
VDBG(fsg, "get max LUN\n");
*(u8 *) req->buf = fsg->common->nluns - 1;
return 1;
/* Respond with data/status */
req->length = min((u16)1, w_length);
fsg->common->ep0req_name =
ctrl->bRequestType & USB_DIR_IN ? "ep0-in" : "ep0-out";
return ep0_queue(fsg->common);
}
VDBG(fsg,
@ -705,9 +709,7 @@ static int do_read(struct fsg_common *common)
u32 amount_left;
loff_t file_offset, file_offset_tmp;
unsigned int amount;
// partial_page handling causes hangs
// same thing in do_write() --mmoskal
//unsigned int partial_page;
unsigned int partial_page;
ssize_t nread;
/* Get the starting Logical Block Address and check that it's
@ -752,12 +754,10 @@ static int do_read(struct fsg_common *common)
amount = min(amount_left, FSG_BUFLEN);
amount = min((loff_t) amount,
curlun->file_length - file_offset);
/*
partial_page = file_offset & (PAGE_CACHE_SIZE - 1);
if (partial_page > 0)
amount = min(amount, (unsigned int) PAGE_CACHE_SIZE -
partial_page);
*/
/* Wait for the next buffer to become available */
bh = common->next_buffhd_to_fill;
while (bh->state != BUF_STATE_EMPTY) {
@ -840,7 +840,7 @@ static int do_write(struct fsg_common *common)
u32 amount_left_to_req, amount_left_to_write;
loff_t usb_offset, file_offset, file_offset_tmp;
unsigned int amount;
//unsigned int partial_page;
unsigned int partial_page;
ssize_t nwritten;
int rc;
@ -904,12 +904,10 @@ static int do_write(struct fsg_common *common)
amount = min(amount_left_to_req, FSG_BUFLEN);
amount = min((loff_t) amount, curlun->file_length -
usb_offset);
/*
partial_page = usb_offset & (PAGE_CACHE_SIZE - 1);
if (partial_page > 0)
amount = min(amount,
(unsigned int) PAGE_CACHE_SIZE - partial_page);
*/
if (amount == 0) {
get_some_more = 0;
@ -940,6 +938,7 @@ static int do_write(struct fsg_common *common)
bh->outreq->length = amount;
bh->bulk_out_intended_length = amount;
bh->outreq->short_not_ok = 1;
printk("write transfer %d\n", amount);
START_TRANSFER_OR(common, bulk_out, bh->outreq,
&bh->outreq_busy, &bh->state)
/* Don't know what to do if
@ -1016,10 +1015,14 @@ static int do_write(struct fsg_common *common)
continue;
}
printk("before sleep\n");
/* Wait for something to happen */
rc = sleep_thread(common);
if (rc)
return rc;
printk("after sleep\n");
}
return -EIO; /* No default reply */
@ -2263,24 +2266,20 @@ static int alloc_request(struct fsg_common *common, struct usb_ep *ep,
return -ENOMEM;
}
/*
* Reset interface setting and re-init endpoint state (toggle etc).
* Call with altsetting < 0 to disable the interface. The only other
* available altsetting is 0, which enables the interface.
*/
static int do_set_interface(struct fsg_common *common, int altsetting)
/* Reset interface setting and re-init endpoint state (toggle etc). */
static int do_set_interface(struct fsg_common *common, struct fsg_dev *new_fsg)
{
int rc = 0;
int i;
const struct usb_endpoint_descriptor *d;
const struct usb_endpoint_descriptor *d;
struct fsg_dev *fsg;
int i, rc = 0;
if (common->running)
DBG(common, "reset interface\n");
reset:
/* Deallocate the requests */
if (common->prev_fsg) {
struct fsg_dev *fsg = common->prev_fsg;
if (common->fsg) {
fsg = common->fsg;
for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
struct fsg_buffhd *bh = &common->buffhds[i];
@ -2305,88 +2304,53 @@ reset:
fsg->bulk_out_enabled = 0;
}
common->prev_fsg = 0;
common->fsg = NULL;
wake_up(&common->fsg_wait);
}
common->running = 0;
if (altsetting < 0 || rc != 0)
if (!new_fsg || rc)
return rc;
DBG(common, "set interface %d\n", altsetting);
common->fsg = new_fsg;
fsg = common->fsg;
if (fsg_is_set(common)) {
struct fsg_dev *fsg = common->fsg;
common->prev_fsg = common->fsg;
/* Enable the endpoints */
d = fsg_ep_desc(common->gadget,
&fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc);
rc = enable_endpoint(common, fsg->bulk_in, d);
if (rc)
goto reset;
fsg->bulk_in_enabled = 1;
/* Enable the endpoints */
d = fsg_ep_desc(common->gadget,
&fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc);
rc = enable_endpoint(common, fsg->bulk_in, d);
d = fsg_ep_desc(common->gadget,
&fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc);
rc = enable_endpoint(common, fsg->bulk_out, d);
if (rc)
goto reset;
fsg->bulk_out_enabled = 1;
common->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize);
clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
/* Allocate the requests */
for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
struct fsg_buffhd *bh = &common->buffhds[i];
rc = alloc_request(common, fsg->bulk_in, &bh->inreq);
if (rc)
goto reset;
fsg->bulk_in_enabled = 1;
d = fsg_ep_desc(common->gadget,
&fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc);
rc = enable_endpoint(common, fsg->bulk_out, d);
rc = alloc_request(common, fsg->bulk_out, &bh->outreq);
if (rc)
goto reset;
fsg->bulk_out_enabled = 1;
common->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize);
clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
/* Allocate the requests */
for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
struct fsg_buffhd *bh = &common->buffhds[i];
rc = alloc_request(common, fsg->bulk_in, &bh->inreq);
if (rc)
goto reset;
rc = alloc_request(common, fsg->bulk_out, &bh->outreq);
if (rc)
goto reset;
bh->inreq->buf = bh->outreq->buf = bh->buf;
bh->inreq->context = bh->outreq->context = bh;
bh->inreq->complete = bulk_in_complete;
bh->outreq->complete = bulk_out_complete;
}
common->running = 1;
for (i = 0; i < common->nluns; ++i)
common->luns[i].unit_attention_data = SS_RESET_OCCURRED;
return rc;
} else {
return -EIO;
}
}
/*
* Change our operational configuration. This code must agree with the code
* that returns config descriptors, and with interface altsetting code.
*
* It's also responsible for power management interactions. Some
* configurations might not work with our current power sources.
* For now we just assume the gadget is always self-powered.
*/
static int do_set_config(struct fsg_common *common, u8 new_config)
{
int rc = 0;
/* Disable the single interface */
if (common->config != 0) {
DBG(common, "reset config\n");
common->config = 0;
rc = do_set_interface(common, -1);
bh->inreq->buf = bh->outreq->buf = bh->buf;
bh->inreq->context = bh->outreq->context = bh;
bh->inreq->complete = bulk_in_complete;
bh->outreq->complete = bulk_out_complete;
}
/* Enable the interface */
if (new_config != 0) {
common->config = new_config;
rc = do_set_interface(common, 0);
if (rc != 0)
common->config = 0; /* Reset on errors */
}
common->running = 1;
for (i = 0; i < common->nluns; ++i)
common->luns[i].unit_attention_data = SS_RESET_OCCURRED;
return rc;
}
@ -2397,9 +2361,7 @@ static int do_set_config(struct fsg_common *common, u8 new_config)
static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct fsg_dev *fsg = fsg_from_func(f);
fsg->common->prev_fsg = fsg->common->fsg;
fsg->common->fsg = fsg;
fsg->common->new_config = 1;
fsg->common->new_fsg = fsg;
raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
return 0;
}
@ -2407,31 +2369,8 @@ static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
static void fsg_disable(struct usb_function *f)
{
struct fsg_dev *fsg = fsg_from_func(f);
fsg->common->prev_fsg = fsg->common->fsg;
fsg->common->fsg = fsg;
fsg->common->new_config = 0;
DBG(common, "fsg_disable filp=%p\n", fsg->common->luns[0].filp);
raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE_DISABLE);
}
static void shutdown_server(void) {
DBG(fsg_common, "shutdown_server filp=%p\n", fsg_common->luns[0].filp);
if (fsg_common->luns[0].filp) {
uint32_t buf[512 / 4];
loff_t file_offset_tmp = 512 * 50000; // make sure we're outside of FS area
int i;
// this should shut down the nbd server, so that the caches are flushed
memset(buf, 0, sizeof(buf));
buf[0] = 0x20da6d81;
buf[1] = 0x747e09d4;
fsg_common->luns[0].filp->f_flags |= O_SYNC;
for (i = 0; i < 2; ++i)
vfs_write(fsg_common->luns[0].filp,
(char*)buf, 512, &file_offset_tmp);
}
fsg->common->new_fsg = NULL;
raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
}
@ -2440,19 +2379,17 @@ static void shutdown_server(void) {
static void handle_exception(struct fsg_common *common)
{
siginfo_t info;
int sig;
int i;
struct fsg_buffhd *bh;
enum fsg_state old_state;
u8 new_config;
struct fsg_lun *curlun;
unsigned int exception_req_tag;
int rc;
/* Clear the existing signals. Anything but SIGUSR1 is converted
* into a high-priority EXIT exception. */
for (;;) {
sig = dequeue_signal_lock(current, &current->blocked, &info);
int sig =
dequeue_signal_lock(current, &current->blocked, &info);
if (!sig)
break;
if (sig != SIGUSR1) {
@ -2463,7 +2400,7 @@ static void handle_exception(struct fsg_common *common)
}
/* Cancel all the pending transfers */
if (fsg_is_set(common)) {
if (likely(common->fsg)) {
for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
bh = &common->buffhds[i];
if (bh->inreq_busy)
@ -2504,7 +2441,6 @@ static void handle_exception(struct fsg_common *common)
common->next_buffhd_to_fill = &common->buffhds[0];
common->next_buffhd_to_drain = &common->buffhds[0];
exception_req_tag = common->exception_req_tag;
new_config = common->new_config;
old_state = common->state;
if (old_state == FSG_STATE_ABORT_BULK_OUT)
@ -2553,24 +2489,13 @@ static void handle_exception(struct fsg_common *common)
/* SS_RESET_OCCURRED; */
break;
case FSG_STATE_CONFIG_CHANGE_DISABLE:
shutdown_server();
// fall-through
case FSG_STATE_CONFIG_CHANGE:
rc = do_set_config(common, new_config);
if (common->ep0_req_tag != exception_req_tag)
break;
if (rc != 0) { /* STALL on errors */
DBG(common, "ep0 set halt\n");
usb_ep_set_halt(common->ep0);
} else { /* Complete the status stage */
ep0_queue(common);
}
do_set_interface(common, common->new_fsg);
break;
case FSG_STATE_EXIT:
case FSG_STATE_TERMINATED:
do_set_config(common, 0); /* Free resources */
do_set_interface(common, NULL); /* Free resources */
spin_lock_irq(&common->lock);
common->state = FSG_STATE_TERMINATED; /* Stop the thread */
spin_unlock_irq(&common->lock);
@ -2744,7 +2669,7 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
return ERR_PTR(-ENOMEM);
common->free_storage_on_release = 1;
} else {
memset(common, 0, sizeof common);
memset(common, 0, sizeof *common);
common->free_storage_on_release = 0;
}
@ -2822,13 +2747,19 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
/* Data buffers cyclic list */
/* Buffers in buffhds are static -- no need for additional
* allocation. */
bh = common->buffhds;
i = FSG_NUM_BUFFERS - 1;
i = FSG_NUM_BUFFERS;
goto buffhds_first_it;
do {
bh->next = bh + 1;
} while (++bh, --i);
++bh;
buffhds_first_it:
bh->buf = kmalloc(FSG_BUFLEN, GFP_KERNEL);
if (unlikely(!bh->buf)) {
rc = -ENOMEM;
goto error_release;
}
} while (--i);
bh->next = common->buffhds;
@ -2882,6 +2813,7 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
goto error_release;
}
init_completion(&common->thread_notifier);
init_waitqueue_head(&common->fsg_wait);
#undef OR
@ -2936,6 +2868,7 @@ static void fsg_common_release(struct kref *ref)
container_of(ref, struct fsg_common, ref);
unsigned i = common->nluns;
struct fsg_lun *lun = common->luns;
struct fsg_buffhd *bh;
/* If the thread isn't already dead, tell it to exit now */
if (common->state != FSG_STATE_TERMINATED) {
@ -2957,6 +2890,13 @@ static void fsg_common_release(struct kref *ref)
}
kfree(common->luns);
i = FSG_NUM_BUFFERS;
bh = common->buffhds;
do {
kfree(bh->buf);
} while (++bh, --i);
if (common->free_storage_on_release)
kfree(common);
}
@ -2968,9 +2908,19 @@ static void fsg_common_release(struct kref *ref)
static void fsg_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct fsg_dev *fsg = fsg_from_func(f);
struct fsg_common *common = fsg->common;
DBG(fsg, "unbind\n");
fsg_common_put(fsg->common);
if (fsg->common->fsg == fsg) {
fsg->common->new_fsg = NULL;
raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
/* FIXME: make interruptible or killable somehow? */
wait_event(common->fsg_wait, common->fsg != fsg);
}
fsg_common_put(common);
kfree(fsg);
}
@ -2979,7 +2929,6 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
{
struct fsg_dev *fsg = fsg_from_func(f);
struct usb_gadget *gadget = c->cdev->gadget;
int rc;
int i;
struct usb_ep *ep;
@ -3005,6 +2954,13 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
ep->driver_data = fsg->common; /* claim the endpoint */
fsg->bulk_out = ep;
/* Copy descriptors */
f->descriptors = usb_copy_descriptors(fsg_fs_function);
if (unlikely(!f->descriptors)) {
usb_free_descriptors(f->descriptors);
return -ENOMEM;
}
if (gadget_is_dualspeed(gadget)) {
/* Assume endpoint addresses are the same for both speeds */
fsg_hs_bulk_in_desc.bEndpointAddress =
@ -3018,9 +2974,8 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
autoconf_fail:
ERROR(fsg, "unable to autoconfigure all endpoints\n");
rc = -ENOTSUPP;
fsg_unbind(c, f);
return rc;
return -ENOTSUPP;
}

View File

@ -73,6 +73,7 @@
#undef DUMP_MSGS
#endif /* !DEBUG */
#define VERBOSE_DEBUG
#ifdef VERBOSE_DEBUG
#define VLDBG LDBG
#else
@ -334,7 +335,6 @@ enum fsg_state {
FSG_STATE_ABORT_BULK_OUT,
FSG_STATE_RESET,
FSG_STATE_INTERFACE_CHANGE,
FSG_STATE_CONFIG_CHANGE_DISABLE,
FSG_STATE_CONFIG_CHANGE,
FSG_STATE_DISCONNECT,
FSG_STATE_EXIT,

View File

@ -1,14 +0,0 @@
# Patched EV3 image
The file `ev3-fs.patch` summarizes the changes done to the original V1.09D image.
You can see some text files are edited, the `d_usbdev.ko` is updated (sources in `../kernel`),
`uf2d` added (sources in `../uf2daemon`), and a stock `nbd.ko` module is added.
Additionally, the `edimax01.ko` is replaced by now much more popular `rtl8192cu.ko` (also stock).
The init script has a hook for running a shell script from `/mnt/ramdisk/`. This can be used
for testing different modules etc.
The kernel command line has been modified to:
* disable DMA for the MUSB driver - otherwise the mass storage device is very unstable
* increase the size of dmesg buffer to 128k

View File

@ -1,53 +0,0 @@
diff -ur orig-ev3/etc/init.d/ev3init.sh dev-ev3/etc/init.d/ev3init.sh
--- orig-ev3/etc/init.d/ev3init.sh 1970-01-01 01:00:00.000000000 +0100
+++ dev-ev3/etc/init.d/ev3init.sh 2017-07-27 12:19:43.195041798 +0100
@@ -1,5 +1,7 @@
#!/bin/sh
+echo Y > /sys/module/printk/parameters/time
+
bluetoothd -n > /dev/null 2>&1 &
echo "Setting up VirtualDrive...";
@@ -60,3 +62,6 @@
sleep 2
hciattach /dev/ttyS2 texas 2000000 "flow" "nosleep" $STRING
sdptool add SP
+
+insmod /lib/modules/2.6.33-rc4/kernel/drivers/net/wireless/rtl8192cu.ko
+. /mnt/ramdisk/rc.local || :
Only in orig-ev3/home/root/lms2012/sys: exit~
diff -ur orig-ev3/home/root/lms2012/sys/init dev-ev3/home/root/lms2012/sys/init
--- orig-ev3/home/root/lms2012/sys/init 1970-01-01 01:00:00.000000000 +0100
+++ dev-ev3/home/root/lms2012/sys/init 2017-07-27 12:23:43.072605126 +0100
@@ -5,13 +5,15 @@
var=$(printf 'HostStr=%s SerialStr=%s' $(cat /home/root/lms2012/sys/settings/BrickName) $(cat /home/root/lms2012/sys/settings/BTser))
echo $var > /home/root/lms2012/sys/settings/UsbInfo.dat
+insmod ${PWD}/mod/nbd.ko
+${PWD}/uf2d
insmod ${PWD}/mod/d_iic.ko `cat /home/root/lms2012/sys/settings/HwId`
insmod ${PWD}/mod/d_uart.ko `cat /home/root/lms2012/sys/settings/HwId`
insmod ${PWD}/mod/d_power.ko `cat /home/root/lms2012/sys/settings/HwId`
insmod ${PWD}/mod/d_pwm.ko `cat /home/root/lms2012/sys/settings/HwId`
insmod ${PWD}/mod/d_ui.ko `cat /home/root/lms2012/sys/settings/HwId`
insmod ${PWD}/mod/d_analog.ko `cat /home/root/lms2012/sys/settings/HwId`
-insmod ${PWD}/mod/d_usbdev.ko `cat /home/root/lms2012/sys/settings/UsbInfo.dat`
+insmod ${PWD}/mod/d_usbdev.ko `cat /home/root/lms2012/sys/settings/UsbInfo.dat` file=/dev/nbd0
insmod ${PWD}/mod/d_usbhost.ko
insmod ${PWD}/mod/d_sound.ko `cat /home/root/lms2012/sys/settings/HwId`
insmod ${PWD}/mod/d_bt.ko `cat /home/root/lms2012/sys/settings/HwId`
@@ -29,6 +31,8 @@
chmod 666 /dev/lms_iic
chmod 666 /dev/lms_bt
+echo 4 > /proc/sys/kernel/printk
+
cd ..
ls -R > /dev/null
cd sys
Binary files orig-ev3/home/root/lms2012/sys/mod/d_usbdev.ko and dev-ev3/home/root/lms2012/sys/mod/d_usbdev.ko differ
Only in dev-ev3/home/root/lms2012/sys/mod: nbd.ko
Only in dev-ev3/home/root/lms2012/sys: uf2d
Only in orig-ev3/lib/modules/2.6.33-rc4/kernel/drivers/net/wireless: edimax01.ko
Only in dev-ev3/lib/modules/2.6.33-rc4/kernel/drivers/net/wireless: rtl8192cu.ko

View File

@ -1,36 +1,14 @@
#!/bin/sh
let fs = require("fs")
// we try to use shorter versions of all parameters for the additional parameters to fit
let bootargs = "mem=${memsize} initrd=${filesysaddr},${filesyssize} root=/dev/ram0 rw rootfstype=cramfs console=${console} ip=${ipaddr} lpj=747520 quiet"
let bootnews = "mem=64M initrd=0xC1180000,10M root=1:0 rw rootfstype=cramfs console=${console} lpj=747520 musb_hdrc.use_dma=0 log_buf_len=128k quiet"
let piggy = true
function build() {
if (bootnews.length > bootargs.length) {
console.log("args too long")
return
}
while (bootnews.length < bootargs.length)
bootnews += " "
let cr = fs.readFileSync("cram.bin")
if (cr.length > 10485760) {
console.log("too big by " + (cr.length - 10485760))
if (cr.length > 10878976) {
console.log("too big")
return
}
let img = fs.readFileSync("EV3 Firmware V1.09D.bin")
for (let i = 0; i < bootnews.length; ++i) {
if (img[0x21DDA + i] != bootargs.charCodeAt(i)) {
console.log("boot args mismatch")
return
}
img[0x21DDA + i] = bootnews.charCodeAt(i)
}
let img = fs.readFileSync("boot.bin")
let off = 0x250000
if (img[off] != 0x45 || img[off + 1] != 0x3d) {
console.log("bad magic: " + img[off] + " / " + img[off+1])
@ -39,8 +17,8 @@ function build() {
cr.copy(img, off)
let kern = fs.readFileSync(piggy ? "piggy-patched.gzip" : "linux/arch/arm/boot/uImage")
off = piggy ? 0x0005540f : 0x00050000
let kern = fs.readFileSync("piggy-patched.gzip")
off = 0x0005540f
if (img[off] != kern[0] || img[off+1] != kern[1]) {
console.log("bad kernel magic: " + img[off] + " / " + img[off+1])

View File

@ -1,9 +1,4 @@
#!/bin/sh
# dd if=EV3\ Firmware\ V1.09D.bin of=orig.cram bs=65536 skip=37 count=400
sudo cp linux/pxt/uf2daemon/server dev-ev3/home/root/lms2012/sys/uf2d
sudo cp linux/pxt/kernel/d_usbdev.ko dev-ev3/home/root/lms2012/sys/mod/d_usbdev.ko
sudo chown -R root:root dev-ev3/home/root/lms2012
sudo mkfs.cramfs dev-ev3 cram.bin
node img
ls -l firmware.bin

View File

@ -19,9 +19,6 @@
#include <time.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>
#define max(a, b) \
({ \
@ -630,48 +627,6 @@ void read_block(uint32_t block_no, uint8_t *data) {
}
}
char rbfPath[300];
uint8_t stopApp[] = {
0x05, 0x00, // size
0x00, 0x00, // seq. no.
0x3f, 0x3d, // usb magic,
0x02, // req. no.
};
uint8_t runStart[] = {0x00, 0x00, // size
0x00, 0x00, // seq. no.
0x00, 0x00, 0x08, // something
0xC0, 0x08, 0x82, 0x01, 0x00, 0x84};
uint8_t runEnd[] = {0x00, 0x60, 0x64, 0x03, 0x01, 0x60, 0x64, 0x00};
#define FEED_DATA _IOC(_IOC_WRITE, 't', 108, 1024)
void startRbf() {
char buf[1024];
memset(buf, 0, sizeof(buf));
memcpy(buf, stopApp, sizeof(stopApp));
int fd = open("/dev/lms_usbdev", O_RDWR);
ioctl(fd, FEED_DATA, buf);
usleep(500000);
int off = 0;
memcpy(buf + off, runStart, sizeof(runStart));
off += sizeof(runStart);
strcpy(buf + off, rbfPath);
off += strlen(rbfPath);
memcpy(buf + off, runEnd, sizeof(runEnd));
off += sizeof(runEnd);
off -= 2;
buf[0] = off & 0xff;
buf[1] = off >> 8;
ioctl(fd, FEED_DATA, buf);
close(fd);
}
#define MAX_BLOCKS 8000
typedef struct {
uint32_t numBlocks;
@ -679,30 +634,81 @@ typedef struct {
uint8_t writtenMask[MAX_BLOCKS / 8 + 1];
} WriteState;
void restartProgram() {
if (!rbfPath[0])
exit(0);
startRbf();
exit(0); // causes parent to eject MSD etc
char elfPath[300];
int lmsPid;
void stopLMS() {
struct dirent *ent;
DIR *dir;
dir = opendir("/proc");
if (dir == NULL)
return;
while ((ent = readdir(dir)) != NULL) {
int pid = atoi(ent->d_name);
if (!pid)
continue;
char namebuf[100];
snprintf(namebuf, 1000, "/proc/%d/cmdline", pid);
FILE *f = fopen(namebuf, "r");
if (f) {
fread(namebuf, 1, 99, f);
if (strcmp(namebuf, "./lms2012") == 0) {
lmsPid = pid;
}
fclose(f);
if (lmsPid)
break;
}
}
closedir(dir);
if (lmsPid) {
DBG("SIGSTOP to lmsPID=%d", lmsPid);
kill(lmsPid, SIGSTOP);
} else {
DBG("LMS not found");
}
}
void waitAndContinue() {
stopLMS();
for (int fd = 3; fd < 9999; ++fd)
close(fd);
pid_t child = fork();
if (child == 0) {
DBG("start %s", elfPath);
execl(elfPath, elfPath, "--msd", (char *)NULL);
exit(128);
}
int status;
waitpid(child, &status, 0);
DBG("re-start LMS");
if (lmsPid) {
kill(lmsPid, SIGCONT);
}
exit(0);
}
void restartProgram() {
if (!elfPath[0])
exit(0);
pid_t child = fork();
if (child == 0)
waitAndContinue();
else
exit(0); // causes parent to eject MSD etc
}
int numWrites = 0;
static WriteState wrState;
void write_block(uint32_t block_no, uint8_t *data) {
WriteState *state = &wrState;
UF2_Block *bl = (void *)data;
if (bl->magicStart0 == 0x20da6d81 && bl->magicStart1 == 0x747e09d4) {
DBG("restart req, #wr=%d", numWrites);
if (numWrites) {
exit(0);
}
return;
}
numWrites++;
if (!is_uf2_block(bl)) {
return;
}
@ -741,10 +747,6 @@ void write_block(uint32_t block_no, uint8_t *data) {
*p = '/';
int fd = open(fn, O_WRONLY | O_CREAT, 0777);
if (fd < 0 && errno == ETXTBSY) {
unlink(fn);
fd = open(fn, O_WRONLY | O_CREAT, 0777);
}
if (fd >= 0) {
ftruncate(fd, bl->fileSize);
lseek(fd, bl->targetAddr, SEEK_SET);
@ -752,8 +754,8 @@ void write_block(uint32_t block_no, uint8_t *data) {
write(fd, bl->data, bl->payloadSize);
close(fd);
if (strlen(fn) > 4 && !strcmp(fn + strlen(fn) - 4, ".rbf")) {
strcpy(rbfPath, fn);
if (strlen(fn) > 4 && !strcmp(fn + strlen(fn) - 4, ".elf")) {
strcpy(elfPath, fn);
}
}
}

View File

@ -20,9 +20,6 @@
#include "uf2.h"
const char *dev_file = "/dev/nbd0";
#define NUM_BLOCKS NUM_FAT_BLOCKS
uint64_t ntohll(uint64_t a) {
@ -32,30 +29,23 @@ uint64_t ntohll(uint64_t a) {
void mylog(const char *fmt, ...) {
va_list args;
char *p, *p2;
char *p;
va_start(args, fmt);
vasprintf(&p, fmt, args);
vprintf(fmt, args);
va_end(args);
if (p[0] != '<')
asprintf(&p2, "<6>%s\n", p);
else
asprintf(&p2, "%s\n", p);
int len = strlen(p2);
int len = strlen(p) + 1;
p[len - 1] = '\n';
#ifdef X86
write(2, p2, len);
write(2, p, len);
#else
int fd = open("/dev/kmsg", O_WRONLY);
write(fd, p2, len);
write(fd, p, len);
close(fd);
#endif
free(p);
free(p2);
}
void readAll(int fd, void *dst, uint32_t length) {
@ -99,6 +89,8 @@ void startclient() {
exit(0);
}
#define dev_file "/dev/nbd0"
void handleread(int off, int len) {
uint8_t buf[512];
LOG("read @%d len=%d", off, len);
@ -197,14 +189,11 @@ void enableMSD(int enabled) {
#endif
}
int main(int argc, char **argv) {
int main() {
#ifndef X86
daemon(0, 1);
#endif
if (argc > 1)
dev_file = argv[1];
for (;;) {
pid_t child = fork();
if (child == 0) {

View File

@ -37,7 +37,7 @@ void mylog(const char *fmt, ...);
#define FAIL(args...) \
do { \
mylog("<4>" args); \
mylog(args); \
exit(1); \
} while (0)

View File

@ -1 +0,0 @@
<meta name="robots" content="noindex">

View File

@ -28,26 +28,20 @@ namespace pxt.editor {
let initPromise: Promise<Ev3Wrapper>
function initAsync() {
const forceHexDownload = /forceHexDownload/i.test(window.location.href);
if (!initPromise && Cloud.isLocalHost() && Cloud.localToken && !forceHexDownload)
if (!initPromise)
initPromise = hf2Async()
.catch(err => {
initPromise = null
noHID = true
return Promise.reject(err)
})
else {
noHID = true
initPromise = Promise.reject(new Error("no HID"))
}
return initPromise
}
export function deployCoreAsync(resp: pxtc.CompileResult, isCli = false) {
let w: Ev3Wrapper
let filename = resp.downloadFileBaseName || "pxt"
filename = filename.replace(/^lego-/, "")
let filename = resp.downloadFileBaseName
let fspath = "../prjs/BrkProg_SAVE/"
@ -121,9 +115,13 @@ namespace pxt.editor {
const res: pxt.editor.ExtensionResult = {
deployCoreAsync,
};
initAsync().catch(e => {
// probably no HID - we'll try this again upon deployment
})
initAsync()
/*
.then(w => w.streamFileAsync("/tmp/serial.txt", buf => {
let str = Util.fromUTF8(Util.uint8ArrayToString(buf))
}))
*/
return Promise.resolve<pxt.editor.ExtensionResult>(res);
}
}

View File

@ -1,37 +1,4 @@
{
"Array": "Add, remove, and replace items in lists.\n\nAdd, remove, and replace items in lists.",
"Array.filter": "Returns the elements of an array that meet the condition specified in a callback function.",
"Array.filter|param|callbackfn": "A function that accepts up to two arguments. The filter method calls the callbackfn function one time for each element in the array.",
"Array.get": "Gets the value at a particular index",
"Array.get|param|index": "the zero-based position in the list of the item, eg: 0",
"Array.indexOf": "Returns the index of the first occurrence of a value in an array.",
"Array.indexOf|param|fromIndex": "The array index at which to begin the search. If fromIndex is omitted, the search starts at index 0.",
"Array.indexOf|param|item": "The value to locate in the array.",
"Array.insertAt": "Insert the value at a particular index, increases length by 1",
"Array.insertAt|param|index": "the zero-based position in the list to insert the value, eg: 0",
"Array.length": "Gets or sets the length of the array. This is a number one higher than the highest element defined in an array.",
"Array.map": "Calls a defined callback function on each element of an array, and returns an array that contains the results.",
"Array.map|param|callbackfn": "A function that accepts up to two arguments. The map method calls the callbackfn function one time for each element in the array.",
"Array.pop": "Removes the last element from an array and returns it.",
"Array.push": "Appends new elements to an array.",
"Array.reduce": "Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.",
"Array.reduce|param|callbackfn": "A function that accepts up to three arguments. The reduce method calls the callbackfn function one time for each element in the array.",
"Array.reduce|param|initialValue": "Initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.",
"Array.removeAt": "Removes the object at position index.",
"Array.removeElement": "Removes the first occurence of an object. Returns true if removed.",
"Array.reverse": "Reverses the elements in an Array. The first array element becomes the last, and the last array element becomes the first.",
"Array.set": "Stores the value at a particular index",
"Array.set|param|index": "the zero-based position in the list to store the value, eg: 0",
"Array.shift": "Removes the first element from an array and returns that element. This method changes the length of the array.",
"Array.slice": "Returns a section of an array.",
"Array.slice|param|end": "The end of the specified portion of the array. eg: 0",
"Array.slice|param|start": "The beginning of the specified portion of the array. eg: 0",
"Array.sort": "Sorts the elements of an array in place and returns the array. The sort is not necessarily stable.",
"Array.splice": "Removes elements from an array.",
"Array.splice|param|deleteCount": "The number of elements to remove. eg: 0",
"Array.splice|param|start": "The zero-based location in the array from which to start removing elements. eg: 0",
"Array.unshift": "Adds one element to the beginning of an array and returns the new length of the array.",
"Math": "More complex operations with numbers.",
"Math.abs": "Returns the absolute value of a number (the value without regard to whether it is positive or negative).\nFor example, the absolute value of -5 is the same as the absolute value of 5.",
"Math.abs|param|x": "A numeric expression for which the absolute value is needed.",
"Math.acos": "Returns the arccosine (in radians) of a number",
@ -91,7 +58,6 @@
"Math.tan|param|x": "An angle in radians",
"Math.trunc": "Returns the number with the decimal part truncated.",
"Math.trunc|param|x": "A numeric expression.",
"String": "Combine, split, and search text strings.\n\nCombine, split, and search text strings.",
"String.charAt": "Returns the character at the specified index.",
"String.charAt|param|index": "The zero-based index of the desired character.",
"String.charCodeAt": "Returns the Unicode value of the character at the specified location.",

View File

@ -1,14 +1,4 @@
{
"Array.indexOf|block": "%list| find index of %value",
"Array.insertAt|block": "%list| insert at %index| value %value",
"Array.length|block": "length of %VALUE",
"Array.pop|block": "get and remove last value from %list",
"Array.push|block": "%list| add value %value| to end",
"Array.removeAt|block": "%list| remove value at %index",
"Array.reverse|block": "reverse %list",
"Array.shift|block": "get and remove first value from %list",
"Array.unshift|block": "%list| insert %value| at beginning",
"Array|block": "Array",
"Math.constrain|block": "constrain %value|between %low|and %high",
"Math.map|block": "map %value|from low %fromLow|from high %fromHigh|to low %toLow|to high %toHigh",
"Math.randomRange|block": "pick random %min|to %limit",
@ -37,8 +27,6 @@
"serial.writeString|block": "serial|write string %text",
"serial.writeValue|block": "serial|write value %name|= %value",
"serial|block": "serial",
"{id:category}Array": "Array",
"{id:category}Arrays": "Arrays",
"{id:category}Control": "Control",
"{id:category}Loops": "Loops",
"{id:category}Math": "Math",

View File

@ -30,5 +30,7 @@
Object = 4,
Function = 5,
}
declare namespace serial {
}
// Auto-generated. Do not edit. Really.

158
libs/base/shims.d.ts vendored
View File

@ -1,158 +0,0 @@
// Auto-generated. Do not edit.
//% indexerGet=BufferMethods::getByte indexerSet=BufferMethods::setByte
declare interface Buffer {
/**
* Write a number in specified format in the buffer.
*/
//% shim=BufferMethods::setNumber
setNumber(format: NumberFormat, offset: int32, value: number): void;
/**
* Read a number in specified format from the buffer.
*/
//% shim=BufferMethods::getNumber
getNumber(format: NumberFormat, offset: int32): number;
/** Returns the length of a Buffer object. */
//% property shim=BufferMethods::length
length: int32;
/**
* Fill (a fragment) of the buffer with given value.
*/
//% offset.defl=0 length.defl=-1 shim=BufferMethods::fill
fill(value: int32, offset?: int32, length?: int32): void;
/**
* Return a copy of a fragment of a buffer.
*/
//% offset.defl=0 length.defl=-1 shim=BufferMethods::slice
slice(offset?: int32, length?: int32): Buffer;
/**
* Shift buffer left in place, with zero padding.
* @param offset number of bytes to shift; use negative value to shift right
* @param start start offset in buffer. Default is 0.
* @param length number of elements in buffer. If negative, length is set as the buffer length minus
* start. eg: -1
*/
//% start.defl=0 length.defl=-1 shim=BufferMethods::shift
shift(offset: int32, start?: int32, length?: int32): void;
/**
* Convert a buffer to its hexadecimal representation.
*/
//% shim=BufferMethods::toHex
toHex(): string;
/**
* Rotate buffer left in place.
* @param offset number of bytes to shift; use negative value to shift right
* @param start start offset in buffer. Default is 0.
* @param length number of elements in buffer. If negative, length is set as the buffer length minus
* start. eg: -1
*/
//% start.defl=0 length.defl=-1 shim=BufferMethods::rotate
rotate(offset: int32, start?: int32, length?: int32): void;
/**
* Write contents of `src` at `dstOffset` in current buffer.
*/
//% shim=BufferMethods::write
write(dstOffset: int32, src: Buffer): void;
}
declare namespace loops {
/**
* Repeats the code forever in the background. On each iteration, allows other codes to run.
* @param body code to execute
*/
//% help=loops/forever weight=100 blockGap=8
//% blockId=forever block="forever" blockAllowMultiple=1 shim=loops::forever
function forever(a: () => void): void;
/**
* Pause for the specified time in milliseconds
* @param ms how long to pause for, eg: 100, 200, 500, 1000, 2000
*/
//% help=loops/pause weight=99
//% async block="pause (ms) %pause"
//% blockId=device_pause shim=loops::pause
function pause(ms: int32): void;
}
declare namespace control {
/**
* Gets the number of milliseconds elapsed since power on.
*/
//% help=control/millis weight=50
//% blockId=control_running_time block="millis (ms)" shim=control::millis
function millis(): int32;
/**
* Run code when a registered event happens.
* @param id the event compoent id
* @param value the event value to match
*/
//% weight=20 blockGap=8 blockId="control_on_event" block="on event|from %src|with value %value"
//% blockExternalInputs=1
//% help="control/on-event" shim=control::onEvent
function onEvent(src: int32, value: int32, handler: () => void): void;
/**
* Reset the device.
*/
//% weight=30 async help=control/reset blockGap=8
//% blockId="control_reset" block="reset" shim=control::reset
function reset(): void;
/**
* Block the current fiber for the given microseconds
* @param micros number of micro-seconds to wait. eg: 4
*/
//% help=control/wait-micros weight=29 async
//% blockId="control_wait_us" block="wait (µs)%micros" shim=control::waitMicros
function waitMicros(micros: int32): void;
/**
* Run other code in the background.
*/
//% help=control/run-in-background blockAllowMultiple=1
//% blockId="control_run_in_background" block="run in background" blockGap=8 shim=control::runInBackground
function runInBackground(a: () => void): void;
/**
* Blocks the calling thread until the specified event is raised.
*/
//% help=control/wait-for-event async
//% blockId=control_wait_for_event block="wait for event|from %src|with value %value" shim=control::waitForEvent
function waitForEvent(src: int32, value: int32): void;
/**
* Derive a unique, consistent serial number of this device from internal data.
*/
//% blockId="control_device_serial_number" block="device serial number" weight=9 shim=control::deviceSerialNumber
function deviceSerialNumber(): int32;
}
declare namespace serial {
/**
* Write some text to the serial port.
*/
//% help=serial/write-string
//% weight=87
//% blockId=serial_writestring block="serial|write string %text" shim=serial::writeString
function writeString(text: string): void;
/**
* Send a buffer across the serial connection.
*/
//% help=serial/write-buffer advanced=true weight=6
//% blockId=serial_writebuffer block="serial|write buffer %buffer" shim=serial::writeBuffer
function writeBuffer(buffer: Buffer): void;
}
// Auto-generated. Do not edit. Really.

View File

@ -20,7 +20,6 @@
"input.remoteTopRight": "Remote top-right button.",
"output.createBuffer": "Create a new zero-initialized buffer.",
"output.createBuffer|param|size": "number of bytes in the buffer",
"output.getPattern": "Pattern block.",
"output.setLights": "Set lights.",
"screen.clear": "Clear screen and reset font to normal.",
"screen.doubleIcon": "Double size of an icon.",

View File

@ -16,8 +16,7 @@
"input.remoteTopLeft|block": "remote top-left",
"input.remoteTopRight|block": "remote top-right",
"input|block": "input",
"output.getPattern|block": "%pattern",
"output.setLights|block": "set lights %pattern=led_pattern",
"output.setLights|block": "set lights %pattern",
"output|block": "output",
"screen|block": "screen",
"serial|block": "serial",

View File

@ -216,8 +216,8 @@ namespace output {
/**
* Set lights.
*/
//% blockId=setLights block="set lights %pattern=led_pattern"
export function setLights(pattern: number): void {
//% blockId=setLights block="set lights %pattern"
export function setLights(pattern: LightsPattern): void {
if (currPattern === pattern)
return
currPattern = pattern
@ -225,15 +225,4 @@ namespace output {
cmd[0] = pattern + 48
input.internal.getBtnsMM().write(cmd)
}
/**
* Pattern block.
*/
//% blockId=led_pattern block="%pattern"
//% shim=TD_ID colorSecondary="#6e9a36"
//% blockHidden=true
export function getPattern(pattern: LightsPattern): number {
return pattern;
}
}

View File

@ -1,6 +1,6 @@
{
"name": "pxt-ev3",
"version": "0.0.10",
"version": "0.0.0",
"description": "LEGO Mindstorms EV3 for Microsoft MakeCode",
"private": true,
"keywords": [
@ -39,8 +39,8 @@
"semantic-ui-less": "^2.2.4"
},
"dependencies": {
"pxt-common-packages": "0.9.2",
"pxt-core": "2.0.6"
"pxt-common-packages": "0.8.1",
"pxt-core": "1.7.4"
},
"scripts": {
"test": "node node_modules/pxt-core/built/pxt.js travis"

View File

@ -73,6 +73,10 @@ div.blocklyTreeRow {
box-shadow: inset 0px 0px 0px 3px rgba(0,0,0,0.1);
}
div.blocklyTreeRoot div div div div div.blocklyTreeRow {
padding-top: 0.5rem !important;
}
/* Remove shadow around blockly blocks */
.blocklyPathDark, .blocklyPathLight {
display: none;
@ -94,12 +98,11 @@ span.blocklyTreeLabel {
}
/* Editor menu toggle */
#menubar .ui.menu.fixed .item.editor-menuitem .ui.grid {
#menubar .ui.menu .item.editor-menuitem {
background: @blue !important;
}
#menubar .ui.menu.fixed .ui.item.editor-menuitem .item:not(.active) {
color: white;
#menubar .ui.menu .item.editor-menuitem .item {
color: white !important;
}
/* Search box */
@ -121,6 +124,11 @@ span.blocklyTreeLabel {
#filelist {
background: transparent;
}
div.blocklyTreeRow {
padding-bottom: 2rem !important;
min-height: @blocklyRowHeightMobile;
line-height: @blocklyRowHeightMobile/2;
}
#blocklyTrashIcon {
margin: 0.2rem;
}
@ -128,14 +136,29 @@ span.blocklyTreeLabel {
/* Tablet */
@media only screen and (min-width: @tabletBreakpoint) and (max-width: @largestTabletScreen) {
div.blocklyTreeRow {
padding-left: 0.5rem !important;
min-height: @blocklyRowHeightTablet;
line-height: @blocklyRowHeightTablet/2;
}
}
/* Small Monitor */
@media only screen and (min-width: @computerBreakpoint) and (max-width: @largestSmallMonitor) {
div.blocklyTreeRow {
padding-left: 0.5rem !important;
min-height: @blocklyRowHeightComputer;
line-height: @blocklyRowHeightComputer/2;
}
}
/* Large Monitor */
@media only screen and (min-width: @largeMonitorBreakpoint) {
div.blocklyTreeRow {
padding-left: 0.5rem !important;
min-height: @blocklyRowHeightWide;
line-height: @blocklyRowHeightWide/2;
}
}
/* Mobile, Tablet AND thin screen */
@media only screen and (max-width: @largestTabletScreen) and (max-height: @thinEditorBreakpoint) {