VoCore2: Port mt7628.ko to LuCI 3

Get some progress 🙂

PS: This uci system is so hard to understand, it has executables, libraries and also scripts without any documentation…I guess from beginning its designer never think about portable…

The real call wifi up/down is this from /sbin/wifi: ubus_wifi_cmd “$cmd” “$2”

_wifi_updown() {                                
        for device in ${2:-$DEVICES}; do (       
                config_get disabled "$device" disabled
                [ "$disabled" = "1" ] && {       
                        echo "'$device' is disabled"
                        set disable                   
                }                                     
                config_get iftype "$device" type    
                if eval "type ${1}_$iftype" 2>/dev/null >/dev/null; then
                        eval "scan_$iftype '$device'"                   
                        eval "${1}_$iftype '$device'" || echo "$device($iftype): ${1} failed"
                elif [ ! -f /lib/netifd/wireless/$iftype.sh ]; then                          
                        echo "$device($iftype): Interface type not supported"                
                fi                                                           
        ); done                                                              
}                                                                                            
                                                                                             
wifi_updown() {                                                              
        cmd=down                                                             
        [ enable = "$1" ] && {                                          
                _wifi_updown disable "$2"                                                    
                ubus_wifi_cmd "$cmd" "$2"                                                    
                scan_wifi                                                                    
                cmd=up                                                                       
        }                                                                                    
        ubus_wifi_cmd "$cmd" "$2"                                            
        _wifi_updown "$@"                                                    
} 

Rest code…I do not understand yet, maybe separated setting up?

Now check ubus_wifi_cmd

ubus_wifi_cmd() {
        local cmd="$1"
        local dev="$2"

        json_init
        [ -n "$2" ] && json_add_string device "$2"
        ubus call network.wireless "$1" "$(json_dump)"
}

Once we run wifi, in this function, it is calling

ubus call network.wireless down { }
ubus call network.wireless up { }

This should be different from old version, because I can not find any function named “mac80211_enable” or “enable_mac80211”, so the script enable way is not working anymore.

What is ubus…?
It is a small application used to transfer data between processes by socket/udp protocol.

It has many protocol, and this protocol is hard coded in the ubus source code by C.

root@OpenWrt:/etc# ubus list
dhcp
dnsmasq
hostapd.wlan0
log
network
network.device
network.interface
network.interface.lan
network.interface.loopback
network.wireless
service
system

You see? network.wireless is one of its compose.
At least ubus I can find some tutorials 🙂

I can run ubus monitor to check what is transfer when I call wifi down.

-> 070d3ffa #00000003         status: {"status":0}
-> 3aa4a160 #3aa4a160          hello: {}
<- 3aa4a160 #00000000         lookup: {"objpath":"network.wireless"}
-> 3aa4a160 #00000000           data: {"objpath":"network.wireless","objid":-946844025,"objtype":976669475,"signature":{"up":{},"down":{},"status":{},"notify":{},"get_validate":{}}}
-> 3aa4a160 #00000000         status: {"status":0}
<- 3aa4a160 #c7904e87         invoke: {"objid":-946844025,"method":"down","data":{}}
-> 7d12fdb8 #3aa4a160         invoke: {"objid":-946844025,"method":"down","data":{},"user":"root","group":"root"}
<- 7d12fdb8 #3aa4a160         status: {"status":0,"objid":-946844025}
-> 3aa4a160 #c7904e87         status: {"status":0,"objid":-946844025}

Emm, still no clue where it goes…
Have to search the source code, key word “network.wireless”

Catch it! I get something at “netifd-2018-04-03-xxxx”. So I guess network.wireless is registered in this netifd/ubus.c

static struct ubus_object wireless_object = {
        .name = "network.wireless",
        .type = &wireless_object_type,
        .methods = wireless_object_methods,
        .n_methods = ARRAY_SIZE(wireless_object_methods),
};

This interface looks much better, I can guess with its name “wireless_object_methods” must contains the feature to control wireless up and down.

I am right 🙂 In same ubus.c I find its define.

static struct ubus_method wireless_object_methods[] = {
        { .name = "up", .handler = netifd_handle_wdev_up },
        { .name = "down", .handler = netifd_handle_wdev_down },
        { .name = "status", .handler = netifd_handle_wdev_status },
        { .name = "notify", .handler = netifd_handle_wdev_notify },
        { .name = "get_validate", .handler = netifd_handle_wdev_get_validate },
};

