Beginner: OpenWrt 18.06 stable version

I think we need a stable version based on 18.06 for beginner 🙂
So this week I am going to work on a firmware. And I will also write how I do this to help beginner of openwrt.

Targets:

1. port mt7628 driver for 18.06.
2. port sound driver for 18.06.
3. fix possible SD card driver broken.
4. fix LuCI WPA2 encryption bug.
5. make it able to download/upgrade package by LuCI directly.

Once finish all of above, I hope it is easy for beginners to use. 🙂

PS: if you are a beginner and have questions, please email to support@vocore.io, we are glad to hear from you.

Tutorial: VoCore2+Screen Port Super Marie Bros.

The screen is ready and works pretty well, I am having a lot of fun 🙂

I am trying to port two version of NES simulators, one is LiteNES, another one is LaiNES. LiteNES is working pretty smooth but it has many bugs, some codes are broken; LaiNES is good and less bugs, but it is very very very slow, only 1/6 speed of LiteNES. I do not have much time to fix the bugs or optimize code. I guess it is a pretty funny process to fix it.

Here I explain how I port the two NES. Please download latest SDK of the screen(vocore.io/screen.html), in demo folder, we have a firmware with frame buffer driver, which is much easier for you port applications, or you can use the fbusb.ko driver to make your own.

fbusb.ko depends on framebuffer driver, usb driver and some sys_* draw function of framebuffer, please make sure every function is implied in kernel to make sure it works normal.

LiteNES requires to port keyboard and pixel display function.

To use framebuffer, here is my sample code.

void nes_hal_init()
{
	...

	// map framebuffer to local, so we can access it directly.
	int fd = open("/dev/fb0", O_RDWR);
	if (fd <= 0) {
		printf("frame buffer is not exist, exit.\n");
		exit(-1);
	}

	framebuffer = mmap(NULL, FBMAP_SIZE, PROT_WRITE, MAP_SHARED, fd, 0);
	if (framebuffer == NULL) {
		printf("can not map to frame buffer, exit.\n");
		exit(-1);
	}

	...

}

...

void nes_flip_display()
{
	if (framecount++ % 2)
		return;

	// directly write to middle of the frame buffer.
	memcpy(framebuffer + 144 * 480, local, LOCAL_SIZE);
}

I use a thread to handle the keyboard input…Maybe async is another better way, but its code might complex, so I make it easy.

Thread will update a key table, so app can directly read from the key table to get keys’ states.

void* keyboard_thread(void *arg)
{
	struct input_event ie;
	int kf;

	kf = open("/dev/input/event0", O_RDONLY);
	if (kf < 0) {
		printf("keyboard is not exist, input ignore.\n");
		return NULL;
	}

	while (1) {
		int size;
		size = read(kf, &ie, sizeof(struct input_event));
		if (size != sizeof(struct input_event))
			break;

		if (ie.type != EV_KEY || ie.code >= MAX_KEYCODE)
			continue;

		key_table[ie.code] = ie.value;
		if (ie.code == KEY_ESC)
			break;
	};

	close(kf);
	return arg;
}

PS: LiteNES is great but just bugs are weird, it has memory leak, no idea where it is but it write to the framebuffer area do not belong to it, should be the ppu engine issue.

PSS: source code here http://vonger.cn/misc/screen/LaiNES.tar.xz, http://vonger.cn/misc/screen/LiteNES.tar.xz

VoCore2: Port Qt Success

Smooth Qt UI, at 30fps, WVGA.
I think it should be a good replace for UI devices who requires wifi or ethernet.
10K units for this solution price should be less than 15USD(screen + VoCore2)

Now I am porting a weather app on Qt to it, it should be good.

VoCore2: Port Qt

Now we have screen 🙂 So I’d like to port Qt. Thanks to pawelkn, https://github.com/pawelkn/qt5-openwrt-package, this package works well, just need to add one line. I search google, this is because MIPS do not have 64bit atomic operation, so we need to use the library to simulate it.

