Device driver classification - c

So according to 2 books I have on device drivers(for Linux), notable the O'REILLY version("Linux Device Drivers(3rd edition") there are 3 types of device drivers:
Character drivers that read/write on byte of data at a time.
Block drivers which read and write blocks of data(file storage devices).
Network drivers that send and receive frames(for ethernet and wifi these are 48 bit chunks of data).
So where do video and sound drivers fit in? I ask because AFAIK your screen has a bus that sends frames from the motherboard to the monitor and the image on your screen is a memory map(a square array) that is refreshed at a certain number of frames/sec. The sound driver also sends 16-bit(2 byte) frames to the DAC at a sampling rate of 44.1 KHz. Are these character drivers? I would think they would be in a similar category as network drivers as network interfaces, screens, speakers, microphones, and webcams are all devices that read and write frames(an array of bytes).

A more accurate way of describing these types of devices would be to say that:
Character devices deal with streams of data. You can write data to them and read data from them, but you can't skip around. Reading data from them may block if nothing is available. Common character devices include terminals, serial ports, and special devices like /dev/null and /dev/random. This need not be one byte at a time, though: many devices return data as larger frames. (For instance, Linux input devices under /dev/input return data in 32-byte frames, each one representing one event.)
Block devices deal with a fixed block of data. You can read and write data to any location in them you want, as well as mapping them to memory using mmap(). They are often used to represent storage devices (like disks), but can also be used to represent other, more unusual things.
Network devices are a special case. They are more or less exclusively used for devices that actually interface with a network (e.g, Ethernet NICs, wireless network hardware, cell modems, etc). They don't show up in /dev at all.
Both character and block devices can implement special operations that don't fit into the normal framework using ioctl() (e.g, changing the baud rate of a serial port, ejecting a CD, etc), so, to some degree, the decision of which one to use can be somewhat arbitrary. Nevertheless, for the types of devices you're describing:
Audio devices are typically represented as character devices.
The older OSS Linux sound system represented a sound device as /dev/dsp: reading data from it would read PCM data representing microphone input, and writing PCM data to it would play that through the sound card. The more modern ALSA sound system uses a more complex set of devices and formats, but the concept is the same.
Video devices are an unusual situation.
Some simple "framebuffer" video drivers are implemented as a block device such as /dev/fb0. This device represents the image on screen — a 1024x768 screen using 32-bit color would be represented by a 3 MB buffer, for instance — and writing to it changes that image. It's most commonly used by mapping it into memory.
Most modern video devices aren't that simple, though. Many of them are implemented partially as a kernel driver and partially as a X11 device driver which may directly map and write to the video hardware. They may sometimes include an extra character or block device (e.g, some Nvidia graphics cards use a character device /dev/nvidia), but the details of what data is read from or written to this device are largely proprietary.

They are character drivers.
gfx http://www.x.org/wiki/Development/Documentation/HowVideoCardsWork/
audio http://www.alsa-project.org/~tiwai/writing-an-alsa-driver/

Related

Linux UART imx8 how to quickly detect frame end?