Now let’s check what is netifd_handle_wdev_up, in same file too.

static int
netifd_handle_wdev_up(struct ubus_context *ctx, struct ubus_object *obj,
                      struct ubus_request_data *req, const char *method,
                      struct blob_attr *msg)
{
        struct wireless_device *wdev;
        int ret;

        wdev = get_wdev(msg, &ret);
        if (ret == UBUS_STATUS_NOT_FOUND)
                return ret;

        if (wdev) {
                wireless_device_set_up(wdev);
        } else {
                vlist_for_each_element(&wireless_devices, wdev, node)
                        wireless_device_set_up(wdev);
        }

        return 0;
}

static int
netifd_handle_wdev_down(struct ubus_context *ctx, struct ubus_object *obj,
                        struct ubus_request_data *req, const char *method,
                        struct blob_attr *msg)
{
        struct wireless_device *wdev;
        int ret;

        wdev = get_wdev(msg, &ret);
        if (ret == UBUS_STATUS_NOT_FOUND)
                return ret;

        if (wdev) {
                wireless_device_set_down(wdev);
        } else {
                vlist_for_each_element(&wireless_devices, wdev, node)
                        wireless_device_set_down(wdev);
        }

        return 0;
}

They are all calling wireless_device_set_up/wireless_device_set_down, in wireless.c

static void
__wireless_device_set_up(struct wireless_device *wdev)
{
        if (wdev->disabled)
                return;

        if (wdev->retry_setup_failed)
                return;

        if (!wdev->autostart)
                return;

        if (wdev->state != IFS_DOWN || config_init)
                return;

        free(wdev->prev_config);
        wdev->prev_config = NULL;
        wdev->state = IFS_SETUP;
        wireless_device_run_handler(wdev, true);
}

check wireless_device_run_handler, same file

static void
wireless_device_run_handler(struct wireless_device *wdev, bool up)
{
        const char *action = up ? "setup" : "teardown";
        const char *argv[6];
        char *config;
        int i = 0;
        int fds[2] = { -1, -1 };

        D(WIRELESS, "Wireless device '%s' run %s handler\n", wdev->name, action);
        if (!up && wdev->prev_config) {
                config = blobmsg_format_json(wdev->prev_config, true);
                free(wdev->prev_config);
                wdev->prev_config = NULL;
        } else {
                prepare_config(wdev, &b, up);
                config = blobmsg_format_json(b.head, true);
        }

        argv[i++] = wdev->drv->script;
        argv[i++] = wdev->drv->name;
        argv[i++] = action;
        argv[i++] = wdev->name;
        argv[i++] = config;
        argv[i] = NULL;

        if (up && pipe(fds) == 0) {
                wdev->script_proc_fd.fd = fds[0];
                uloop_fd_add(&wdev->script_proc_fd,
                             ULOOP_READ | ULOOP_EDGE_TRIGGER);
        }

        netifd_start_process(argv, NULL, &wdev->script_task);

        if (fds[1] >= 0)
                close(fds[1]);

        free(config);
}

Haha, I guess it is running script here.

What is in argv… I do not know, but should be easy, let’s add log to the file.

main.c:netifd_start_process

...

 if (pfds[1] > 2)
     close(pfds[1]);

+{
+    FILE *fp = fopen("/tmp/netifd.log", "a+");
+    int id = 0;
+    for (; argv[id]; id++)
+        fprintf(fp, "%d: %s\n", id, argv[id]);
+    fclose(fp);
+}

 execvp(argv[0], (char **) argv);
...

make package/network/config/netifd

Get this result:

0: ./mac80211.sh
1: mac80211
2: setup
3: radio0
4: {"config":{"channel":"11","hwmode":"11g","path":"platform\/10300000.wmac","htmode":"HT20","disabled":false},"interfaces":{"0":{"bridge":"br-lan","config":{"mode":"ap","ssid":"OpenWrt","encryption":"none","network":["lan"],"mode":"ap"}}}}

The parameters are send to netifd-wireless.sh:init_wireless_driver
name = mac80211
cmd = setup
interface = radio0
data = {…}