@@ -171,6 +171,7 @@
 	$(CP) $(PKG_BUILD_DIR)/qtbase/lib/libQt5Core.so* $(1)/usr/lib/
 	$(CP) $(PKG_BUILD_DIR)/qtbase/lib/libQt5Core.prl $(1)/usr/lib/
 	$(CP) $(PKG_BUILD_DIR)/qtbase/lib/libQt5Core.la $(1)/usr/lib/
+	$(CP) $(TOOLCHAIN_DIR)/lib/libatomic.so* $(1)/usr/lib/
 endef
 
 define Package/qt5-concurrent/install

Put this Makefile and other files to openwrt/package, then make menuconfig, in Library -> Qt5, select qt5-core, make V=s

Compile is smooth and take a long time…After upgrade, also make the example file and copy to VoCore2.

Like usual, it is not work at first time, the screen part only. 🙂

This is necessary: export QT_QPA_PLATFORM=linuxfb:fb=/dev/fb0
We must use this command setup framebuffer device.

I do not know why it not work, possible reasons:

1. Qt support DRM but not framebuffer anymore, need to check linuxfb plugin for Qt5.
2. My framebuffer driver has bug, that block the demo runs on the screen.
3. VoCore2 do not fast enough for Qt(this is not likely, because I can find it actually works, but just stopped at somewhere, looks like it is wait for some device ready)
4. Other necessary device such as keyboard, mouse block the process.

From my first test, I guess Qt application bootup on VoCore2 is pretty fast. It takes around three seconds to start, just eat a lot of memory, demo consume around 22MB memory, maybe it can not work on vocore2 lite which free memory only around 30MB.

Also I port Qt5.11.3, here is the link: http://vonger.cn/misc/vocore2/qt5.11.3-openwrt.zip

VoCore2: USB Device Develop 4

The driver develop is done.