I have an imx8 module running Linux on my PCB and i would like some tips or pointers on how to modify the UART driver to allow me to be able to detect the end of frame very quickly (less than 2ms) from my user space C application. The UART frame does not have any specific ending character or frame length. The standard VTIME of 100ms is much too long
I am reading from a Sim card, i have no control over the data, no control over the size or content of the data. I just need to detect the end of frame very quickly. The frame could be 3 bytes or 500. The SIM card reacts to data that it receives, typically I send it a couple of bytes and then it will respond a couple of ms later with an uninterrupted string of bytes of unknown length. I am using an iMX8MP
I thought about using the IDLE interrupt to detect the frame end. Turn it on when any byte is received and off once the idle interrupt fires. How can I propagate this signal back to user space? Or is there an existing method to do this?
Waiting for an "idle" is a poor way to do this.
Use termios to set raw mode with VTIME of 0 and VMIN of 1. This will allow the userspace app to get control as soon as a single byte arrives. See:
How to read serial with interrupt serial?
How do I use termios.h to configure a serial port to pass raw bytes?
How to open a tty device in noncanonical mode on Linux using .NET Core
But, you need a "protocol" of sorts, so you can know how much to read to get a complete packet. You prefix all data with a struct that has (e.g.) A type and a payload length. Then, you send "payload length" bytes. The receiver gets/reads that fixed length struct and then reads the payload which is "payload length" bytes long. This struct is always sent (in both directions).
See my answer: thread function doesn't terminate until Enter is pressed for a working example.
What you have/need is similar to doing socket programming using a stream socket except that the lower level is the UART rather than an actual socket.
My example code uses sockets, but if you change the low level to open your uart in raw mode (as above), it will be very similar.
UPDATE:
How quickly after the frame finished would i have the data at the application level? When I try to read my random length frames currently reading in 512 byte chunks, it will sometimes read all the frame in one go, other times it reads the frame broken up into chunks. –
Engo
In my link, in the last code block, there is an xrecv function. It shows how to read partial data that comes in chunks.
That is what you'll need to do.
Things missing from your post:
You didn't post which imx8 board/configuration you have. And, which SIM card you have (the protocols are card specific).
And, you didn't post your other code [or any code] that drives the device and illustrates the problem.
How much time must pass without receiving a byte before the [uart] device is "idle"? That is, (e.g.) the device sends 100 bytes and is then finished. How many byte times does one wait before considering the device to be "idle"?
What speed is the UART running at?
A thorough description of the device, its capabilities, and how you intend to use it.
A uart device doesn't have an "idle" interrupt. From some imx8 docs, the DMA device may have an "idle" interrupt and the uart can be driven by the DMA controller.
But, I looked at some of the linux kernel imx8 device drivers, and, AFAICT, the idle interrupt isn't supported.
I need to read everything in one go and get this data within a few hundred microseconds.
Based on the scheduling granularity, it may not be possible to guarantee that a process runs in a given amount of time.
It is possible to help this a bit. You can change the process to use the R/T scheduler (e.g. SCHED_FIFO). Also, you can use sched_setaffinity to lock the process to a given CPU core. There is a corresponding call to lock IRQ interrupts to a given CPU core.
I assume that the SIM card acts like a [passive] device (like a disk). That is, you send it a command, and it sends back a response or does a transfer.
Based on what command you give it, you should know how many bytes it will send back. Or, it should tell you how many optional bytes it will send (similar to the struct in my link).
The method you've described (e.g.) wait for idle, then "race" to get/process the data [for which you don't know the length] is fraught with problems.
Even if you could get it to work, it will be unreliable. At some point, system activity will be just high enough to delay wakeup of your process and you'll miss the window.
If you're reading data, why must you process the data within a fixed period of time (e.g. 100 us)? What happens if you don't? Does the device catch fire?
Without more specific information, there are probably other ways to do this.
I've programmed such systems before that relied on data races. They were unreliable. Either missing data. Or, for some motor control applications, device lockup. The remedy was to redesign things so that there was some positive/definitive way to communicate that was tolerant of delays.
Otherwise, I think you've "fallen in love" with "idle interrupt" idea, making this an XY problem: https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem

Enumerating capture ALSA devices and capture from them

I am writing a C program where I would like to enumerate all the capture devices in my system (in practice, I know I have three webcams plus the "integrated" microphone), recognize them and start capturing from them all at the same time.
I has some success using snd_device_name_hint() to enumerate all PCM devices and then snd_device_name_get_hint() to determine the "IOID" to see if they support capture. But now, how do I open the related device with snd_pcm_open() so that I can capture? I would like to use the "hw" interface as I do not want to overload the system with many conversion rates, so I would also like to see if there is a method to report the HW supported sampling frequencies.
Thank you!
snd_device_name_hint() can return multiple device names for the same hardware device (e.g., plughw and hw).
It can also returns devices that do not correspond to a single hardware device (such as null, or PulseAudio/Jack/Bluetooth devices).
To enumerate hardware devices, call snd_card_next() and snd_ctl_pcm_next_device() (see the aplay source code for an example).
To check whether a sample rate is supported, call snd_pcm_hw_params_test_rate().

How to access my 9-data bit serial port on a PC?