init_wireless_driver() {
	name="$1"; shift
	cmd="$1"; shift

	case "$cmd" in
		dump)
			add_driver() {
				eval "drv_$1_cleanup"

				json_init
				json_add_string name "$1"

				json_add_array device
				_wdev_common_device_config
				eval "drv_$1_init_device_config"
				json_close_array

				json_add_array iface
				_wdev_common_iface_config
				eval "drv_$1_init_iface_config"
				json_close_array

				json_dump
			}
		;;
		setup|teardown)
			interface="$1"; shift
			data="$1"; shift
			export __netifd_device="$interface"

			add_driver() {
				[[ "$name" == "$1" ]] || return 0
				_wdev_handler "$1" "$cmd"
			}
		;;
	esac
}

Setup will call: _wdev_handler mac80211 setup

_wdev_handler() {                                     
        json_load "$data"                             
                                                      
        json_select config                            
        _wdev_prepare_channel                         
        json_select ..                                
                                                      
        eval "drv_$1_$2 \"$interface\""               
}     

that is drv_mac80211_setup, this function is in /lib/netifd/wireless/mac80211.sh, from my test, it will only run once at startup wifi.

Now everything is clear, time to move on merge the driver!

VoCore2: Port mt7628.ko to LuCI 2

Keep study 🙂

This is the /etc/config/wireless from old version vocore2(Linux 3.18) which is working well with uci system. Thanks to its contributors. It is from Linkit.

config wifi-device	radio0
	option type     ralink
	option variant	mt7628
	option country	CN
	option hwmode	11g
	option htmode	HT40
	option channel  auto
	option disabled	0

config wifi-iface ap
	option device   radio0
	option mode	ap
	option network  lan
	option ifname   ra0
	option ssid	VoCore2
	option encryption none 

config wifi-iface sta
	option device   radio0
	option mode	sta
	option network  wwan
	option ifname   apcli0
	option led 	mediatek:orange:wifi
	option ssid	UplinkAp
	option key	SecretKey
	option encryption psk
	option disabled	1

Note: this file is generated by /lib/wifi/ralink.sh:detect_ralink

Now we can add some log to /lib/netifd/wireless/ralink.sh, check how it read the file and how it call the driver to setup the connection.

It is a pretty long script…

If my last blog is correct, it will call “enable_ralink” … what? no such function…
OK, let’s add some log to it. In every function head, we can add “echo xxxxxx_function >> /tmp/vocore2.log”, it will show the called function name into vocore2.log.

But nothing show in the file…so it is not used at all!

How it works? just a mystery.

Emm, it won’t help, get rid of it, let’s check openwrt with mt76 script, that one works without hack.

No luck today, try tomorrow.

To be continue…

VoCore2: Port mt7628.ko to LuCI

This will be a long journey…
LuCI should be the most easy way to setting router.

I will study from start, try to learn how uci system works and how LuCI works with current openwrt system.

I know something about C/C++, maybe RF, PCB layout or even physics, but I am absolutely a noob of script especially bash.

Lucky me, 50% UCI system is based on shell script. 🙂 So I have a chance to learn it.

For wifi turn on or turn off, all I know is the magic command “wifi”, so what is it?

I find it actually a script, its path can be found by call “which wifi”

interface code of /sbin/wifi

case "$1" in
	down) wifi_updown "disable" "$2";;
	detect) wifi_detect_notice ;;
	config) wifi_config ;;
	status) ubus_wifi_cmd "status" "$2";;
	reload) wifi_reload "$2";;
	reload_legacy) wifi_reload_legacy "$2";;
	--help|help) usage;;
	*) ubus call network reload; wifi_updown "enable" "$2";;
esac

After couple of hours painful decode wifi script, looks like once we call wifi, it will goto ‘*) ubus call network reload; wifi_updown “enable” “$2”;;

I reach a dead end at the beginning. What is this “ubus call network reload”? Is it an important part of uci system? I directly run it in command line, nothing happens…OK, ignore it.

PS: That’s why I hate script, you will be very hard to find what it is exactly doing.

Next it is “wifi_updown enable”. Good name, from its name I know it is able to control enable/disable wifi.

_wifi_updown() {
	for device in ${2:-$DEVICES}; do (
		config_get disabled "$device" disabled
		[ "$disabled" = "1" ] && {
			echo "'$device' is disabled"
			set disable
		}
		config_get iftype "$device" type
		if eval "type ${1}_$iftype" 2>/dev/null >/dev/null; then
			eval "scan_$iftype '$device'"
			eval "${1}_$iftype '$device'" || echo "$device($iftype): ${1} failed"
		elif [ ! -f /lib/netifd/wireless/$iftype.sh ]; then
			echo "$device($iftype): Interface type not supported"
		fi
	); done
}

