The Best Part of Low End

As I get closer and closer to the cranky old man stage of life I get pickier and picker about my monitors and office lighting. I also find supposed high end products intolerable.

Anyone who reads this blog knows I write a lot of books. “The Minimum You Need to Know About the Phallus of AGILE” is off for final editing. I have two more books that are somewhere between 2/3 and 3/4 of the way completed. Those who visit the Interesting Authors blog were exposed to early (and ugly) drafts of their early chapters.

Over the course of the past few weeks I’ve been waxing nostalgic for my days of COBOL. This has lead me to experiment with GnuCOBOL under KDE Neon 64-bit. This is Yet Another uBUntu (YABU) Linux distro. I’ve also been kicking around various editors and IDEs, even giving UltraEdit another try. Just haven’t had the time to see if they actually fixed the C/C++ standard formatting. What is with editors like Atom, Sublime Text 3, and UltraEdit wanting you to hack default configuration files instead of providing options in menus? This is not a good trend though many seem to be following it. A previous post about Sublime Text 3 and COBOL may have given you some idea of what I was doing now.

Why so many things? There is no IDE for GnuCOBOL. Yes, during my days on OpenVMS I gladly used EDT or LSE, then ran a stand alone debugger. I would still do it today if the billing rates weren’t all illegal alien wages. The one thing that made it great, besides OpenVMS, was the debugger. It was screen oriented and worked with any language back when PC’s were selling retail with only two floppy disks installed.

The massively overweight Eclipse was my first knee-jerk impression. Let me save you some time. The COBOL plugin is horribly broken. By that I mean it will not install from the marketplace. Fine! I did some digging and found out where it lives. I followed the manual install instructions. Managed to install, but it crashes and burns when you try to start a COBOL project. Despite my not favorable impression of Eclipse, this could have justified its existence.

Someone pointed me to the OpenSource version of Visual Studio Code, VSCodium. The one without evil empire tracking information built into it. I installed it and the COBOL plugin for it. I also installed the OpenVMS IDE plugin. Don’t install both of these together. I no longer have my DS-10 Alpha (and don’t want another) but I did want to kick the tires on that plugin at some point. If you install both the COBOL plugin jumps off a cliff. Uninstalling the VMS IDE plugin made the COBOL plugin “work”. It can’t really “work” until version 3.1 or greater of GnuCOBOL is released. A compilation switch has been added to make things nicer. You can read a bit about it in this message thread.

One thing is for certain, the $299 SlickEdit Pro didn’t handle GnuCOBOL like an IDE. I uninstalled that trial within half an hour of getting the trial license key. The makers of SlickEdit need a 5 active install, $99/year license like UltraEdit if they want to be taken seriously. UEStudio would have been worth looking at, but it doesn’t run on a real operating system so why bother?

I honestly hoped KDevelop had a COBOL plugin. A quick search of the user forum shows quite a few people interested in COBOL, but so far nobody interested in developing the plugin others are waiting for. I like KDevelop because it is built around/using KATE. One of the reasons I was paying $99/year for UltraEdit was clients kept forcing me to use default Ubuntu desktop for development. Yes, QtCreator is great for Qt development, but you need another really good editor for all of your cutting and pasting and note taking. That’s what I used to use KATE for.

UltraEdit has always had a “feature” (inexcusable bug) that has pissed me off to no end over the years. It’s a burr under the saddle that just rubs both horse and rider raw. It happens when you have told UE to use spaces instead of tabs. The tab size value isn’t correct.

empty file with cursor in first position image
empty file with cursor in first position

With tab size set to 4 an IT oriented user would expect the first “tab stop” to be at 4, then multiples of 4 across the page.

after first tab key image
after first tab key

 

after second tab stop image
after second tab stop

