IOCTL is a function call that allows you to interface with kernel drivers, allowing you to adjust settings or set parameters from code without compiling a new module.
From a programming perspective, having the linux kernel source is a prerequisite. In this example, I cloned the main kernel:
git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git linux
Once I had the source, I specifically wanted to see exactly how I could interface with the driver called usblp. I was using a USB to parallel port converter, and wanted to see if there was any way to force it to operate differently as we needed a non-printer device to work with it.
After opening the kernel source, I found the driver file itself which was under /drivers/usb/class/usblp.c. In this file I found a section of information about which IOCTLs it supported, and put them in a header file for my program called usblp-hack.h:
#ifndef USBLP_HACK_H #define USBLP_HACK_H #include <linux/ioctl.h> /* ioctls: */ #define IOCNR_GET_DEVICE_ID 1 #define IOCNR_GET_PROTOCOLS 2 #define IOCNR_SET_PROTOCOL 3 #define IOCNR_HP_SET_CHANNEL 4 #define IOCNR_GET_BUS_ADDRESS 5 #define IOCNR_GET_VID_PID 6 #define IOCNR_SOFT_RESET 7 /* Get device_id string: */ #define LPIOC_GET_DEVICE_ID(len) _IOC(_IOC_READ, 'P', IOCNR_GET_DEVICE_ID, len) /* The following ioctls were added for http://hpoj.sourceforge.net: */ /* Get two-int array: * [0]=current protocol (1=7/1/1, 2=7/1/2, 3=7/1/3), * [1]=supported protocol mask (mask&(1<<n)!=0 means 7/1/n supported): */ #define LPIOC_GET_PROTOCOLS(len) _IOC(_IOC_READ, 'P', IOCNR_GET_PROTOCOLS, len) /* Set protocol (arg: 1=7/1/1, 2=7/1/2, 3=7/1/3): */ #define LPIOC_SET_PROTOCOL _IOC(_IOC_WRITE, 'P', IOCNR_SET_PROTOCOL, 0) /* Set channel number (HP Vendor-specific command): */ #define LPIOC_HP_SET_CHANNEL _IOC(_IOC_WRITE, 'P', IOCNR_HP_SET_CHANNEL, 0) /* Get two-int array: [0]=bus number, [1]=device address: */ #define LPIOC_GET_BUS_ADDRESS(len) _IOC(_IOC_READ, 'P', IOCNR_GET_BUS_ADDRESS, len) /* Get two-int array: [0]=vendor ID, [1]=product ID: */ #define LPIOC_GET_VID_PID(len) _IOC(_IOC_READ, 'P', IOCNR_GET_VID_PID, len) /* Perform class specific soft reset */ #define LPIOC_SOFT_RESET _IOC(_IOC_NONE, 'P', IOCNR_SOFT_RESET, 0) #endif
There are plenty of hints on usage here, and I was able to grab a little more info after searching for some of those defines in google code search. Using these IOCTLs I wanted to know exactly which modes the USB to parallel converter supported, and try to set it in mode 3, which I hoped would give me more options for talking to the device.
Here is my usblp-hack.c which was able to probe and update these settings, although in my case mode 3 didn’t work, but shows an example of exactly how to do it:
#include "usblp-hack.h"
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h> /* open */
#include <unistd.h> /* exit */
#include <sys/ioctl.h> /* ioctl */
main()
{
int fd;
int twoints[2];
fd = open("/dev/usblp0", O_RDONLY | O_NONBLOCK);
if (fd < 0) {
printf("Cannot open device.\n");
exit(-1);
}
if(ioctl(fd, LPIOC_GET_PROTOCOLS(sizeof(int[2])), &twoints) >= 0)
printf("Great success: %d / %d\n", twoints[0], twoints[1]);
else {
printf("Fail!\n");
exit(-1);
}
if(ioctl(fd, LPIOC_SET_PROTOCOL, 2) >= 0)
printf("set protocol to version 2\n");
else {
printf("Fail!\n");
exit(-1);
}
if(ioctl(fd, LPIOC_GET_PROTOCOLS(sizeof(int[2])), &twoints) >= 0)
printf("Great success: %d / %d\n", twoints[0], twoints[1]);
else {
printf("Fail!\n");
exit(-1);
}
if(ioctl(fd, LPIOC_SOFT_RESET) >= 0)
{
printf("Success reset device\n");
} else {
printf("Could not reset device\n");
exit(-1);
}
close(fd);
exit(0);
}
You can compile the above example just using gcc:
gcc -o usblp-hack usblp-hack.c
You can see in the example above I was able to probe the device to see what mode it was currently in and supports (LPIOC_GET_PROTOCOLS), attempt to set the device mode (LPIOC_SET_PROTOCOL), and soft reset the device (LPIOC_SOFT_RESET).
So in the end, nothing here too complex or mind blowing, but if you are just getting your feet wet it might take you a minute to dig this sort of information up.
Related posts:
#1 by Maximi89 on July 10, 2010 - 9:34 pm
Quote
Hi, nice post, i would like to do the same as you, i bougth a usb to parallel converter, and i want to send specified signals to different pins on cable, this could be possible?
i’m trying to manage the pins…
i’m actually reading the source code of usblp and reading ioctl…
do you find anything about to how to manage the D0 to D7 pin?
Thanks
#2 by admin on August 16, 2010 - 8:11 pm
Quote
Here is the fun part: usb to parallel converters don’t implement the hardware controls needed to talk to each pin, instead they implement a bastardized version of the usb printer spec.
For hacking the usb to serial converters work, I would ditch parallel.
#3 by Samuel Ventura on March 30, 2011 - 3:48 am
Quote
I guess your are looking for something like http://thx8411.over-blog.com/pages/Add_TTL_outputs_to_your_USB_Laptop_Part_1_Hardware-3229030.html.
I dislike having to buffer/latch because of imposed current limit since every pin current is now drawn from power pins so I tested Prolific PL2305 based adapter and working in SPP compatibility mode keeps data bus state if ACK if left unconnected. The issue now is how to clear data bus to send new data because unless ACK is pulsed data is buffered and writing to device blocks. Here is where your post got interesting since I will now try to issue a soft reset (as a form of ‘soft ack’) before writing new data.