wifi_updown() {
	cmd=down
	[ enable = "$1" ] && {
		_wifi_updown disable "$2"
		ubus_wifi_cmd "$cmd" "$2"
		scan_wifi
		cmd=up
	}
	ubus_wifi_cmd "$cmd" "$2"
	_wifi_updown "$@"
}

$DEVICE, this should come from scan_wifi.

wifi script it will read /lib/wifi/mt7628.sh, and once we run wifi, it will call function in mt7628.sh, scan_mt7628/enable_mt7628/disable_mt7628 and all the parameters are from /etc/config/wireless.

To Be Continue…

VoCore2: Port mt7628.ko for Linux 4.14 Part2

I have upload source code of uictx to github: https://github.com/Vonger/uictx.git, and also mt7628 driver.
So you can make your own latest version of openwrt now.

PS: please help me do a homework 🙂

1. use it as a OpenWRT feeds, so people do not have to manually do the compile.

2. combine the api to UCI, so we can use UCI and /etc/config/wireless to control the wifi.

Feel free to submit patch.

Note: my last firmware 20180419.bin have some log like this:

lts
[  108.175895] mmc0: error -22 whilst initialising MMC card
[  108.263953] mtk-sd 10130000.sdhci: no support for card's volts

This is because you do not have SD card hardware. We have two ways, one call “rmmod mtk_sd” in console.

Another, use this firmware :p
http://vonger.cn/misc/vocore2/20180419V.bin

md5: 41fc7dfb02bd183de8a520ad146a65b8

I added shellinabox to this firmware, so you can directly access to console without using ssh or putty. Open 192.168.61.1:4200.

Or third way, compile your own firmware without mtk_sd.

Happy hacking!!

VoCore2: Port mt7628.ko to Linux4.14

Yes, I have successfully port mt7628.ko to latest openwrt based on Linux 4.14.

Here is the firmware: http://vonger.cn/misc/vocore2/20180419.bin

md5: 547fba4379e61fefb145cef2e1c30609

This firmware can not working with uci system yet…

PS: I am still study UCI…

But I write a simple replace.
It is a cgi-bin app, can run in /www/cgi-bin or directly run in console command line.

if you want to use it as webserver, just run http://192.168.61.1/cgi-bin/uictx?f=sta_disconnect in your browser.

if you want to use it as command, just run uictx “f=sta_disconnect” in command line.

 

### ap_set_ssid

– used to set current ap ssid name.
– usage: ap_set_ssid [ssid]
– example: http://192.168.61.1/cgi-bin/uictx?f=ap_set_ssid&p1=VoCore2

### ap_set_password

– used to set current ap ssid password.
– usage: ap_set_password [password]
– example: http://192.168.61.1/cgi-bin/uictx?f=ap_set_password&p1=12345678

## Scan API

### ap_scan

– get nearby ssid, bssid, and signal strength.
– usage: ap_scan
– example: http://192.168.61.1/cgi-bin/uictx?f=ap_scan
– return: {“status”:”success”, “message”:””, “ssid1″:”KB86”, “bssid1″:”30:b4:9e:a6:99:bb”, “signal1″:”99”, “ssid2″:””, “bssid2″:”28:6c:07:cc:e7:46”, “signal2″:”18”, “ssid3″:”CX-8888”, “bssid3″:”30:c8:9e:aa:e6:2f”, “signal3″:”57”}

## Station Mode API

### sta_connect

– connect to nearby access point as client device. if everything is correct, it will alloc a ip for apcli0. MUST call ap_scan before sta_connect.
– usage:
– normal ap: sta_connect [ssid] [password]
– hidden ap: sta_connect “” [password] [bssid]
– example1: http://192.168.61.1/cgi-bin/uictx?f=sta_connect&p1=MY%20ROUTE&p2=88884444

note: in the example, “MY ROUTE” has a space, it needs to convert into url format.

– example2: http://192.168.61.1/cgi-bin/uictx?f=sta_connect&p1=&p2=88884444&p3=30:b4:9e:a6:99:bb

### sta_disconnect

– disconnect from connected access point, you will immediatily disconnect from your router and vocore will back to ap mode.
– usage: sta_disconnect
– example: http://192.168.61.1/cgi-bin/uictx?f=sta_disconnect