I have developed an embedded solution which communicates over a Multi Drop Bus and now I would like to develop a PC based application which monitors traffic on the bus.
MDB supports true 9 data bits (plus start/stop/parity - and *no fudging* by using the parity bit as a 9th data bit) whereas standard Windows and Linux libraries offer a maximum of 8 data bits.
I have a StarTech PCI2S950 PC serial port card which supports 9-data bits, but am not sure how to code my monitoring app & have googled a lot to no great avail.
I would prefer to code in C (or Delphi, or C++). I have a slight preference for Cygwn, but am willing to use straightforward Windows or Linux.
Just anything to read/write 9 data bits over that PC serial port card.
Can anyone help?
The document at http://www.semiconductorstore.com/pdf/newsite/oxford/ox16c950b.pdf describes the differences between various UARTs.
While your StarTech board includes the 16C950, which is RS-485 (and 9-bit) capable, it uses it in RS-232 compatible (550) mode, similar to 16550/8250 from IBM-PC days, and supports max 8 bit data.
You need a board with the same chip (16C950) but that exposes the RS-485 compatible 950 mode that supports 9 bit data as per the spec. And any board claiming such support would have to come with custom drivers for Windows, since Microsoft's is 8 bit only.
There are several other chips that can do 9-bit RS-485 mentioned here but again finding Windows driver support will be tricky. And, of course, many boards use 16C950 but only in 8-bit and/or RS-232 only mode, and without appropriate drivers.
In answer to your related question on Superuser, sawdust suggested the Sealevel 7205e, which looks like a good choice, with Windows driver support. It is pricey but they specifically mention 9-bit, RS-485 support, and Windows drivers. It may well be your best option.
The card you selected is not suitable for this application. It has just plain RS-232 ports, it is not suitable for a multi-drop bus. You'll need to shop elsewhere for an EIA-485 style bus interface, you could only find those at industrial electronics suppliers. By far the best way is to go through the National Automatic Merchandising Association, the industry group that owns the MDB specification.
The 9-bit data format is just a trick and is used in the MDB protocol to mode-switch between address bytes and data bytes. All ports on the bus listen to address bytes, only the addressed port listens to data bytes.
The 9th bit is simply the parity bit that any UART can generate. The fundamental data size is still 8 bits. An UART auto-generates the parity bit from the way it was initialized, you can choose between mark, space, odd and even parity.
Now this is easy to do in a micro-controller that has an UART, the kind of processor used on a bus like this. You simply re-program the UART on-the-fly, telling it to generate mark parity when you send the address bytes. And re-program it again to space parity when you send the data bytes. Waiting for the fifo to empty will typically be necessary although it depends on the actual UART chip.
That is a lot harder to do on a regular Windows or Linux machine, there's a driver between the user mode program and the UART. The driver generates a "transmit buffer empty" status bit, like WaitCommmEvent() for EV_TXEMPTY on Windows, but this doesn't include the fifo empty status, it only indicates that the buffer is empty. A workaround would be to wait for the buffer empty status and then sleep() long enough to ensure that the fifo is emptied. A fifo is typically 16 bytes deep so sleep for 16 times the bit time. You'll need the datasheet for the UART on the card you selected to know these details for sure.
Under Win32 serial ports are just files, so you create a handle for it with CreateFile and then use a DCB structure to set up the configuration options (the members are documented here and include number of data bits as ByteSize).
There's a good walk through here:
http://www.codeproject.com/Articles/3061/Creating-a-Serial-communication-on-Win32
The link provided shows the card supports 9 data bits and Windows 8, so I would presume all the cards features are available to the application through the standard Windows API.
Apart from setting the correct data format in a DCB and opening the port, I would have thought the standard ReadFile would work. I wonder if the data read in would actually be 2*8 bit bytes which represent the 9 data bits, rather than a continuous 9 bits streamed in (which you will need to decode at a later date).
Is the 9th bit used for some purpose other than data?

GPS SD Card Data Logger in 16F819 raw type issue