Like I said, the cranky old man stage of life waits for all of us if we live long enough no matter what gender we are born as. For as long as I can remember this bug has existed in UltraEdit when using spaces and it is just ()&*)(*&ing annoying! In the new new new new world of COBOL it is a catastrophe. Why?

cobol fixed format sample image
cobol fixed format sample

In fixed format COBOL, Area-A starts in column 8, Area-B starts in column 12, columns 1-6 are for line numbers, column 7 is the comment/continuation, and column 72 is quite literally the end of the line. In “modern” fixed format COBOL, they tend to skip the line numbers.

modern fixed format cobol image
modern fixed format COBOL

In short, you need a tab size of 4 to actually be 4 when inserting spaces instead of tabs. This means if the cursor is sitting in column 1, you only insert 3. In fact, at all times you take the current cursor position, subtract it from the next highest multiple of 4, and insert exactly that many spaces so the cursor lands in said multiple of 4.

Now that I’ve issued this severe beating, it should be noted that UE does handle syntax highlighting correctly in both fixed and free format COBOL.

COBOL free format UE image
COBOL free format UE

That is something notepadqq doesn’t do or at least doesn’t do in an obvious manner.

notepadqq COBOL image
notepadqq COBOL

Notice how the first 6 characters are blue? That’s because it is hard coded for fixed format (or at least not an obvious way to enable free format.)

notepadqq COBOL fixed format image
notepadqq COBOL fixed format

There is supposedly some hack I can write to make UE auto-generate zero filled line numbers spacing them by 10. I just haven’t had time to try. I’m not certain I want to pay that kind of money _and_ have to fix that myself, especially since it sounds like I’m going to have to hack the default C++ template so it doesn’t force K&R on me. No, I don’t care what your argument is. If your C/C++ coding style involves this:

if (fred) {
    /* do someting */
} else {
    /* do something else */
}

Where they “{” is on the same line as the if and the “}” is on the same line as the else you are using the archaic K&R style. Java also uses this style and it hasn’t been a valid coding style since about 1985, neither is any style based on it.

In it’s defense, I really like the color schema notepadqq uses for COBOL. I “think” one should be able to hack the existing cobol.js file to make it fixed/free sensitive.

Sublime Text doesn’t do a bad job. Unlike UltraEdit, I can click the line numbers in the output window for errors and be taken to them. You just can’t debug even with the GDB debug plugin.

line numbers compiled with -free
compiled with -free

There might be yet another hack I can do to UE to make that work but I think you are starting to see the lack of appeal when it comes to paying an annual fee for something that I not only have to hack, but have to keep those hacks on-line so I have them when I travel to a client site and have to use one of their development machines for 6-12+ months. Sublime Text 3 was something like $25 back when I bought it and good forever. (I do think it is around $80 now.) If they come out with the same one time fee no matter how many times you install deal for Sublime Text 4 in a few years, yeah, I’ll do that. So far I haven’t actually had to hack hidden files. I’ve installed a few plugins and tweaked a couple of user/configuration settings using menu options. I didn’t have to find the super secret directory where they hid the file on this platform.

Why then, the title of this blog post?

Ah grasshopper. You’ve been reading my books and my blogs for years (or you should have been!) The journey is more important than the destination. I don’t write for the Twitter generation, I write for people who can think for more than 140 characters.

My waxing nostalgic for COBOL (and an academic on-line who knows less than nothing talking out his ass) got me to do some COBOL programming and COBOL research again. I’ve once again been spending an ocean of time in text editors and LibreOffice. My eyes have become more sensitive as I age and I’ve become pissed off at all of the “dots” I see making up text on my screens.

Yes, that Zotac branded NVIDIA card with 384 CUDA core would make BOINC crunch like the dickens and complex images would appear quite clear, but plain old text looks more and more like it was printed on the screen by an 8-pin dot matrix than some high end combination of graphics card and monitor. I have to get a long way from the monitor before that collection of dots looks like a “solid” character. In truth, with or without my glasses, the monitor has to be a good twelve inches past the end of the fingers on my outstretched arm before enough of a blur effect happens to push the dots together.

Believe it or not, the ViewSonic Super CLEAR IPS monitor I’m using right now looks even worse than the VG2732m-LED monitor sitting beside it connected to the same video card on the same computer.

For the past few days I’ve been hacking this 7th-gen i7 HP EliteDesk. I suspected the NVIDIA based card just kind of sucked at text. The machine has a built in Intel HD card, so why not use it?

intel built in graphics image
Intel built in graphics

Intel HD Graphics cards, Skylake in particular, have a lot of issues under Linux. There is a continually recurring bug where two monitors are found, but only one displays anything. Yesterday I didn’t really have time to fight with it so I snagged an old MSI (Radeon based) video card out of my spare parts drawer and stuck it in. I wish my attempts at taking a picture yielded something I could be certain would show up on a Web page. Most of the dots were gone?

Why was that video card one of half a dozen “spare” cards in that massive drawer? AMD ceased supporting it with the stuff that became part of Ubuntu 18.04. The “free” driver doesn’t make the GPU available to BOINC so I had to obtain another of those NVIDIA based Zotac cards. (I can usually find them for $50 or less, probably also one of the reasons I see dots.) They certainly make BOINC crunch numbers when I’m not at the keyboard!

Today I was miffed. I really wanted to solve my built-in Intel video problem. When I got to the office after running errands I re-read _all_ of the posts in that bug link. Please allow me to tell you how to solve your problem of second monitor displaying either nothing or horrible looking stuff when Intel HD graphics are involved. Oh, laptop users, you are screwed.

  1. Power down.
  2. Open your case.
  3. Install any non-Intel video card you have available which will fit an existing slot and connect monitor(s).
  4. Close case.
  5. Boot Linux.
  6. Open a terminal.
  7. sudo apt purge xserver-xorg-video-intel
  8. Power down.
  9. Open your case, remove installed video card, connect monitors to ports of your built-in video card, close case.
  10. Boot Linux.

This fixes the problem. My text is about as grainy as it was with the higher end NVIDIA based card. At some point tomorrow I may be forced to install the really old MSI card because text looked a lot cleaner.

Companies writing drivers for Windows do, and Windows itself does a lot of cheating when it comes to making text look solid. On the Linux platform few developers take the time to make text look as solid as a green phosphor letter.

You’ve probably guessed one of the books I just started writing; “The Minimum You Need to Know About GnuCOBOL.” What you probably haven’t guessed is where I landed for a COBOL “IDE”

Emacs

Yeah, I didn’t think I would ever install something that heavy again. Well, that heavy excluding the total tonnage of Eclipse.

I’m not certain but Emacs just might be the last place Lisp still exists. I vaguely remember having to write portions of an Algol then a Lisp compiler back when I was working on a masters degree.

I really did try a lot. If I was willing to push a bolder up hill with my nose, I probably could have dug into Anjuta and got it working with the compiler and debugger.

Anjuta old fixed format image
Anjuta old COBOL fixed format

 

Anjuta COBOL new fixed format image
Anjuta COBOL new fixed format

 

Anjuta COBOL free format image
Anjuta COBOL free format

I suspect the reason the 2 lines are in RED is this free format isn’t quite as “free” as others. It most likely wants SELECT and a few other things to start where Area-B would have them when using fixed format.

Summary

While waiting for my next contract to come along I am now writing four different books.

  1. “Dream Recorder”
  2. “Twenty of Two – The Infamous They”
  3. “The Minimum You Need to Know About GnuCOBOL”
  4. “The Minimum You Need to Know About GUI Emacs”

Too many Emacs books focus on just the terminal. Yes, there are hundreds of “free” sites listing 20-30 commands, just enough to really get into trouble. Yes, there are probably a hundred or more Emacs books out there right now. Most focus on Emacs in a terminal or simply add credence to the old mantra

If Emacs had an editor it would be one Hell of an operating system!

Yeah, I’ve heard it, I’ve said it, and I’m living it again.

To those who say “How can you keep all of those books straight?” that’s easy. Work on one until you get tired or lose the spark, then work on another. I’ve done the one-book-for-an-entire-year thing. That was “The Minimum You Need to Know to Be an OpenVMS Application Developer.” It’s really hard to maintain a spark for that long. Taking a few weeks or months off to write about something else that interests you then coming back to an older piece is rejuvenating, or at least it is for me.

The GDPR and Linux Distros

dusting off hands imageIf you use a general purpose Linux distro which pulls from somewhere else (and they all pull from somewhere else) then you’ve experienced it when you tried to file a bug. Tickets file against Linux distros fall into two primary categories.

  1. Let it rot until it can be closed due to no longer supported version
  2. Flag it closed and tell the user to report it “upstream.”

Today’s rant is about the second category because it is how so many “maintainers” juice up their tickets closed count and because, you can’t do that anymore. Yes, I encountered it today when filing this bug report. If you visit the bug report you will see I was told to report it to Ubuntu because that is where they get the package. I politely informed them they had to report it. They proceeded to keep closing the bug.

Well, here is a cold water bath in reality. The GDPR has been law for a while. Every one of those “upstream” locations requires a user provide personal information to create an account before they can report a bug. I choose to use KDE Neon. The bug was discovered while using KDE Neon. I choose to give my information to the KDE Neon bug reporting system because I like the distro and wanted to report a bug which appears to be biting quite a few people in the writing world.

You cannot tell me to provide my personal information “upstream” which is what you are doing when you tell me to “report it upstream.” Every distro pulling something from somewhere now has to have their own method of forwarding bugs “upstream.” You cannot tell your users they have to provide their personal information to someone else. That’s my take on the GDPR.

Of course, you want to flag it “upstream” and dust off your hands, jacking up that closer rate. This was the norm before GDPR. It cannot be the norm now. Admittedly I live in America, but a large chunk of Linux users live in the EU which is where the GDPR became law in 2016. They will get around to knocking on your distro’s door. The kudos for doing nothing days are over.

 

Secret to G fonts in LibreOffice on KDE Neon

The list of people who have been burned by this grows daily and still it won’t get fixed in Ubuntu based distros. Work with Linux Libertine in LibreOffice because that used to be the default font. Finish writing your book or other work which needs to be sent out for editing. Send it out to an editor who uses LibreOffice but uses it on Windows. “No problem” you think. “I used a standard LibreOffice font.” Boy have you just gotten screwed!

I went through this little tale myself this past Sunday. Tuesday afternoon now and I can just now sit down for a while. Two hours on the phone trying to decipher why my 598 page book opened as a 900+ page document with bizarre page breaks on their machine.

In their infinite wisdom the boys and girls at Ubuntu (possibly all the way back to Debian itself) have decided to package “Linux Libertine O.” The LibreOffice package from libreoffice.org packages “Linux Libertine G.” Do you think the font selection logic could say “Hey! Only the last letter is different, use this one?” Noooo. So instead of looking like this:

how it should have looked

It looks like this:

how it actually looked

Not even close.

I ended up burning the entire day setting up VMs with various flavors of Windows and Ubuntu to test raw installs. I deliberately set up a Ubuntu Mate minimal installation so I wouldn’t get all of the extra baggage that could gunk up the works. After that I pulled down the Linux Libertine font file directly from linuxlibertine.org. In the Windows VM I installed 6.3.4 of LibreOffice directly from the LibreOffice Web site.

LibreOffice on Windows

Given the ordeal of the weekend, this is what I expected, the G flavor of the font families. Next I installed LibreOffice in the Ubuntu Mate 18.04 minimal VM.

LibreOffice on Ubuntu Mate

Perfect! Exactly what I expected! So. Move back to my KDE Neon host computer. I uninstall LibreOffice which was installed from the PPA to get a much later version. I use the font management software to manually uninstall all of the Linux Libertine fonts I had installed while troubleshooting the problem. Then I install Libre Office directly from the Web site and reboot. Guess what? No Linux Libertine fonts. (It’s possible I should have rebooted before installing, but still.)

Now I’m ripping my hair out because I don’t want to run in a VM I’ve allotted two CPUs while working on three different books. My editor is going to convert the one he is working on to Liberation Serif and Liberation Sans because thankfully that does match. I just have to worry about the other three.

Oh, I’ve been down a road much like this before. Some yahoo decided to remove FreeTimes from the t1-cyrillic package and I’ve got quite a few books in print which I wrote using that font as primary. &%*&^%*&^ing 12 year old boys being allowed to “maintain” a package for a distro! I used FreeTimes because it looked good and was available everywhere. Not anymore!

Yes, I searched. This has been a looong standing problem in the Ubuntu world. I even found this bug report.

Ubuntu Bug Report

Gotta love a “Won’t Fix” from 2014. Just let the bug rot until it is logged against an unsupported version and you can close it.

So, after much scrambling and diet Mt. Dew consumption I found where LibreOffice gets the font. I pull it down and extract it in my newly rebooted KDE Neon host. I use the font manager to install the font files and vola!

after font install

The only stupid thing I did was not leaving the “O” versions installed. Perhaps it is less stupid than I think right now? The font chooser won’t choose a visually matching font so it should be easy to find scrolling through. I can always use this hack to identify fonts in use. Then use this to search for the place they are used.

Now it is time to write one more blog post then work on those other three books.

 

 

 

Manually Adding a Disk to Linux

We all have to do it from time to time. I’m creating this post so I can stop looking for a trusted source. I don’t add a second or third hard drive to my KDE Neon (Linux) desktops often enough to commit it to memory, and let’s face it, things have changed over the years.

I will assume you already have physically installed your drive and formatted it the way you want.

roland@roland-HP-EliteDesk-800-G2-SFF:~$ sudo mkdir /media/Data
roland@roland-HP-EliteDesk-800-G2-SFF:~$ sudo chown roland:roland /media/Data
roland@roland-HP-EliteDesk-800-G2-SFF:~$ sudo chmod +rw /media/Data
roland@roland-HP-EliteDesk-800-G2-SFF:~$ sudo apt-get install libblkid1

 

Years ago we used to create the mount point under /mnt. Today the preferred method is under /media. Come on, admit it. If you are an old timer you’ve even created the mount point at root. I labeled my drive “Data” when I formatted it to ext4 so I created the mount point as /media/Data.

Technically the chown command is
sudo chown [user]:[group] /media/Data

I wanted the drive to be owned by me because eventually I will be using the entire thing for a PostgreSQL database as part of another post series I really am working. I know, life and clients end many of my coding post series early. Either that or they get so involved I turn them into a book in my The Minimum You Need to Know book series. You also need to turn on Read Write privs which what the chmod command does.

The next thing you need to do is install libblkid1. Yes, there are other ways to identify the UUID but this utility is the current trend.

roland@roland-HP-EliteDesk-800-G2-SFF:~$ sudo blkid
/dev/loop0: TYPE="squashfs"
/dev/loop1: TYPE="squashfs"
/dev/loop2: TYPE="squashfs"
/dev/loop3: TYPE="squashfs"
/dev/loop4: TYPE="squashfs"
/dev/loop5: TYPE="squashfs"
/dev/loop6: TYPE="squashfs"
/dev/loop7: TYPE="squashfs"
/dev/sda1: UUID="DE4D-6645" TYPE="vfat" PARTLABEL="EFI System Partition" PARTUUID="1069be1a-bbde-4bef-97b0-657f14575d37"
/dev/sda2: UUID="9a3dc19b-fee9-4575-8d06-c77b00eb800c" TYPE="ext4" PARTUUID="c4cd47b7-2cb6-45f8-a546-828253162291"
/dev/sdb1: PARTLABEL="Microsoft reserved partition" PARTUUID="eae2381f-c7fc-45ab-952f-7af47a1535f5"
/dev/sdb2: LABEL="Seagate Backup Plus Drive" UUID="50C02DC2C02DAF62" TYPE="ntfs" PTTYPE="atari" PARTLABEL="Basic data partition" PARTUUID="c39930ea-d9d0-4956-b196-100b6e95bc3c"
/dev/sdc1: LABEL="Data" UUID="f1848754-cf08-4553-bf8e-33ec61693eab" TYPE="ext4" PARTUUID="1de73a93-bef6-3243-8419-ce262b3ca58f"
/dev/loop8: TYPE="squashfs"
/dev/loop9: TYPE="squashfs"
/dev/loop10: TYPE="squashfs"
/dev/loop11: TYPE="squashfs"
/dev/loop12: TYPE="squashfs"
/dev/loop13: TYPE="squashfs"
/dev/loop14: TYPE="squashfs"
/dev/loop15: TYPE="squashfs"
/dev/loop16: TYPE="squashfs"
/dev/loop17: TYPE="squashfs"
/dev/loop18: TYPE="squashfs"
/dev/loop19: TYPE="squashfs"
/dev/loop20: TYPE="squashfs"

You can see my system currently identifies the disk as /dev/sdc1. Years ago we used to use /dev/sdc1 when creating entries in /etc/fstab. You can tell from the labels that the drive identified as /dev/sdb is my external Seagate Backup Plus Drive that is connected via USB3. Given that removable drives are now part of the standard device path most Linux distros have migrated to UUID values. A firmware update or a missing external drive used to play Hell with configurations. If I remove my external drive and reboot my new internal drive will end up as /dev/sdb and that would hose a manually edited /etc/fstab.

roland@roland-HP-EliteDesk-800-G2-SFF:~$ sudo jed /etc/fstab

 

You can see how I added the line to the end of /etc/fstab

To make things easier for you here it is:

UUID=f1848754-cf08-4553-bf8e-33ec61693eab /media/Data auto rw,user,auto 0 0

 

In theory you can just mount it now. Best to test with a reboot though. When you rebooted after installation, especially with KDE Neon, Dolphin will have identified the drive and created an entry in itself based on the label. The auto qualifier will cause the drive to mount at boot. The first auto qualifier is for the format. I could have chosen to use ext4 because that is how I formatted the drive. This is better only because it is a big spare drive I will use for multiple projects which may require reformatting from time to time.

Honestly, even after reboot I had to go back in and do the following

As a final test I used Dolphin to create a directory

Qt and USB – Pt. 7

generic usb ttl uartsIt took quite a bit of poking around to get as far as I am. Doing a whole bunch of training at a new client site and coming home with a brain of mush doesn’t help either. Adding insult to injury it seems that 90% of the “working code” posted on Stack Exchange is non-functioning feces which won’t even compile. When you need grown up answers, don’t go to Stack Exchange. It’s a place for 12 year old boys, not professional developers. When you ask a grown-up development question on there you get one of those little nasty grams from the admin along the lines of

some of the recent questions you’ve asked weren’t well received and didn’t get many views, you are in danger of losing the privilege to ask questions.

They prefer questions about stuff which exists in 60 different C/C++ books where 12 year old boys can Google an answer in under 30 seconds.

If you look carefully you will notice that I swapped the wire connections, effectively creating a null modem between the two USB UART bridges. At this point you can ls /dev to find out what ttyUSB devices exist, install the Linux screen program and with a line something like this,

screen /dev/ttyUSB0 19200 vt100

in two different terminal windows, one for each device, then type in one and see it appear in the other. Depending on your Linux distro you may need sudo in front of that or some other “run as root” qualifier.

Since I really hate unwashed rectal sphincters who post stuff like this and only pay lip service to the environment, here is a bit for you.

Latest KDE Neon based on Ubuntu 18.04 with all updates applied.

#!/bin/bash 
# additional dependancies for building Qt from scratch 
#  

sudo apt-get -f --assume-yes install g++ libgl1-mesa-dev libx11-dev \ 
libxss-dev git devscripts chrpath pax-utils squashfs-tools \ 
genisoimage curl whois synaptic  

#libgl1-mesa-glx kpat gnome-calculator 

sudo apt-get -f --assume-yes install bison build-essential gperf \ 
flex ruby python libasound2-dev libbz2-dev libcap-dev \ 
libcups2-dev libdrm-dev libegl1-mesa-dev libgcrypt11-dev libnss3-dev \ 
libpci-dev libpulse-dev libudev-dev \ 
libxtst-dev gyp meld 

# syslinux-utils 

sudo apt-get -f --assume-yes install libssl-dev libxcursor-dev \ 
libxcomposite-dev \ 
libxdamage-dev libxrandr-dev \ 
libfontconfig1-dev libxss-dev libcap-dev libxi-dev libxtst-dev re2c \ 
libdbus-1-dev libpci-dev libudev-dev \ 
gconf-2.0 gconf2 libgnome-keyring-dev libgnome-keyring-dev python-gtk2-dev \ 
libsnappy-dev  chromium-browser-l10n chromium-browser \ 
chromium-codecs-ffmpeg-extra  

# libre2-dev snapcraft 

-----

#!/bin/bash
# additional dependancies for building Qt from scratch on Ubuntu 18.04
#  

sudo apt-get -f --assume-yes install libssl1.0-dev

#!/bin/bash
# install editors and terminals
#  

sudo apt-get -f --assume-yes install bluefish jed \
qtcreator


I used synaptic to install the following:

roland@roland-HP-Compaq-Elite-8300-SFF:~/2019-analytics/doc$ sudo apt list --installed | grep libusb 
[sudo] password for roland:  

WARNING: apt does not have a stable CLI interface. Use with caution in scripts. 

libusb-1.0-0/bionic,now 2:1.0.21-2 amd64 [installed] 
libusb-1.0-0-dev/bionic,now 2:1.0.21-2 amd64 [installed]



roland@roland-HP-Compaq-Elite-8300-SFF:~$ sudo apt list --installed | grep codelite 

WARNING: apt does not have a stable CLI interface. Use with caution in scripts. 

codelite/bionic,now 10.0+dfsg-2 amd64 [installed] 
codelite-plugins/bionic,now 10.0+dfsg-2 amd64 [installed]

Yes, I could have used something besides CodeLite to do my development with, and so can you. When I started this I was on regular Ubuntu on a different machine so something like KDevelop would have forced installing about half (or so it seems like watching it install) of KDE. Yes, CodeLite has things which annoy me. The biggest annoyance is, unlike QtCreator, I can’t find a way to configure it to automatically save all modified source before performing a build and debug run.

testing uart screen shot

On the left is the terminal window created by running the program. On the right is the “screen” instance I tested against. The screen image has more than one test on it.

I have no idea what is causing the -9 errors trying to set up the device. I have rebooted multiple times, unplugged the devices and plugged them back in. This pipe error does not always occur. For an entire afternoon it didn’t happen no matter how many test runs I did. It also doesn’t seem to matter because the previously successful configuration either remains in place or is simply the default.

To be able to debug without creating udev rules I start CodeLite under sudo from a terminal. I chose not to do this with QtCreator because that used to really cock-up the environment so when you tried to run QtCreator as a mere mortal you got all kinds of protection errors. Never noticed that with CodeLite. Not saying it couldn’t happen, just saying I never noticed it.

Yes, some of the pieces of this code will look like like the hundred other “examples” you can find around the Web. The important parts aren’t. We will talk more about this code in another post.

 

/*
 * usb-test
 * 
 * Written by Roland Hughes as a stripped down test of basic libusb 1.0
 * communication. It is a very cleaned up version of an example found here:
 * https://stackoverflow.com/questions/14769107/libusb-bulk-transfer
 * 
 */
#include 
#include 
#include <sys/types.h>
#include 
#include 
#include <libusb-1.0/libusb.h>
#include 

#pragma pack(1)
struct CONTROL_MESSAGE
{
    struct libusb_control_setup ctrl_setup;
    uint32_t data;
};
#pragma pack()

const uint8_t IFC_ENABLE = 0X00;
const uint8_t SET_LINE_CTL = 0x03;
const uint8_t PURGE = 0x12;
const uint8_t SET_FLOW = 0x13;
const uint8_t SET_BAUDRATE = 0x1E;

/* 
 * Cygnal Integrated Products, Inc. CP210x
 * 
 *  idVendor           0x10c4 Cygnal Integrated Products, Inc.
 *  idProduct          0xea60 CP210x UART Bridge / myAVR mySmartUSB light
 *
 *       Endpoint Descriptor:
 *       bLength                 7
 *       bDescriptorType         5
 *       bEndpointAddress     0x81  EP 1 IN
 *       bmAttributes            2
 *         Transfer Type            Bulk
 *         Synch Type               None
 *         Usage Type               Data
 *       wMaxPacketSize     0x0040  1x 64 bytes
 * 
 *       Endpoint Descriptor:
 *       bLength                 7
 *       bDescriptorType         5
 *       bEndpointAddress     0x01  EP 1 OUT
 *       bmAttributes            2
 *         Transfer Type            Bulk
 *         Synch Type               None
 *         Usage Type               Data
 *       wMaxPacketSize     0x0040  1x 64 bytes
 *
 */
 
 /* 
  * Prolific 
  * Prolific Technology, Inc. PL2303 Serial Port
  * 
  *  idVendor           0x067b Prolific Technology, Inc.
  *  idProduct          0x2303 PL2303 Serial Port
  *
  *      bEndpointAddress     0x02  EP 2 OUT
  *      bmAttributes            2
  *        Transfer Type            Bulk
  *        Synch Type               None
  *        Usage Type               Data
  *      wMaxPacketSize     0x0040  1x 64 bytes
  *
  *      bEndpointAddress     0x83  EP 3 IN
  *      bmAttributes            2
  *        Transfer Type            Bulk
  *        Synch Type               None
  *        Usage Type               Data
  *      wMaxPacketSize     0x0040  1x 64 bytes
  *
  */
#define VENDOR_ID       0x10c4
#define PRODUCT_ID      0xea60

//  The OUT EP is where you write your output to the device
//
#define BULK_EP_OUT     0x01
#define OUT_PACKET_SIZE 0x0040

//  The IN EP is where YOU read FROM. Your device input comes from here
//
#define BULK_EP_IN      0x81
#define IN_PACKET_SIZE  0x0040

int interface_ref = 0;
int alt_interface,interface_number;

int print_configuration(struct libusb_device_handle *hDevice,
                        struct libusb_config_descriptor *config)
{
    char *data;
    int index;

    data = malloc(512);
    memset(data,0,512);

    index = config->iConfiguration;

    libusb_get_string_descriptor_ascii(hDevice,index,(unsigned char *)data,512);

    printf("\nInterface Descriptors: ");
    printf("\n\tNumber of Interfaces : %d",config->bNumInterfaces);
    printf("\n\tLength : %d",config->bLength);
    printf("\n\tDesc_Type : %d",config->bDescriptorType);
    printf("\n\tConfig_index : %d",config->iConfiguration);
    printf("\n\tTotal length : %d",config->wTotalLength);
    printf("\n\tConfiguration Value  : %d",config->bConfigurationValue);
    printf("\n\tConfiguration Attributes : %d",config->bmAttributes);
    printf("\n\tMaxPower(mA) : %d\n",config->MaxPower);

    free(data);
    data = NULL;
    return 0;
}

struct libusb_endpoint_descriptor* active_config(struct libusb_device *dev,struct libusb_device_handle *handle)
{
    struct libusb_device_handle *hDevice_req;
    struct libusb_config_descriptor *config;
    struct libusb_endpoint_descriptor *endpoint;
    int altsetting_index,interface_index=0;

    hDevice_req = handle;

    libusb_get_active_config_descriptor(dev,&config);
    print_configuration(hDevice_req,config);

    for(interface_index=0;interface_indexbNumInterfaces;interface_index++)
    {
        const struct libusb_interface *iface = &config->interface[interface_index];
        for(altsetting_index=0;altsetting_indexnum_altsetting;altsetting_index++)
        {
            const struct libusb_interface_descriptor *altsetting = &iface->altsetting[altsetting_index];

            int endpoint_index;
            for(endpoint_index=0;endpoint_indexbNumEndpoints;endpoint_index++)
            {
                endpoint = (struct libusb_endpoint_descriptor*)&altsetting->endpoint[endpoint_index];
                alt_interface = altsetting->bAlternateSetting;
                interface_number = altsetting->bInterfaceNumber;
            }

            printf("\nEndPoint Descriptors: ");
            printf("\n\tSize of EndPoint Descriptor : %d",endpoint->bLength);
            printf("\n\tType of Descriptor : %d",endpoint->bDescriptorType);
            printf("\n\tEndpoint Address : 0x0%x",endpoint->bEndpointAddress);
            printf("\n\tMaximum Packet Size: %x",endpoint->wMaxPacketSize);
            printf("\n\tAttributes applied to Endpoint: %d",endpoint->bmAttributes);
            printf("\n\tInterval for Polling for data Tranfer : %d\n",endpoint->bInterval);
        }
    }
    libusb_free_config_descriptor(NULL);
    return endpoint;
}

void init_and_set_protocol( libusb_device_handle *handle, uint32_t baud, 
                            uint8_t data_bits, uint8_t parity, uint8_t stop_bits)
{
    struct CONTROL_MESSAGE ctrl_msg;
    int e = 0;
    int length = sizeof(ctrl_msg);

    // purge any activity still on the device
    //
    ctrl_msg.data = 0;
    libusb_fill_control_setup((unsigned char *)&ctrl_msg, 0b01000001, PURGE, 0b00001111, 1, 0);
    
    e = libusb_control_transfer(handle, ctrl_msg.ctrl_setup.bmRequestType,
                                ctrl_msg.ctrl_setup.bRequest,
                                ctrl_msg.ctrl_setup.wValue,
                                ctrl_msg.ctrl_setup.wIndex,
                                (unsigned char *)&ctrl_msg.data,
                                length, 5000);
    
    if(e > 0)
    {
        printf("\nPurge successful!");
    }
    else
    {
        printf("\nError purging device e = %d \n",e);
        printf("message: %s\n", libusb_strerror(e));
    }
    
    ctrl_msg.data = 0;
    libusb_fill_control_setup((unsigned char *)&ctrl_msg, 0b01000001, IFC_ENABLE, 1, 1, 0);
    
    e = libusb_control_transfer(handle, ctrl_msg.ctrl_setup.bmRequestType,
                                ctrl_msg.ctrl_setup.bRequest,
                                ctrl_msg.ctrl_setup.wValue,
                                ctrl_msg.ctrl_setup.wIndex,
                                (unsigned char *)&ctrl_msg.data,
                                LIBUSB_CONTROL_SETUP_SIZE, 5000);
    
    if(e > 0)
    {
        printf("\nEnable successful!");
    }
    else
    {
        printf("\nError Enabling device e = %d \n",e);
        printf("message: %s\n", libusb_strerror(e));
    }

    //  SET_LINE_CONTROL
    //
    #pragma pack(1)
    union
    {
        struct LINE_CONTROL
        {
            uint8_t stop_bits   : 4;
            uint8_t parity      : 4;
            uint8_t word_length;
        } line_control;
        uint16_t wValue;
    } ctrl;
    #pragma pack()
    
    //  Yes, a production system would validate these values against a legal
    //  range, but this is not production.
    //
    ctrl.line_control.stop_bits = stop_bits;
    ctrl.line_control.parity = parity;
    ctrl.line_control.word_length = data_bits;
    ctrl_msg.data = 0;
    libusb_fill_control_setup((unsigned char *)&ctrl_msg, 0b01000001, SET_LINE_CTL, ctrl.wValue, 1, 0);
    e = libusb_control_transfer(handle, ctrl_msg.ctrl_setup.bmRequestType,
                                ctrl_msg.ctrl_setup.bRequest,
                                ctrl_msg.ctrl_setup.wValue,
                                ctrl_msg.ctrl_setup.wIndex,
                                (unsigned char *)&ctrl_msg.data,
                                LIBUSB_CONTROL_SETUP_SIZE, 5000);
    
    if(e == length)
    {
        printf("\nLine control set to %d %d %d\n", data_bits, stop_bits, parity);
    }
    else
    {
        printf("\nError setting line control  e = %d \n",e);
        printf("message: %s\n", libusb_strerror(e));
    }
    
    ctrl_msg.data = baud;
    libusb_fill_control_setup((unsigned char *)&ctrl_msg, 0b01000001, SET_BAUDRATE, 0, 1, 4);
        
    e = libusb_control_transfer(handle, ctrl_msg.ctrl_setup.bmRequestType,
                                ctrl_msg.ctrl_setup.bRequest,
                                ctrl_msg.ctrl_setup.wValue,
                                ctrl_msg.ctrl_setup.wIndex,
                                (unsigned char *)&ctrl_msg.data,
                                length, 5000);
    
    if(e == length)
    {
        printf("\nBaud rate set to %d\n", baud);
    }
    else
    {
        printf("\nError setting baud rate  e = %d \n",e);
        printf("message: %s\n", libusb_strerror(e));
    }
}

int check_for_exit( char *txt)
{
    int ret_val = 0;
    
    if (txt != NULL)
    {
        // This is why everybody moved to C++. It's 2019 and we __still__
        // have to do left handed things to search a string in a case
        // insensitive manner.
        //
        char *sPtr = txt;
        while( *sPtr != '\0' )
        {
            *sPtr = toupper( ( unsigned char ) *sPtr );
            sPtr++;
        }

        if (strstr(txt, "EXIT") != NULL)
        {
            ret_val = 1;
        }
    }
    
    return ret_val;
}

int main(void)
{
    int r = 1;
    libusb_device **devs;
    libusb_device_handle *handle = NULL, *hDevice_expected = NULL;
    libusb_device *dev,*dev_expected;

    ssize_t cnt;
    int e = 0,config2;
    int i = 0;
    unsigned char str1[2048], str2[2048];
    char found = 0;

// Init libusb
    r = libusb_init(NULL);
    if(r < 0)
    {
        printf("\nfailed to initialise libusb\n");
        return 1;
    }
    else
    {
        printf("\nInit Successful!\n");
        
    }
    
    libusb_set_debug(NULL, LIBUSB_LOG_LEVEL_WARNING);

// Get a list os USB devices
    cnt = libusb_get_device_list(NULL, &devs);
    if (cnt < 0)
    {
        printf("\nThere are no USB devices on bus\n");
        return -1;
    }
    printf("\nDevice Count : %ld\n-------------------------------\n",cnt);

    while ((dev = devs[i++]) != NULL  &&  !found)
    {
        struct libusb_device_descriptor desc = {0};
        r = libusb_get_device_descriptor(dev, &desc);
        if (r < 0)
            {
            printf("failed to get device descriptor\n");
            libusb_free_device_list(devs,1);
            libusb_exit(NULL);
            break;
        }

        e = libusb_open(dev,&handle);
        if (e < 0) { printf("error opening device\n"); libusb_free_device_list(devs,1); libusb_exit(NULL); break; } printf("\nDevice Descriptors: "); printf("\nManufacturer index: %d", desc.iManufacturer); printf("\n\tVendor ID : %x",desc.idVendor); printf("\n\tProduct ID : %x",desc.idProduct); printf("\n\tSerial Number : %x",desc.iSerialNumber); printf("\n\tSize of Device Descriptor : %d",desc.bLength); printf("\n\tType of Descriptor : %d",desc.bDescriptorType); printf("\n\tUSB Specification Release Number : %d",desc.bcdUSB); printf("\n\tDevice Release Number : %d",desc.bcdDevice); printf("\n\tDevice Class : %d",desc.bDeviceClass); printf("\n\tDevice Sub-Class : %d",desc.bDeviceSubClass); printf("\n\tDevice Protocol : %d",desc.bDeviceProtocol); printf("\n\tMax. Packet Size : %d",desc.bMaxPacketSize0); printf("\n\tNo. of Configuraions : %d\n",desc.bNumConfigurations); e = libusb_get_string_descriptor_ascii(handle, desc.iManufacturer, (unsigned char*) str1, sizeof(str1)); if (e > 0)
        {
            printf("\nManufactured : %s",str1);
        }
        

        e = libusb_get_string_descriptor_ascii(handle, desc.iProduct, (unsigned char*) str2, sizeof(str2));
        if(e > 0)
        {
            printf("\nProduct : %s",str2);
        }

        printf("\n----------------------------------------");

        if(desc.idVendor == VENDOR_ID && desc.idProduct == PRODUCT_ID)
        {
            found = 1;
            break;
        }
        
        libusb_close(handle);      // only leave open the last one.
    }//end of while

    
    if(found == 0)
    {
        printf("\nDevice NOT found\n");
        libusb_free_device_list(devs,1);
        libusb_close(handle);
        libusb_exit(NULL);
        return EXIT_FAILURE;
    }
    else
    {
        printf("\nDevice found");
        dev_expected = dev;
        hDevice_expected = handle;
    }

    e = libusb_get_configuration(handle,&config2);
    if(e!=0)
    {
        printf("\n***Error in libusb_get_configuration\n");
        libusb_free_device_list(devs,1);
        libusb_close(handle);
        libusb_exit(NULL);
        return EXIT_FAILURE;
    }
    printf("\nConfigured value : %d",config2);

    if(config2 != 1)
    {
        libusb_set_configuration(handle, 1);
        if(e!=0)
        {
            printf("Error in libusb_set_configuration  %s\n", libusb_strerror(e));
            libusb_free_device_list(devs,1);
            libusb_close(handle);
            libusb_exit(NULL);
            return EXIT_FAILURE;
        }
        else
        {
            printf("\nDevice is in configured state!");
        }
    }

    libusb_free_device_list(devs, 1);

    int reAttacheDriver = 0;
    if(libusb_kernel_driver_active(handle, 0) == 1)
    {
        printf("\nKernel Driver Active");
        if(libusb_detach_kernel_driver(handle, 0) == 0)
        {
            reAttacheDriver = 1;
            printf("\nKernel Driver Detached!");
        }
        else
        {
            printf("\nCouldn't detach kernel driver!\n");
            libusb_free_device_list(devs,1);
            libusb_close(handle);
            return -1;
        }
    }

    e = libusb_claim_interface(handle, 0);
    if(e < 0)
    {
        printf("\nCannot Claim Interface");
        printf("\nMessage: %s", libusb_strerror(e));
        if (reAttacheDriver)
        {
            libusb_attach_kernel_driver(handle, 0);
        }
        libusb_free_device_list(devs,1);
        libusb_close(handle);
        libusb_exit(NULL);
        return -1;
    }
    else
    {
        printf("\nClaimed Interface\n");
    }

    active_config(dev_expected,hDevice_expected);
    printf("returned from active_config\n");
    //   Communicate
    
    init_and_set_protocol( handle, 19200, 8, 0, 1);

    char *my_string, *my_string1;
    int transferred = 0;
    int received = 0;
    int length = 0;

    puts("creating strings");
    my_string = (char *)malloc(OUT_PACKET_SIZE + 1);
    my_string1 = (char *)malloc(IN_PACKET_SIZE + 1);

    memset(my_string,'\0', OUT_PACKET_SIZE+1);
    memset(my_string1,'\0', IN_PACKET_SIZE+1);

    strcpy(my_string,"Roland Hughes\n");
    length = strlen(my_string);

    printf("\nTo be sent : %s\n",my_string);

    e = libusb_bulk_transfer(handle, (BULK_EP_OUT | LIBUSB_ENDPOINT_OUT),
            (unsigned char *)my_string,length,&transferred,5000);  // last param is timeout in ms
    if(e == 0 && transferred == length)
    {
        printf("\nWrite successful!");
        printf("\nSent %d bytes with string: %s\n", transferred, my_string);
    }
    else
    {
        printf("\nError in write! e = %d and transferred = %d\n",e,transferred);
        printf("message: %s\n", libusb_strerror(e));
    }

    int retry_count = 0;
    int exit_flag = 0;
    int rcvLength = IN_PACKET_SIZE;
    char current_line[2048];
    unsigned char new_line[] = {'\r', '\n', '\0'};
    unsigned char next_char[] = { ' ', '\0'};
    
    memset(current_line, '\0', sizeof(current_line));
    
    while( exit_flag == 0)
    {
        memset(my_string1, '\0', IN_PACKET_SIZE+1);
        e = libusb_bulk_transfer(handle, (BULK_EP_IN | LIBUSB_ENDPOINT_IN), 
                (unsigned char *)my_string1,rcvLength,&received,5000);
        if(e == 0)
        {
            for (int i=0; i < strlen(my_string1); i++) { if (my_string1[i] == '\r') { exit_flag = check_for_exit(current_line); printf("\n"); e = libusb_bulk_transfer(handle, (BULK_EP_OUT | LIBUSB_ENDPOINT_OUT), new_line,2,&transferred,5000); memset(current_line, '\0', sizeof(current_line)); } else { next_char[0] = my_string1[i]; putchar(my_string1[i]); e = libusb_bulk_transfer(handle, (BULK_EP_OUT | LIBUSB_ENDPOINT_OUT), next_char,1,&transferred,5000); strcat(current_line, (char *)next_char); } } fflush(stdout); } else { retry_count++; printf("retry_count %d\n", retry_count); if (retry_count > 10)
            {
                printf("retry_count %d\n", retry_count);
                exit_flag = 1;
            }
        }
    }

    //  Disable the device
    struct CONTROL_MESSAGE ctrl_msg;
    length = sizeof(ctrl_msg);
    
    ctrl_msg.data = 0;
    libusb_fill_control_setup((unsigned char *)&ctrl_msg, 0b01000001, IFC_ENABLE, 0, 1, 0);
    
    e = libusb_control_transfer(handle, ctrl_msg.ctrl_setup.bmRequestType,
                                ctrl_msg.ctrl_setup.bRequest,
                                ctrl_msg.ctrl_setup.wValue,
                                ctrl_msg.ctrl_setup.wIndex,
                                (unsigned char *)&ctrl_msg.data,
                                length, 5000);
    
    if(e == length)
    {
        printf("\nDisable successful!");
    }

    if (reAttacheDriver)
    {
        e = libusb_attach_kernel_driver(handle, 0);
        printf("Result of reattaching kernel driver: %d  msg: %s", e, libusb_strerror(e));
    }

    e = libusb_release_interface(handle, 0);

    libusb_close(handle);
    libusb_exit(NULL);

    printf("Hit return to continue\n");
    getchar();
    return 0;
}