Source code I will upload to github soon.

 

PS: do not forget setting up the network first.

uci add_list firewall.@zone[1].network="wwan"
uci set network.wan=interface
uci set network.wan.ifname=eth0.2
uci set network.wan.force_link=1
uci set network.wan.proto=dhcp
uci set network.wwan=interface
uci set network.wwan.ifname=apcli0
uci set network.wwan.proto=dhcp
uci commit

VoCore2 + DOOM Port

[embedyt] https://www.youtube.com/watch?v=homxYyDKVis[/embedyt]

I spend around 10 hours finish this, looks like it is pretty easy to port exists project to using VoCore2 display.

First checkout source code from git@github.com:Vonger/Doom.git, thanks for its contributor jeffdoggett, other DOOM source code I can not even compile 🙂

Only need to change one file /Doom/Source/i_video.c

//
// DESCRIPTION:
//	DOOM graphics stuff for VoCore2 Display, OpenWrt.
//  based on i_video.c.
//
//-----------------------------------------------------------------------------

#include "includes.h"
#include "libvodisp.h"

unsigned int SCREENWIDTH = 320;
unsigned int SCREENHEIGHT = 200;
unsigned int ORIGSCREENWIDTH = 320;
unsigned int ORIGSCREENHEIGHT = 200;

union pixel {
    struct _rgb {
        unsigned char red;
        unsigned char green;
        unsigned char blue;
    }rgb;
    unsigned int full:24;
};
static union pixel palette_table[256];
static byte *framebuffer;

void I_InitGraphics (void)
{
    framebuffer = malloc(800 * 480 * 3);
    memset(framebuffer, 0, 800 * 480 * 3);

    vodisp_connect();
}

void I_ShutdownGraphics(void)
{
    vodisp_disconnect();
}

void I_SetPalette (byte* palette)
{
    unsigned int colour = 0;
    do {
        palette_table[colour].rgb.blue   = gammatable[usegamma][*palette++];
        palette_table[colour].rgb.green = gammatable[usegamma][*palette++];
        palette_table[colour].rgb.red  = gammatable[usegamma][*palette++];
    } while (++colour < 256);
}

void I_FinishUpdate (void)
{
    byte *src = screens[0], *dst = framebuffer;
    int i, j, pos;

    for (j = 0; j < SCREENHEIGHT; j++) {
        pos = j * 480 * 3;
        for (i = 0; i < SCREENWIDTH; i++)
            *(unsigned int *)(dst + pos + i * 3) = palette_table[*src++].full;
    }

    vodisp_write_frame(framebuffer);
}

void I_ReadScreen (byte* scr)
{
    memcpy(scr, screens[0], SCREENWIDTH * SCREENHEIGHT);
}

void I_InitKeyboard (void)
{
}

void I_UpdateNoBlit (void)
{
}

void I_SetScreenSize (void)
{
}

void I_SetWindowName (const char * title)
{
}

void I_StartFrame (void)
{
}

void I_StartTic (void)
{
}

Here is necessary library and Makefile, you might need to modify.

Download: http://vonger.cn/misc/vocore2/doom.openwrt.tar.xz, uncompress and put it in /Doom/Source then compile.

VoCore2: UI Library 4

I want to make the display support SDL so we can play a lot of games and make app based on SDL2.

PS: I love DOS games 🙂

SDL2 supports directFB which is not maintained anymore.
So I am consider to write a video driver for my display.

In SDL source code, SDL_DirectFB_video.c, we can get every interface:

    /* Set the function pointers */
    device->VideoInit = DirectFB_VideoInit;
    device->VideoQuit = DirectFB_VideoQuit;
    device->GetDisplayModes = DirectFB_GetDisplayModes;
    device->SetDisplayMode = DirectFB_SetDisplayMode;
    device->PumpEvents = DirectFB_PumpEventsWindow;
    device->CreateSDLWindow = DirectFB_CreateWindow;
    device->CreateSDLWindowFrom = DirectFB_CreateWindowFrom;
    device->SetWindowTitle = DirectFB_SetWindowTitle;
    device->SetWindowIcon = DirectFB_SetWindowIcon;
    device->SetWindowPosition = DirectFB_SetWindowPosition;
    device->SetWindowSize = DirectFB_SetWindowSize;
    device->SetWindowOpacity = DirectFB_SetWindowOpacity;
    device->ShowWindow = DirectFB_ShowWindow;
    device->HideWindow = DirectFB_HideWindow;
    device->RaiseWindow = DirectFB_RaiseWindow;
    device->MaximizeWindow = DirectFB_MaximizeWindow;
    device->MinimizeWindow = DirectFB_MinimizeWindow;
    device->RestoreWindow = DirectFB_RestoreWindow;
    device->SetWindowGrab = DirectFB_SetWindowGrab;
    device->DestroyWindow = DirectFB_DestroyWindow;
    device->GetWindowWMInfo = DirectFB_GetWindowWMInfo;

    /* !!! FIXME: implement SetWindowBordered */