I did a GPS data logger application with PIC 16F819, my sd card format type fat32. i run my application and sent gps record to sd card.then, i couldnt see any text file or file. it saved it probably native raw type. I used a hex editor but this is not showed me relevant characters. My used project http://www.gedanken.demon.co.uk/gps-sd-logger/
How i can see the datas!!..
From the linked page (emphasis mine),
The data is written to the SD card in blocks of 512 bytes (the native SD card block size). One block is written each second and padded to 512 bytes with zero bytes. Starting and stopping is recorded by a blank block in the SD card. There is no file system on the SD card so it needs to be read byte by byte (easy in Linux using a USB adapter and reading the raw device). To read the card I have a simple C program that extracts the NMEA data and writes one file for each time that the box was awake.
You need to read the card directly, which is easy on Linux or OS X but will differ between platforms. Feed the binary data to the C program he should have also posted on the site. (I didn't look.)
This assumes the hardware and everything is working correctly, which is not a safe assumption for a project simply built from online instructions. Probably you should contact the author of that page, he likely won't mind giving a little help.

C Linux Device Programming - Reading Straight from /Dev

I have been playing with creating sounds using mathematical wave functions in C. The next step in my project is getting user input from a MIDI keyboard controller in order to modulate the waves to different pitches.
My first notion was that this would be relatively simple and that Linux, being Linux, would allow me to read the raw data stream from my device like I would any other file.
However, research overwhelmingly advises that I write a device driver for the MIDI controller. The general idea is that even though the device file may be present, the kernel will not know what system calls to execute when my application calls functions like read() and write().
Despite these warnings, I did an experiment. I plugged in the MIDI controller and cat'ed the "/dev/midi1" device file. A steady stream of null characters appeared, and when I pressed a key on the MIDI controller several bytes appeared corresponding to the expected Message Chunks that a MIDI device should output. MIDI Protocol Info
So my questions are:
Why does the cat'ed stream behave this way?
Does this mean that there is a plug and play device driver already installed on my system?
Should I still go ahead and write a device driver, or can I get away with reading it like a file?
Thank you in advanced for sharing your wisdom in these areas.
Why does the cat'ed stream behave this way?
Because that is presumably the raw MIDI data that is being received by the controller. The null bytes are probably some sort of sync tick.
Does this mean that there is a plug and play device driver already installed on my system?
Yes.
However, research overwhelmingly advises that I write a device driver for the MIDI controller. The general idea is that even though the device file may be present, the kernel will not know what system calls to execute when my application calls functions like read() and write().
<...>
Should I still go ahead and write a device driver, or can I get away with reading it like a file?
I'm not sure what you're reading or how you're coming to this conclusion, but it's wrong. :) You've already got a perfectly good driver installed for your MIDI controller -- go ahead and use it!
Are you sure you are reading NUL bytes? And not 0xf8 bytes? Because 0xf8 is the MIDI time tick status and is usually sent periodically to keep the instruments in sync. Try reading the device using od:
od -vtx1 /dev/midi1
If you're seeing a bunch of 0xf8, it's okay. If you don't need the tempo information sent by your MIDI controller, either disable it on your controller or ignore those 0xf8 status bytes.
Also, for MIDI, keep in mind that the current MIDI status is usually sent once (to save on bytes) and then the payload bytes follow for as long as needed. For example, the pitch bend status is byte 0xeK (where K is the channel number, i.e. 0 to 15) and its payload is 7 bits of the least significant byte followed by 7 bits of the most significant bytes. Thus, maybe you got a weird controller and you're seeing only repeated payloads of some status, but any controller that's not stupid won't repeat what it doesn't need to.
Now for the driver: have a look at dmesg when you plug in your MIDI controller. Now if your OSS /dev/midi1 appears when you plug in your device (udev is doing this job), and dmesg doesn't shoot any error, you don't need anything else. The MIDI protocol is yet-another-serial-protocol that has a fixed baudrate and transmits/receives bytes. There's nothing complicated about that... just read from or write to the device and you're done.
The only issue is that queuing at some place could result in bad audio latency (if you're using the MIDI commands to control live audio, which I believe is what you're doing). It seems like those devices are mostly made for system exclusive messages, that is, for example, downloading some patch/preset for a synthesizer online and uploading it to the device using MIDI. Latency doesn't really matter in this situation.
Also have a look at the ALSA way of playing with MIDI on Linux.
If you are not developing a new MIDI controller hardware, you shouldn't worry about writing a driver for it. It's the user's concern installing their hardware, and the vendor's obligation to supply the drivers.
Under Linux, you just read the file. Now to interpret and make useful things with the data.

Resources