Try this firmware(http://vonger.cn/misc/vocore2/20181218.screen.bin) and video tutorial(https://www.youtube.com/watch?v=VueZU-78Jjg)

Upgrade your VoCore2 to 20181218.screen.bin, connect the screen then reboot, it will work.

Later I will release a blob for the driver so you can embed it into your project/firmware easily.

VoCore2: USB Device Develop 3

This blog do not have any tech included. Just want to write down something to boost myself, I need to spend more time on it because the screen is still need a lot polishing.

First of all, I need to imply all drivers to kernel level. My demo just show the result, actually I did a lot of prepare before that, it is not plugin and play yet.

And Christmas is near, I do not have much out-source work for now, so I can focus on this screen. I will try my best to finish it before that holiday.

Next step, I am going to port FC games simulator or dos games simulator(except DOOM, not a challenge for me anymore 🙂 after this and with exists sound support, VoCore will like a real small computer.

Maybe before step two, another good choice is to port X-window system to VoCore2(with a SD card for more storage). As I know a lot of desktop linux is able to run in 128MB memory(even 64MB, very old computer). If I can do optimize on it, it should be no problem to run on VoCore2.

This is also a tech prepare for VoCore3.

PS: VoCore3 still have a long way to go. I need its solution price lower enough, power consume lower enough, also tons of interfaces. VoCore2 is the best choice in the market as I know and for further two year it should be. Anyway, VoCore2 will keep production, nearly ten years should no such worry 🙂

PSS: I know USB2.0 screen is kind of out-of-date, if we do screen in USB3.0, it should be pretty easy. I spend almost one year and a lot of money to finish it has three reasons:

1. USB 3.0 power consume is super high. Of course, you can not let 10GHz bus eat only a little power.
2. USB 2.0 is common and compatible with USB3.0, so for small screen, it is good enough in most situation.
3. USB 3.0 supported chip is very expansive for now, everybody like cheap but fantastic production, so any unnecessary cost is a waste.
3.1 VoCore and RaspberryPi do not support USB3.0.
no reason four. 🙂

VoCore2: USB Device Develop 2

Actually the bug is easy to fix. URB can not use buffer alloced in stack, that cause the driver broken…

So fix that driver is super simple, remove that line: u8 buf[6] = {…};, and replace with u8 *buf; buf = kzalloc(8, GPF_KERNEL); everything will work.

After this simple fix, now I have make fbcon works on VoCore2 and its small display.
Here is the picture:

This driver will map /dev/fb0 and we can easily control it as framebuffer, fbcon need to select from kernel, then it will automatically bind to /dev/tty0. We can use command “/bin/ash –login > /dev/tty0 2>&1” to redirect serial port input/output to the display.

After that, we can get output from the little display.

Next blog, I will try to make it easy to use and public every steps of how to use it.
Later I will public most of its source code to help you easy hack 🙂

attach youtube video https://youtu.be/w98_JvtTgqo, smooth like baby skin. 😉

PS: another best thing is CPU usage is 0%

VoCore2: USB Device Develop

Such a simple code but get mistake…

My source code is as simple as usual

...in fbusb_probe for a new usb device...

		u8 buf[6] = {0x00, 0x2c, 0x00, 0xb8, 0x0b, 0x00};
		usb_control_msg(par->udev, usb_sndctrlpipe(par->udev, 0), 0xb0,
			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
			0, 0, buf, 6, 100);
...

every parameter should be normal, but once the code runs at this position, it crashed…

[   71.998261] ------------[ cut here ]------------
[   72.002980] WARNING: CPU: 0 PID: 347 at drivers/usb/core/hcd.c:1598 usb_hcd_map_urb_for_dma+0x3f4/0x524
[   72.012519] transfer buffer is on stack
[   72.016416] Modules linked in: pppoe ppp_async pppox ppp_generic nf_conntrack_ipv6 lzo iptable_nat ipt_REJECT ipt_MASQUERADE xt_time xt_tcpudp xt_state xt_nat xt_multiport xt_mark xt_mac xt_limit xt_conntrack xt_comment xt_TCPMSS xt_REDIRECT xt_LOG xt_FLOWOFFLOAD slhc nf_reject_ipv4 nf_nat_redirect nf_nat_masquerade_ipv4 nf_conntrack_ipv4 nf_nat_ipv4 nf_nat nf_log_ipv4 nf_flow_table_hw nf_flow_table nf_defrag_ipv6 nf_defrag_ipv4 nf_conntrack_rtcache nf_conntrack lzo_decompress lzo_compress iptable_mangle iptable_filter ip_tables crc_ccitt mt7628 i2c_mt7621 snd_soc_core ralink_gdma virt_dma i2c_dev ip6t_REJECT nf_reject_ipv6 nf_log_ipv6 nf_log_common ip6table_mangle ip6table_filter ip6_tables x_tables snd_compress snd_pcm_oss snd_mixer_oss snd_pcm snd_timer snd_rawmidi snd_seq_device snd_hwdep snd
[   72.088165]  input_core soundcore vfat fat nls_utf8 nls_iso8859_1 nls_cp437 regmap_spi regmap_i2c i2c_core crypto_acompress mmc_block usb_storage mtk_sd mmc_core leds_gpio ohci_platform ohci_hcd ehci_platform sd_mod scsi_mod ehci_hcd gpio_button_hotplug
[   72.111025] CPU: 0 PID: 347 Comm: kworker/0:3 Not tainted 4.14.50 #0
[   72.117496] Workqueue: usb_hub_wq hub_event
[   72.121735] Stack : 86846000 804635c8 86846610 8004e674 87e2b454 80447aa7 803f1944 0000015b
[   72.130235]         803f18b0 87f5187c 86846000 8004f350 86846610 87fd3915 87f51858 24c668a7
[   72.138735]         00000000 00000000 00000000 000000c2 00000000 65756575 7375203a 75685f62
[   72.147234]         00000001 00000000 00000040 63686520 00000000 00000000 802279e8 80412a8c
[   72.155732]         00000009 0000063e 86846000 804635c8 00000002 801db240 00000000 804a0000
[   72.164288]         ...
[   72.166788] Call Trace:
[   72.169290] [<8000e424>] show_stack+0x74/0x104
[   72.173818] [<800245a0>] __warn+0x110/0x118
[   72.178088] [<800245d8>] warn_slowpath_fmt+0x30/0x3c
[   72.183145] [<802279e8>] usb_hcd_map_urb_for_dma+0x3f4/0x524
[   72.188899] [<80228d30>] usb_hcd_submit_urb+0x79c/0x7d0
[   72.194203] [<8022a2c0>] usb_start_wait_urb+0x5c/0xec
[   72.199338] [<8022a400>] usb_control_msg+0xb0/0x114
[   72.204296] [<801c4cfc>] fbusb_probe+0x260/0x2bc
[   72.208995] [<8022dd48>] usb_probe_interface+0x1cc/0x1f4
[   72.214399] [<801e9824>] driver_probe_device+0x1fc/0x2c4
[   72.219799] [<801e7c84>] bus_for_each_drv+0x9c/0xac
[   72.224749] [<801e95d4>] __device_attach+0xac/0xe4
[   72.229621] [<801e8a84>] bus_probe_device+0x3c/0xb0
[   72.234570] [<801e6d68>] device_add+0x444/0x550
[   72.239178] [<8022cb38>] usb_set_configuration+0x5e8/0x634
[   72.244756] [<802359a0>] generic_probe+0x6c/0x9c
[   72.249452] [<801e9824>] driver_probe_device+0x1fc/0x2c4
[   72.254841] [<801e7c84>] bus_for_each_drv+0x9c/0xac
[   72.259800] [<801e95d4>] __device_attach+0xac/0xe4
[   72.264661] [<801e8a84>] bus_probe_device+0x3c/0xb0
[   72.269619] [<801e6d68>] device_add+0x444/0x550
[   72.274221] [<802246ec>] usb_new_device+0x218/0x344
[   72.279182] [<802256bc>] hub_event+0xc48/0xe84
[   72.283705] [<800371a4>] process_one_work+0x1e8/0x318
[   72.288843] [<80037fc8>] worker_thread+0x2f4/0x424
[   72.293708] [<8003c940>] kthread+0x130/0x138
[   72.298054] [<800092d8>] ret_from_kernel_thread+0x14/0x1c
[   72.303531] ---[ end trace e5f3e34a9f1d79f7 ]---
[   72.308231] ------------[ cut here ]------------
[   72.312928] WARNING: CPU: 0 PID: 347 at drivers/usb/core/hcd.c:1595 usb_hcd_map_urb_for_dma+0x3f4/0x524
[   72.322455] transfer buffer not dma capable
[   72.326696] Modules linked in: pppoe ppp_async pppox ppp_generic nf_conntrack_ipv6 lzo iptable_nat ipt_REJECT ipt_MASQUERADE xt_time xt_tcpudp xt_state xt_nat xt_multiport xt_mark xt_mac xt_limit xt_conntrack xt_comment xt_TCPMSS xt_REDIRECT xt_LOG xt_FLOWOFFLOAD slhc nf_reject_ipv4 nf_nat_redirect nf_nat_masquerade_ipv4 nf_conntrack_ipv4 nf_nat_ipv4 nf_nat nf_log_ipv4 nf_flow_table_hw nf_flow_table nf_defrag_ipv6 nf_defrag_ipv4 nf_conntrack_rtcache nf_conntrack lzo_decompress lzo_compress iptable_mangle iptable_filter ip_tables crc_ccitt mt7628 i2c_mt7621 snd_soc_core ralink_gdma virt_dma i2c_dev ip6t_REJECT nf_reject_ipv6 nf_log_ipv6 nf_log_common ip6table_mangle ip6table_filter ip6_tables x_tables snd_compress snd_pcm_oss snd_mixer_oss snd_pcm snd_timer snd_rawmidi snd_seq_device snd_hwdep snd
[   72.398434]  input_core soundcore vfat fat nls_utf8 nls_iso8859_1 nls_cp437 regmap_spi regmap_i2c i2c_core crypto_acompress mmc_block usb_storage mtk_sd mmc_core leds_gpio ohci_platform ohci_hcd ehci_platform sd_mod scsi_mod ehci_hcd gpio_button_hotplug
[   72.421261] CPU: 0 PID: 347 Comm: kworker/0:3 Tainted: G        W       4.14.50 #0
[   72.428952] Workqueue: usb_hub_wq hub_event
[   72.433200] Stack : 86846000 804635c8 86846610 8004e674 87e2b454 80447aa7 803f1944 0000015b
[   72.441699]         803f18b0 87f518b4 86846000 8004f350 86846610 87fd3915 87f51890 24c668a7
[   72.450196]         00000000 00000000 00000000 000000ec 00000000 65756575 7375203a 75685f62
[   72.458691]         00000001 00000000 f8000000 63686520 00000000 00000000 802279e8 80412a8c
[   72.467187]         00000009 0000063b 86846000 804635c8 00000002 801db240 00000000 804a0000
[   72.475671]         ...
[   72.478159] Call Trace:
[   72.480657] [<8000e424>] show_stack+0x74/0x104
[   72.485183] [<800245a0>] __warn+0x110/0x118
[   72.489441] [<800245d8>] warn_slowpath_fmt+0x30/0x3c
[   72.494490] [<802279e8>] usb_hcd_map_urb_for_dma+0x3f4/0x524
[   72.500243] [<80228d30>] usb_hcd_submit_urb+0x79c/0x7d0
[   72.505546] [<8022a2c0>] usb_start_wait_urb+0x5c/0xec
[   72.510688] [<801c4d24>] fbusb_probe+0x288/0x2bc
[   72.515378] [<8022dd48>] usb_probe_interface+0x1cc/0x1f4
[   72.520788] [<801e9824>] driver_probe_device+0x1fc/0x2c4
[   72.526186] [<801e7c84>] bus_for_each_drv+0x9c/0xac
[   72.531135] [<801e95d4>] __device_attach+0xac/0xe4
[   72.536007] [<801e8a84>] bus_probe_device+0x3c/0xb0
[   72.540957] [<801e6d68>] device_add+0x444/0x550
[   72.545554] [<8022cb38>] usb_set_configuration+0x5e8/0x634
[   72.551139] [<802359a0>] generic_probe+0x6c/0x9c
[   72.555838] [<801e9824>] driver_probe_device+0x1fc/0x2c4
[   72.561227] [<801e7c84>] bus_for_each_drv+0x9c/0xac
[   72.566186] [<801e95d4>] __device_attach+0xac/0xe4
[   72.571058] [<801e8a84>] bus_probe_device+0x3c/0xb0
[   72.576014] [<801e6d68>] device_add+0x444/0x550
[   72.580615] [<802246ec>] usb_new_device+0x218/0x344
[   72.585564] [<802256bc>] hub_event+0xc48/0xe84
[   72.590103] [<800371a4>] process_one_work+0x1e8/0x318
[   72.595233] [<80037fc8>] worker_thread+0x2f4/0x424
[   72.600108] [<8003c940>] kthread+0x130/0x138
[   72.604445] [<800092d8>] ret_from_kernel_thread+0x14/0x1c
[   72.609929] ---[ end trace e5f3e34a9f1d79f8 ]---

No idea what is wrong…have to read more code and compare. At least usb-storage is working normal, if that works, it means this usb driver should be no big issue.

fbusb is just a USB device that to map a virtual frame buffer, I thought it is easy to be done 🙂

Weird Issue when Develop Frame buffer Driver

I meet a ghost bug, this almost kills me…
The source code is as simple as the following:

struct fbusb_par {
	struct usb_interface *interface;
	u32 palette[PSEUDO_PALETTE_SIZE];
};

static int fbusb_probe(struct usb_interface *interface,
		       const struct usb_device_id *id)
{
	struct fb_info *info;
	info = framebuffer_alloc(sizeof(struct fbusb_par), &interface->dev);
        if (!info)
               return -ENOMEM;

	par = info->par;
	par->interface = interface;

	return 0;
}

static void fbusb_disconnect(struct usb_interface *interface)
{
	dev_info(&interface->dev,
		 "framebuffer device now disconnected.\n");
}

static const struct usb_device_id fbusb_ids[] = {
	{ USB_DEVICE(0x04b4, 0x1004) },
	{ }
};
MODULE_DEVICE_TABLE(usb, fbusb_ids);

static struct usb_driver fbusb_driver = {
	.name = "fbusb",
	.probe = fbusb_probe,
	.disconnect = fbusb_disconnect,
	.id_table = fbusb_ids,
};

module_usb_driver(fbusb_driver);

The mysterious bug is this driver crashed at par->interface = interface; and after debug, par is a null pointer, but info is not a null pointer! Let’s check framebuffer_alloc source code, if return info is correct, there is no way par to be null pointer.

struct fb_info *framebuffer_alloc(size_t size, struct device *dev)
{
#define BYTES_PER_LONG (BITS_PER_LONG/8)
#define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))
	int fb_info_size = sizeof(struct fb_info);
	struct fb_info *info;
	char *p;

	if (size)
		fb_info_size += PADDING;

	p = kzalloc(fb_info_size + size, GFP_KERNEL);

	if (!p)
		return NULL;

	info = (struct fb_info *) p;

	if (size)
		info->par = p + fb_info_size;

	info->device = dev;

#ifdef CONFIG_FB_BACKLIGHT
	mutex_init(&info->bl_curve_mutex);
#endif

	return info;
#undef PADDING
#undef BYTES_PER_LONG
}
EXPORT_SYMBOL(framebuffer_alloc);

Also step into this framebuffer_alloc function, in this function info->par is not null and its value is correct. So what is happening once info returned? who changed info->par to null? This is really weird bug…I have never seen such thing before.

PS: I guess I find the problem. If I put the source into Linux source code, it will report some .ko is missing, but if I move it to package/kernel as a package, it will happen that weird problem. So it is package missing problem.

VoCore2 Screen: new firmware

After many weeks struggle, finally I finish the new firmware for the screen, this new firmware mainly improve the boot up speed of the screen, old one takes six seconds, new ones less than one. Also add control function to modify backlight strength and support 8bit/16bit/24bit pixel format.

Based on this firmware, now we can have:

1. Linux text console which is based on frame buffer 16bit or 32bit, old firmware only support 24bit.

2. super smooth games from GBA, NES, GEONEO. We support 8bit mode, now we have more CPU for game logic but not for screen data transfer.

3. smooth games from DOS. DOS games such as DOOM will be faster, such game 12fps is playable, new firmware will make it at least 15fps.

4. control rotate the screen, backlight.

The main optimize is for VoCore2 or other embed device with USB port but no display port such as HDMI. This solution will greatly save cost. For example, a chip with display port normally is over 5USD, it is not included DDR and flash yet, full solution with wifi, ethernet should be around 35USD, a screen with HDMI is around 25USD. So mass production is 60USD/pcs 10K unit.

For VoCore2 solution, only 33.99 + 17.99 = 52USD, this is just sample price! 🙂 mass production(1K unit) normally have 40% discount, totally cost should be around 30USD, only half!

What you will get for 30USD? 580MHz Linux based computer + 5 x 100Mbps ethernet + 150Mbps wifi + smooth UI display. It is fit for the control system, education toy solution for kids, also a good replace STM32 or RaspberryPi.

Linux console driver will be released in next week, need more hard work, I am enjoying it 🙂