Thursday, May 14, 2020

Using SpeedFan's driver to call Dell BIOS functions for fan control

SpeedFan uses a signed driver to talk to hardware for temperature measurement and fan control. The fact it's signed is important because Windows 10 makes running unsigned drivers difficult. I wasn't satisfied with how SpeedFan fights with Dell Inspiron 6400 laptop's built in Fan control. I8kfanGUI was better, but didn't have a signed driver, so I only used that in Windows 7 and earlier.

I also wasn't satisfied with fan control options in Linux. The Linux kernel provides access to Dell BIOS temperature measurement and fan control functions via the i8k driver, but attempting fan control led to the same issues. So, I first created a small fan control program in C in Linux. Getting it to run in Windows was surprisingly easy, thanks to SpeedFan's driver. First one needs to open the driver:

    sfdrv = CreateFile("\\\\.\\SpeedFan",
                       FILE_ALL_ACCESS, FILE_SHARE_READ, NULL,
                       OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

Then one can use the IOCTL to call Dell BIOS functions:

#define SFD_CALL_DELL 0x9C402424

int call_dell(uint32_t eax, uint32_t ebx)
{
    uint32_t inbuf[4], outbuf[4];
    DWORD readbytes = 0;

    inbuf[0] = eax;
    inbuf[1] = ebx;
    inbuf[2] = 0;
    inbuf[3] = 0;

    if (DeviceIoControl(sfdrv, SFD_CALL_DELL,
                        &inbuf, sizeof(inbuf),
                        &outbuf, sizeof(outbuf),
                        &readbytes, NULL) == 0 ||
        readbytes < 4) {
        return -1;
    } else {
        return outbuf[0] & 0xFF;
    }
}

I don't know if you need to read and write 16 bytes. SpeedFan always did it this way. I guess they're probably eax, ebx, ecx and edx, in little endian order of course. Certainly the first two seem to be eax and ebx. The i8k Linux driver, now part of dell-smm-hwmon.c, will show you what to do with this.

#define I8K_SMM_SET_FAN     0x01a3
#define I8K_SMM_GET_FAN     0x00a3
#define I8K_SMM_GET_TEMP    0x10a3

int get_temp(unsigned int which)
{
    return call_dell(I8K_SMM_GET_TEMP, which);
}

int get_fan(void)
{
    return call_dell(I8K_SMM_GET_FAN, 0);
}

int set_fan_real(int speed)
{
    if (speed < 0 || speed > 2) return -1;
    return call_dell(I8K_SMM_SET_FAN, (speed << 8) | 0);
}

In my program I used the following rules to reduce fighting between my fan control and the built in fan control: Read fan speed setting (not RPM, but off / low / high) before setting it. Only write it to change it (because otherwise it's pointless). It is always okay to raise fan speed. Once the program raises fan speed, it is allowed to lower it. If the program reads a speed that is higher than the last one it set, it is not allowed to lower it anymore until after the next time it raises fan speed.

I used the rohitab.com API Monitor to figure this out by watching the DeviceIoControl calls that SpeedFan was using.

Other people have also used the SpeedFan driver. Although you need to have Administrator rights to talk to the driver, the driver seems to be a bit of a security hole because even Administrator isn't supposed to have such absolute total control in Windows. Here's one example: https://github.com/SamLarenN/SpeedFan-Exploit/

Here's an example of MSR reading to get the temperature from the CPU's internal thermal sensor:

#define SFD_READ_MSR 0x9C402438

int read_msr(uint32_t msr, uint64_t *dest)
{
    DWORD readbytes = 0;
    if (DeviceIoControl(sfdrv, SFD_READ_MSR,
                        &msr, sizeof(msr),
                        dest, sizeof(*dest),
                        &readbytes, NULL) == 0 ||
        readbytes != sizeof(*dest)) {
        return -1;
    } else {
        return 0;
    }
}

int get_coretemp(void)
{
    uint64_t msrdata;
    if (read_msr(0x19C, &msrdata) < 0) return -1;
    if (msrdata & 0x80000000) {
        return 100 - ((msrdata >> 16) & 0x7F);
    } else  {
        /* Reading not vaid */
        return -1;
    }
}

I'm not using that because the CPU sensor reported by the Dell BIOS gives the temperature of the hottest core. It seems if I read the MSR directly I would have to run the code on each core I want to measure. Calling Dell BIOS once is simpler.

I hope the code didn't get mangled by blogger. I haven't written anything here in a long time. Trying to format stuff as code, in a fixed font, made it too wide, so I didn't bother.

Opening a "single use" armband plastic snap together button

As you can see the armband's button was secured by 4 plastic ribs which stick out from the post. The ribs on the post and the profile of the ring act like a wedge, temporarily deforming plastic to allow you to close and latch the button. But, once latched, the slope you would have to work against to unlatch is very steep. So, the button is easy to close but very hard to open via pulling.

In order to open the button less destructively, a pipe is needed, to go in the slot between the post and ring and . Finding a pipe of the right diameter and thickness would be difficult. So, I took a sheet of aluminum, cut a small piece, and shaped it onto a pipe using a suitably sized wire and pliers. Such aluminum can be easily be cut by scissors, though you probably shouldn't use good scissors which you want to keep sharp. Once I made the pipe, I adjusted it, making it a bit smaller by cutting a tiny sliver with scissors to reduce circumference and again shaping with pliers. Then I forced the pipe into the gap between the ring and core.

Saturday, December 30, 2017

Dealing with a partially installed Windows 7 update

On one computer the June 2017 monthly rollup (KB4022719) somehow installed improperly or got corrupted. It couldn't be uninstalled, and future rollups couldn't be installed. Apparently, the process of installing a new rollup first needs to uninstall the previous one.

The Windows Update user interface simply gave the useless 80004005 error, but detailed information about the failure was available in c:\windows\logs\cbs\cbs.log:

2017-12-28 20:54:05, Error                 CBS    Failed. Attempted to uninstall a version of a non-driver component that is not installed, version: 0X700061db15cfe, component: amd64_microsoft-windows-w..lient-aux.resources_31bf3856ad364e35_7.6.7601.23806_en-us_e79af1b12d6db6c9, owner: Package_835_for_KB4022719~31bf3856ad364e35~amd64~~6.1.1.6.4022719-2919_neutral_LDR [HRESULT = 0x80004005 - E_FAIL]

Each update is a package which refers to many other packages, like Package_835_for_KB4022719~31bf3856ad364e35~amd64~~6.1.1.6.4022719, and each of those packages refers to components, like 2919_neutral_LDR. If you're curious, you can unpack an update and look inside it. First unpack the .msu to get the CAB and then unpack the CAB. You probably must use Microsoft's expand.exe to unpack the CAB because it may use intra package delta (IPD) compression which is not supported by 7-Zip.

Inside the CAB you will find various .mum files, which correspond to the main package and those sub-packages. The logical thing to do would be to reinstall these problematic components. However, I don't see a way to install from a .mum file.

So, I ended up uninstalling the messed up update by editing its already installed .mum file, found in c:\windows\servicing\packages, as  package_for_kb4022719_sp1~31bf3856ad364e35~amd64~~6.1.1.6.mum. (Note the absence of a number after "package" in the file name.) That directory is set up by default so only TrustedInstaller can write there. Not even SYSTEM can write there! So, I had to temporarily grant access to Administrators. The sections to remove are like this:

<update name="4022719-4117_neutral_PACKAGE">
<package integrate="hidden">
<assemblyIdentity name="Package_835_for_KB4022719" version="6.1.1.6" language="neutral" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" />
</package>
</update>


After removing the sections referring to messed up sub-packages, it was possible to uninstall the rollup. However, reinstalling it didn't fix the problem. I guess the messed up partly installed sub-packages don't get reinstalled? Fortunately, I was able to install the December roll-up without problems. Hopefully future roll-ups will install, and the only consequences will be a few small unnecessary but harmless files and registry entries.

Monday, December 04, 2017

How to log in to SoundCloud in Kodi

The SoundCloud add-on for Kodi accesses SoundCloud via its API and OAuth2 authentication. It is supposed to log in once using your username and password, and get a token. After this, there is no need for your password, and it can access SoundCloud using just the token.

The problem is that SoundCloud's initial procedure for obtaining the token has changed, and you now need to use the /connect method. But, once the add-on has the token, it can log in. So, the solution is to get the token using another method.

Soundnode is a desktop app which also authenticates to SoundCloud via OAuth2. After authenticating, the token can be found in its configuration files.  In Linux, URL strings including the token can be found in ~/.config/Soundnode/Cache/data_*. Look for URL inside like https://api.soundcloud.com/me.json?&oauth_token=something . Then add the token to your add-on configuration file, in Linux at ~/.kodi/userdata/addon_data/plugin.audio.soundcloud/settings.xml. You need to add <setting id="login.access_token" value="something" /> .

While the add-on doesn't need the login.username and login.password, its code is written such that they need to exist and the login.hash needs to match. You can compute the hash in Python using:

import hashlib
username = 'username'
password = 'whatever'
m = hashlib.md5()
m.update(username.encode('utf-8') + password.encode('utf-8'))
m.hexdigest()


Since the add-on is incapable of actually using this information to log in, feel free to not actually put your real password there. The OAuth2 token provides access to your SoundCloud account, so keep that secure.

This may not be a complete solution, because although I can access lists of things, playback fails with a HTTP 401 error. I don't know if that means the SoundNode API quota is depleted or if I need to do other things. Setting CLIENT_ID and User-Agent in ~/.kodi/addons/plugin.audio.soundcloud/resources/lib/content/client.py did not help.

Friday, March 17, 2017

Streaming video from an MJPEG network camera to applications in Linux

I recently got a D-Link DCS-930L network camera. It outputs a motion JPEG (MJPEG) stream at http://address/video.cgi. Although there's no kernel driver for receiving that, it is possible to send it to applications from user space via v4l2loopback.

First you need to install the module. Debian and Ubuntu have it in the v4l2loopback-dkms. If your distribution doesn't have it, then you'll need to build it and install it yourself. Then, load the module. If you want Chrome to see it, load it with the exclusive_caps=1 parameter, like sudo modprobe v4l2loopback exclusive_caps=1 .

Then, run a program to read the video and send it to the loopback device. It's possible with ffmpeg, using something like this:

ffmpeg -f mjpeg -i http://user:pass@192.168.1.30/video.cgi -s 640x480 -vf format=pix_fmts=yuv420p -f v4l2 /dev/video0

Note user:pass, which would be the credentials needed to log in to the camera. If you put yours there, note that it's insecure, as ps shows command lines. Also note the resolution and video device, which you may need to change.

A similar method could be used if you wanted to read from one video device, apply effects, and then present the video with effects to an application.

Sunday, December 18, 2016

How to find good yogurt in Canada

While living in Croatia in the 80s, I enjoyed plain yogurt every day. After moving to Canada, I was very disappointed with the yogurt here.

Now I can find good yogurt in Canada. It's really quite simple: look at the ingredients. Yogurt is supposed to contain milk ingredients and bacterial culture. It is not supposed to contain various gums, gelatin or corn starch. I suppose factories add that to create consistent texture even when they're unable to make proper yogurt which would have a good texture on its own.

Astro Original is consistently good. Middle Eastern brands like Halal and Phoenicia are also good. Danone usually disappoints.

In Canada you can buy lots of different yogurt with added flavour and/or fruit. I generally avoid those, because they have lots of added sugar or artificial sweeteners. It's better to enhance yogurt with fruit on your own. Sour cherries which were washed and pitted in the summer are great for this. Preparing it is a lot of work one day, but it's kind of fun and after that they're easy to use. I freeze them in small containers which after defrosting last for a week or so.

Don't shy away from fat. Research shows that fat isn't that bad, sugar is far worse, and even artificial sweeteners are unhealthy. Without fat, it's hard to get the right consistency and flavour, and many low fat products add other unhealthy things instead. So, 3% yogurt is fine, and don't assume 0% is better.

The same general advice applies to other milk products like kefir, huslanka and even sour cream.

Facebook friend request accepted even though I never sent one

A while ago someone I stayed in touch with over 10 years ago appeared in my suggested friends list. That was probably due to a mutual friend. I looked at the suggested friend's page and public posts for a bit, and decided to not send a friend request. Later that day I was told that he accepted my friend request.

I don't think I made a mistake and accidentally sent a friend request. Furthermore, in the friends activity log at https://www.facebook.com/dreamlayers/allactivity?privacy_source=activity_log&log_filter=cluster_8 I see a "became friends" entry, but no "sent a friend request" entry. I just scrolled through the entire log, to the beginning of my time on Facebook, and still see no friend request. For various other people I see a "sent a friend request" entry followed by "became friends".

This isn't a problem and what happened was probably a good thing. So, I'm not complaining. It's just weird and I'm wondering why this happened.

Monday, December 12, 2016

Notes about unlocking a Vonage VDV23 VoIP box

I got a used Vonage VDV23 VoIP box to play with. Vonage seems expensive and limited compared to other VoIP providers like voip.ms, so I decided to unlock the box for use with other providers. A guide is available, but I ran into various problems trying to follow it, and this was quite an adventure. You can access the guide for free, but need to register at voipfan.net and log in there. The comments here are meant as a supplement to the guide, not a replacement.

Note that buying this device for use with Vonage may be pointless. Vonage will give you a new adapter if you sign up, and may not allow you to use an old one.

Firmware loading via TFTP

Always use the internal phy. It will tell you if autonegotiation worked and you got an Ethernet connection. I never had any success whatsoever when I selected external phy.

Network connectivity from the bootloader sometimes seems terribly unreliable. Pinging the VDV23 only worked once. Other times it at best answered a few pings, often with delays of more than a second. Received pings also spit out error messages to the console. Trying to ping first may even break subsequent TFTP attempts. Don't bother testing the network connection with ping; just try TFTP.

After trying my main PC and router, I ended up using the Ethernet port on this Inspiron 6400 laptop. Also, I was always setting up the network immediately after startup, not later on via the menu. When I did this, TFTP always worked.

The "Board IP Gateway" must exist, because the VDV23 will perform ARP queries for it when starting TFTP. Yes, it will even do this if the address is within the LAN. I just set it to the TFTP server address.

Getting the Admin password

Ignore the password at BF7F0118. Yes, there seems to be a password there, but I could never log in using it. If the password at BF3D00FA is all zeros, you didn't wait long enough for the VDV23 to download the password from Vonage and configure itself. If you wait too long, it will start upgrading firmware.

The first time I couldn't log in with the password from BF3D00FA either. I'm not sure if the "safe" vdv21-3.2.6-na_boot.bin firmware needs to get the password over the Internet from Vonage again. It may also start upgrading firmware if you wait too long. So, you need to disconnect from the Internet and continue with the rest of the guide.

Configuring SIP

I chose to configure via the XML file. First I tried to use an address accessible from the blue WAN port, but I didn't see any HTTP requests. Then I chose the default http://192.168.15.10 address and it worked via the yellow Ethernet port.

The device always sends an HTTP request soon after booting, so I was just unplugging it and plugging it back in to change the configuration. Seems like it first configures itself using stored parameters. Then if the XML file has different parameters the phone light will go out for a while as it reconfigures itself. Don't take the phone off hook during that process, because it will be testing the line. You may end up with a blinking phone light and a console complaint about the ringer equivalence number (REN) being too high.

Pay attention to the dialPlan lines. The one in the provided XML isn't enough for some voip.ms numbers, leading to a fast busy. I couldn't figure out how to support 2 and 3 character * codes except using *xx.T, which waits for a timeout before proceeding with the call. For experimenting, I could reconfigure dialPlan once from the console and see immediate results.

I forwarded port 8660 because it is localPort in the XML, but am not sure if that was necessary. The device is meant to be directly connected to the Internet and act as your gateway, providing NAT while prioritizing VoIP traffic. Since I'm still playing with it, I'm not going to do such a drastic change to my network.