#if SDL_DIRECTFB_OPENGL
    device->GL_LoadLibrary = DirectFB_GL_LoadLibrary;
    device->GL_GetProcAddress = DirectFB_GL_GetProcAddress;
    device->GL_MakeCurrent = DirectFB_GL_MakeCurrent;

    device->GL_CreateContext = DirectFB_GL_CreateContext;
    device->GL_SetSwapInterval = DirectFB_GL_SetSwapInterval;
    device->GL_GetSwapInterval = DirectFB_GL_GetSwapInterval;
    device->GL_SwapWindow = DirectFB_GL_SwapWindow;
    device->GL_DeleteContext = DirectFB_GL_DeleteContext;

#endif

    /* Shaped window support */
    device->shape_driver.CreateShaper = DirectFB_CreateShaper;
    device->shape_driver.SetWindowShape = DirectFB_SetWindowShape;
    device->shape_driver.ResizeWindowShape = DirectFB_ResizeWindowShape;

    device->free = DirectFB_DeleteDevice;

Of course, I do not think VoCore2 can run OpenGL, because it do not have any video hardware, ignore that.

PS: mesa3d has a software mode but it must be very slow. 🙂

To be simple, we do not need shaped window support either…

OK, rest interfaces for windows look like necessary, every video driver has them.
So they are VideoInit/Quit, Get/SetDisplayModes, CreateSDLWindow/From, SetWindowTitle/Icon/Position/Size, Show/Hide/Raise/Maximize/Minimize/Restore/DestroyWindow.

But it still need to code a lot…
I find most of the interfaces are empty in its psp video driver, maybe there is a way to make everything simple.

Any good idea?

TO BE CONTINUE…

VoCore2: 5 ethernet + SD card

Currently default pinmux is one ethernet port + sdcard + i2s + i2c + refclk + many gpio.
Actually there is a hidden pinmux in mt7628 chip, we can use ethernet x 5 + sdcard without i2c/i2s.

Very interesting, this pinmux works well on VoCore2 but do not have any clue in datasheet.
We need to remove i2c pull up resistor to make it work normal.

Its pin order:

29-GPIO0-SD_CLK
16-I2S_SDI-SD_D1
18-I2S_WS-SD_D0
19-I2S_CLK-SD_CMD
20-I2C-SCLK-SD_D3
21-I2C-SDAT-SD_D2

but also need to update driver to make it work. 🙂

VoCore2: Production Lifetime

Today, support get an interesting question, how long is the VoCore lifetime?
I think this is an important question for the people want to use VoCore2 as part of their production.

As we promised, VoCore2 life is not end until the chip stop production. Mainly VoCore2 have three chips, one DDR2, one CPU, one NOR flash, DDR2 and NOR flash are widely used in many area, so we can think it will keep production forever. The only problem is CPU.

VoCore first version is using RT5350F which is almost stop production by mediatek/ralink. I said ‘almost’, because we still able to buy it from MTK(not recycle chip, the new one). The only problem is its price is increased a lot, almost double of MT7628.

Chip companies such as mediatek will not allow their chips suddenly death, that is not good for themselves and not good for their loyal clients. Normally mediatek will increase the chip price month by month if they decide to end one production. One day, the old chip price will be higher than the new one, so why pay much to use old slow one but not the new fast low power consume one? At that time people will start to upgrade their production. For RT5350, this process is over three years.

Actually VoCore old version is still in production, but price is much higher than the one based on MT7628. When RT5350F price is same as MT7628, I removed VoCore from site, because MTK is giving a clear signal: we are going to stop, do not use it anymore.

MT7628 is coming around four years, it still have over 10 years lifetime, so no need to worry about VoCore2 🙂
note: MTK is not stupid, why stop a cash cow? unless nobody use it already.:)