I'm writing user-space program that is intended to control some device via usb so I decided to use libusb (libusb-1.0) to send control messages to and receive responses from that device.
But I constantly receive the following bunch of errors from my code (even when it's executed using 'sudo'):
USB error: could not set config 0: Device or resource busy
set configuration: failed
Check that you have permissions to write to 007/012 and, if you don't, that you set up hotplug (http://linux-hotplug.sourceforge.net/) correctly.
USB error: could not claim interface 0: Device or resource busy
claim interface: failed
USB error: error submitting URB: No such file or directory
bulk writing: failed
USB error: error submitting URB: No such file or directory
bulk reading: failed
response was:
The code is:
usb_dev_handle* find_device ();
int
main (int argc, char *argv[])
{
usb_dev_handle* udev;
int status;
char request[] = "K1"; // 'ping' command used to check communication
char response[256];
udev = find_device ();
// udev is successfully found here
status = usb_set_configuration (udev, 0);
printf ("set configuration: %s\n", status ? "failed" : "passed");
status = usb_claim_interface (udev, 0);
printf ("claim interface: %s\n", status ? "failed" : "passed");
status = usb_bulk_write (udev, 3, request, sizeof (request), 500);
printf ("bulk writing: %s\n", status ? "failed" : "passed");
status = usb_bulk_read (udev, 2, response, sizeof (response), 500);
printf ("bulk reading: %s\n", status ? "failed" : "passed");
printf ("response was: %s\n", response);
usb_close (udev);
return 0;
}
What's wrong with the code? And how it could be fixed?
OS: Ubuntu 10.10
Answering this question as I had faced this issue on the same OS and was able to solve in the following manner:
Download and Compile the latest libusb source code 1.0.8.
Following are some API calls that I used in order to claim USB interface 0:
libusb_init(NULL);
libusb_open_device_with_vid_pid(NULL, vendor_id, product_id);
libusb_detach_kernel_driver(devh, 0);
libusb_claim_interface(devh, 0);
libusb_close(devh);
libusb_exit(NULL);
Description of the variables in the above example:
static struct libusb_device_handle *devh = NULL;
uint16_t vendor_id;
uint16_t product_id;
To get the vendor ID and product ID, you may run the following commands (Ex. my device info)
$ lsusb
...
Bus 001 Device 013: ID 0930:6544 Toshiba Corp. Kingston DataTraveler 2.0 Stick (2GB)
...
The bold colon delimited string contains vendor and product id respectively.
How to compile the code:
I used the following command to compile my code:
/bin/bash libtool --silent --tag=CC --mode=link g++ -Wall -Wundef -Wunused -Wshadow -D_DEBUG -I../libusb -g -O2 -o read read.cpp ../libusb/libusb-1.0.la -lusb-1.0 -lrt
Copy libtool to the compilation area from the extracted libusb-1.0.8 directory.
Hope this helps.
Don't you need to open() the device before you set its configuration and claim it?
here's a generic example of a libusb program that you can adapt as needed.
also see the API, which is great!
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libusb.h>
#include <err.h>
#define MFGR_ID 0 // given manufacturer ID
#define DEV_ID 0 // given device ID
/* If device IDs are not known, use libusb_get_device_list() to see a
list of all USB devices connected to the machine. Follow this call with
libusb_free_device_list() to free the allocated device list memory.
*/
int main() {
int init = libusb_init(NULL); // NULL is the default libusb_context
if (init < 0) {
errx(1,”\n\nERROR: Cannot Initialize libusb\n\n”);
}
struct libusb_device_handle *dh = NULL; // The device handle
dh = libusb_open_device_with_vid_pid(NULL,MFGR_ID,DEV_ID);
if (!dh) {
errx(1,”\n\nERROR: Cannot connect to device %d\n\n”,DEV_ID);
}
// set fields for the setup packet as needed
uint8_t bmReqType = 0; // the request type (direction of transfer)
uint8_t bReq = 0; // the request field for this packet
uint16_t wVal = 0; // the value field for this packet
uint16_t wIndex = 0; // the index field for this packet
unsigned char* data = ‘ ‘; // the data buffer for the in/output data
uint16_t wLen = 0; // length of this setup packet
unsigned int to = 0; // timeout duration (if transfer fails)
// transfer the setup packet to the USB device
int config =
libusb_control_transfer(dh,bmReqType,bReq,wVal,wIndex,data,wLen,to);
if (config < 0) {
errx(1,”\n\nERROR: No data transmitted to device %d\n\n”,DEV_ID);
}
// now you can use libusb_bulk_transfer to send raw data to the device
libusb_exit(NULL);
}
Related
I have an application that I have cross-compiled for the USRP N310 that uses the TCA9548 I²C switch to read and write data to and from the TCA6408 I/O Expander on the daughterboard using the built-in Linux I2C driver. To do this, I've been following the tutorial "Implementing I2C drivers in UserSpace" attached here i2c tutorial. I've been able to test out this functionality successfully using the ZC706 embedded platform,however, I'm having issues reading data on the USRP platform. My code is the following
#include "i2c_dev.hpp"
int main()
{
int i2cfd;
__s32 num;
//Opening i2c adapter 6
printf("Opening bus adapter\n");
i2cfd = open("/dev/i2c-6", O_RDWR);
if ( i2cfd <0 )
{
printf("Failed to open /dev/i2c-6: %s\n",strerror( errno ));
return (1);
}
//Instatiating three objects of IO_Expander class
IO_Expander dba;
//Reading data from the IO Expander
printf("Setting slave address of device\n");
if (ioctl(i2cfd, I2C_SLAVE, 0x20) < 0) {
printf("Error setting slave address:%s\n",strerror(errno));
return(1);
}
printf("Reading data from the IO Expander for DB-A Object\n");
num =dba.read_data(i2cfd, 0x00);
if (num<0){
printf("Error reading data: %s\n",strerror(errno));
}
else {
printf("The input value is %d: \n", num);
}
printf("Leaving DB-A Object \n\n\n");
//Closing the adapter
close(i2cfd);
}
The printout I receive when running this application is a string error "No such device or address" when trying to read data from the I/O Expander.The slave address of the device is the 7 bit address of 010 0000 or hex 20 and the address of the input register is 0x00 according to the data sheet. At the moment, I'm unclear as to what's going wrong.I'm a bit of a newbie so I'm unsure of what to try next.
TCA9548 data sheet
TCA6408 data sheet
I recently purchased the FTDI C232HM-DDHSL-0 USB cable for use with SPI and I2C devices; my goal is to read the memory from an SPI memory chip that I removed from a router. However, I am having issues getting the libMPSSE library & 2xx drivers to send signals to a connected device.I hooked the leads of the C232 up to a Saleae logic analyzer and saw that no signals were being output!
Useful links for this question:
FTDI C232HM-DDHSL-0 (purchase):
https://www.ftdichip.com/Products/Cables/USBMPSSE.htm
C232HM-DDHSL-0 data sheet:
https://www.ftdichip.com/Support/Documents/DataSheets/Cables/DS_C232HM_MPSSE_CABLE.PDF
Memory Chip data sheet: https://www.macronix.com/Lists/Datasheet/Attachments/7370/MX25L6406E,%203V,%2064Mb,%20v1.9.pdf
2xx drivers: https://www.ftdichip.com/Drivers/D2XX.htm
MPSSE SPI library:
https://www.ftdichip.com/Support/SoftwareExamples/MPSSE/LibMPSSE-SPI.htm
I am using a Debian x86_64 machine. The MPSSE SPI download only had the i386 version of the library, so I downloaded the MPSSE SPI source (https://www.ftdichip.com/Support/SoftwareExamples/MPSSE/LibMPSSE-SPI.htm) and built the x86_64 bit version. There were no build errors or warnings.
I copied libftd2xx.so and libMPSSE.so to /usr/local/lib.
I plugged the cable in, then removed the following kernel modules:
ftdi_sio <-- Readme says to take take out
usbserial <-- Readme says to take out
usb_wann <-- Needed to remove to take out usbserial
qcserial <-- Needed to remove to take out usbserial
At this point I was hoping that my set up was done correctly.
I connected 6x cables to the 8 pin memory chip:
VCC <--> red lead
Ground <--> black lead
Chip select <--> brown lead
Data in <--> green
serial clock <--> orange
Data out <--> yellow
I did not connect hold and write protect pins on the memory chip. Recall my goal is to read the memory out of the chip, and the waveform for the read did not show those pins were necessary.
I am using this program:
/* Standard C libraries */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include "ftd2xx.h"
#include "libMPSSE_spi.h"
FT_HANDLE ftHandle;
uint8 tx_buffer[4096] = {0};
uint8 rx_buffer[4096] = {0};
int main()
{
uint8 i = 0;
int sizeToTransfer = 0;
int sizeTransfered = 0;
FT_STATUS status = FT_OK;
FT_DEVICE_LIST_INFO_NODE devList = {0};
ChannelConfig channelConf = {0};
channelConf.ClockRate = 30000000; // 30Mhz
channelConf.LatencyTimer = 75;
channelConf.configOptions = SPI_CONFIG_OPTION_MODE0 | SPI_CONFIG_OPTION_CS_DBUS3 | SPI_CONFIG_OPTION_CS_ACTIVELOW;
channelConf.Pin = 0x00000000;/*FinalVal-FinalDir-InitVal-InitDir (for dir 0=in, 1=out)*/
//
// Open the channel and dump some information
//
status = SPI_GetChannelInfo(0,&devList);
if (status != FT_OK)
{
printf("SPI_GetChannelInfo failed, status = %d\n", status);
return -1;
}
printf("Flags=0x%x\n",devList.Flags);
printf("Type=0x%x\n",devList.Type);
printf("ID=0x%x\n",devList.ID);
printf("LocId=0x%x\n",devList.LocId);
printf("SerialNumber=%s\n",devList.SerialNumber); // TODO: Why blank?
printf("Description=%s\n",devList.Description);
printf("ftHandle=0x%p\n",devList.ftHandle);/*is 0 unless open*/
//
// Open channel 0
//
status = SPI_OpenChannel(0,&ftHandle);
if (status != FT_OK)
{
printf("SPI_OpenChannel failed, status = %d\n", status);
return -1;
}
//
// Initialize the channel: See configuration structure at top of main()
//
status = SPI_InitChannel(ftHandle,&channelConf);
if (status != FT_OK)
{
printf("SPI_InitChannel failed, status = %d\n", status);
return -1;
}
//
// Send the read command (0x03), and read what we get in our receive buffer
//
sizeToTransfer=8;
sizeTransfered=0;
tx_buffer[0] = 0x03;
status = SPI_ReadWrite(ftHandle, rx_buffer, tx_buffer, sizeToTransfer, &sizeTransfered,
SPI_TRANSFER_OPTIONS_SIZE_IN_BYTES|SPI_TRANSFER_OPTIONS_CHIPSELECT_DISABLE|SPI_TRANSFER_OPTIONS_CHIPSELECT_ENABLE);
if (status != FT_OK)
{
printf("SPI_ReadWrite failed, status = %d\n", status);
return -1;
}
//
// Dump the receive buffer to see if we got anything.
//
printf("Size transfered = %d\n", sizeTransfered);
i = 0;
while (i < sizeTransfered)
{
printf("%02x",rx_buffer[i]);
i++;
}
printf("\n");
//
// Cleanup
//
status = SPI_CloseChannel(ftHandle);
if (status != FT_OK)
{
printf("SPI_CloseChannel failed, ret = %d\n", status);
}
return 0;
}
To make and run the program:
To compile: gcc read_memory.c -lMPSSE -ldl
To run: sudo ./a.out
My output is:
// The driver can at least detect the cable.
Flags=0x2
Type=0x8
ID=0x4036001
LocId=0x204
SerialNumber=
Description=USB <-> Serial
ftHandle=0x(nil)
Size transfered = 8
// Junk in the receive buffer
ff037fffffffffff
Whether I keep the memory chip connected or disconnected I get the same junk in the receive buffer. I see no LEDs light up on the cable or anything to indicate it is "working." I have tried SPI_Write() and SPI_Read() and get similar behavior. I tested on an Ubuntu VM (running on Windows host) and saw the exact same behavior.
I am really excited to get this to work so if anyone can help me with what I am doing wrong it would be greatly appreciated! Thanks!
Set device first to MPSSE mode, and it should work. See quite minimum (Python) example below based on FTD2XX library only (tested in Windows). LibMPSSE is not required to run simple SPI. Data can be read by s = dev.read(nbytes), where nbytes is byte count. Find more information in nice tutorial: Driving an SPI device using MPSSE
import ftd2xx
OPS = 0x03 # Bit mask for SPI clock and data out
OE = 0x08 # CS
dev = ftd2xx.open(0) # open first available device
if dev:
dev.setTimeouts(1000, 1000)
dev.setBitMode(OPS, 2) # bit mask, MPSSE mode
dev.write(bytes((0x86, 59, 0))) # set SCK to 100kHz
dev.write(bytes((0x80, 0, OE + OPS))) # CS low
data = 0x55,
n = len(data) - 1
dev.write(bytes(((0x11, n % 256, n // 256) + data))) # write SPI data
dev.write(bytes((0x80, OE, OE + OPS))) # CS high
dev.close()
I need to find the specific interface which is used by a socket, so that I can keep stats for it, using the sysfs files (/sys/class/net/<IF>/statistics/etc).
I've tried two different approaches in the test code below, but both fail. The first one connects to a remote server, and uses ioctl with SIOCGIFNAME, but this fails with 'no such device'. The second one instead uses getsockopt with SO_BINDTODEVICE, but this again fails (it sets the name length to 0).
Any ideas on why these are failing, or how to get the I/F name? after compiling, run the test code as test "a.b.c.d", where a.b.c.d is any IPV4 address which is listening on port 80. Note that I've compiled this on Centos 7, which doesn't appear to have IFNAMSZ in <net/if.h>, so you may have to comment out the #define IFNAMSZ line to get this to compile on other systems.
Thanks.
EDIT
I've since found that this is essentially a dupe of How can I get the interface name/index associated with a TCP socket?, so I should probably remove this. (Only) one of the answers there is correct (https://stackoverflow.com/a/37987807/785194) - get your local IP address with getsockname, and then look up this address in the list returned by getifaddrs.
On the general issue that sockets are essentially dynamic (mentioned below, and several times in the other question): not really relevant. I've checked the kernel source, and sockets have an interface index and interface name, and the API includes at least three ways to get the current name, and other routines to look up the name from the index, and vice-versa. However, the index is somtimes zero, which is not valid, which is why the getsockopt version below fails. No idea why ioctl fails.
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <net/if.h>
int main(int argc, char **argv) {
int sock;
struct sockaddr_in dst_sin;
struct in_addr haddr;
if(argc != 2)
return 1;
if(inet_aton(argv[1], &haddr) == 0) {
printf("'%s' is not a valid IP address\n", argv[1]);
return 1;
}
dst_sin.sin_family = AF_INET;
dst_sin.sin_port = htons(80);
dst_sin.sin_addr = haddr;
if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
return 1;
}
if(connect(sock, (struct sockaddr*)&dst_sin, sizeof(dst_sin)) < 0) {
perror("connect");
return 1;
}
printf(
"connected to %s:%d\n",
inet_ntoa(dst_sin.sin_addr), ntohs(dst_sin.sin_port));
#if 0 // ioctl fails with 'no such device'
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
// get the socket's interface index into ifreq.ifr_ifindex
if(ioctl(sock, SIOCGIFINDEX, &ifr) < 0) {
perror("SIOCGIFINDEX");
return 1;
}
// get the I/F name for ifreq.ifr_ifindex
if(ioctl(sock, SIOCGIFNAME, &ifr) < 0) {
perror("SIOCGIFNAME");
return 1;
}
printf("I/F is on '%s'\n", ifr.ifr_name);
#else // only works on Linux 3.8+
#define IFNAMSZ IFNAMSIZ // Centos7 bug in if.h??
char optval[IFNAMSZ] = {0};
socklen_t optlen = IFNAMSZ;
if(getsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &optval, &optlen) < 0) {
perror("getsockopt");
return 1;
}
if(!optlen) {
printf("invalid optlen\n");
return 1;
}
printf("I/F is on '%s'\n", optval);
#endif
close(sock);
return 0;
}
TCP (and UDP) sockets are not bound to interfaces, so there is really no facility for answering this query. Now it's true that in general, a given socket will end up passing packets to a specific interface based on the address of the peer endpoint, but that is nowhere encoded in the socket. That's a routing decision that is made dynamically.
For example, let's say that you are communicating with a remote peer that is not directly on your local LAN. And let's say you have a default gateway configured to be 192.168.2.1 via eth0. There is nothing to prevent your configuring a second gateway, say, 192.168.3.1 via eth1, then taking eth0 down. As long as the new gateway can also reach the remote IP, eth1 can now be used to reach the destination and your session should continue uninterrupted.
So, if you need this info, you'll need to infer it from routing entries (but realize that it is not guaranteed to be static, even though in practice it will likely be so). You can obtain the address of your peer from getpeername(2). You can then examine the available routes to determine which one will get you there.
To do this, you could parse and interpret /proc/net/route for yourself, or you can just ask the ip command. For example, my route to an (arbitrary) ibm.com address goes through my eth0 interface, and connecting a socket to there, my local address will be 192.168.0.102 (which should match what getsockname(2) on the connected socket returns):
$ ip route get 129.42.38.1
129.42.38.1 via 192.168.0.1 dev eth0 src 192.168.0.102
cache
I want to implement command tcpdump -i eth0 arp to observe arp packets on interface eth0 on my ubuntu. I use libpcap, but the return value of function pcap_next_ex is always 0. With tcpdump -i eth0 arp in the same time , it can observe arp packets.
/*
* compile(root): gcc test.c -lpcap
* run : ./a.out
* output : time out
* time out
* time out
* ...
*/
#include <pcap.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define ARP_REQUEST 1
#define ARP_REPLY 2
typedef struct arp_hdr_s arp_hdr_t;
struct arp_hdr_s {
u_int16_t htype;
u_int16_t ptype;
u_char hlen;
u_char plen;
u_int16_t oper;
u_char sha[6];
u_char spa[4];
u_char tha[6];
u_char tpa[4];
};
#define MAXBYTES2CAPTURE 2048
int
main(int argc, char **argv)
{
char err_buf[PCAP_ERRBUF_SIZE];
const unsigned char *packet;
int i;
int ret;
arp_hdr_t *arp_header;
bpf_u_int32 net_addr;
bpf_u_int32 mask;
pcap_t *desrc;
struct pcap_pkthdr *pkthdr;
struct bpf_program filter;
net_addr = 0;
mask = 0;
memset(err_buf, 0, PCAP_ERRBUF_SIZE);
desrc = pcap_open_live("eth0", MAXBYTES2CAPTURE, 0, 512, err_buf);
if (desrc == NULL) {
fprintf(stderr, "error: %s\n", err_buf);
exit(-1);
}
ret = pcap_lookupnet("eth0", &net_addr, &mask, err_buf);
if (ret < 0) {
fprintf(stderr, "error: %s\n", err_buf);
exit(-1);
}
ret = pcap_compile(desrc, &filter, "arp", 1, mask);
if (ret < 0) {
fprintf(stderr, "error: %s\n", pcap_geterr(desrc));
exit(-1);
}
ret = pcap_setfilter(desrc, &filter);
if (ret < 0) {
fprintf(stderr, "errnor: %s\n", pcap_geterr(desrc));
exit(-1);
}
while (1) {
ret = pcap_next_ex(desrc, &pkthdr, &packet);
if (ret == -1) {
printf("%s\n", pcap_geterr(desrc));
exit(1);
} else if (ret == -2) {
printf("no more\n");
} else if (ret == 0) { // here
printf("time out\n");
continue;
}
arp_header = (arp_hdr_t *)(packet + 14);
if (ntohs(arp_header->htype) == 1 && ntohs(arp_header->ptype == 0x0800)) {
printf("src IP: ");
for (i = 0; i < 4; i++) {
printf("%d.", arp_header->spa[i]);
}
printf("dst IP: ");
for (i = 0; i < 4; i++) {
printf("%d.", arp_header->tpa[i]);
}
printf("\n");
}
}
return 0;
}
Without getting too deep in your code, I can see a major problem:
In your use of pcap_open_live(), you do not set promiscuous mode: the third parameter should be non-zero. If the ARP request is not targeted to your interface IP, pcap will not see it without promiscuous mode. tcpdump does, unless specifically told not to do so by using the --no-promiscuous-mode, use promisc (and hence will require CAP_NET_ADMIN privilege, which you'll get by sudo, which your program will require too).
Side note:
1/ Leak: you may want to free your filter using pcap_freecode() after your pcap_setfilter().
2/ I assume you've read the official tuto here:
http://www.tcpdump.org/pcap.html
...if that's not the case you'd be well advised to do that first. I quote:
A note about promiscuous vs. non-promiscuous sniffing: The two
techniques are very different in style. In standard, non-promiscuous
sniffing, a host is sniffing only traffic that is directly related to
it. Only traffic to, from, or routed through the host will be picked
up by the sniffer. Promiscuous mode, on the other hand, sniffs all
traffic on the wire. In a non-switched environment, this could be all
network traffic. [... more stuff on promisc vs non-promisc]
EDIT:
Actually, looking deeper to you code compared to my code running for +1 year at production level (both in-house and at the customer) I can see many more things that could be wrong:
You never call pcap_create()
You never call pcap_set_promisc(), we've talked about this already
You never call pcap_activate(), this may be the core issue here
...pcap is very touchy about the sequence order of operations to first get a pcap_t handle, and then operate on it.
At the moment, the best advice I can give you - otherwise this is going to a live debugging session between you and me, are:
1/ read and play/tweak with the code from the official tutorial:
http://www.tcpdump.org/pcap.html
This is mandatory.
2/ FWIW, my - definitely working - sequence of operations is this:
pcap_lookupnet()
pcap_create()
pcap_set_promisc()
pcap_set_snaplen(), you may or may not need this
pcap_set_buffer_size(), you may or may not need this
pcap_activate() with a note: Very important: first activate, then set non-blocking from PCAP_SETNONBLOCK(3PCAP): When first activated with pcap_activate() or opened with pcap_open_live() , a capture handle is not in non-blocking mode''; a call to pcap_set-nonblock() is required in order to put it intonon-blocking'' mode.
...and then, because I do not use stinking blocking/blocking with timeout, busy looping:
pcap_setnonblock()
pcap_get_selectable_fd()
...then and only then:
- pcap_compile()
- followed by a pcap_setfilter()
- and then as I mentioned a pcap_freecode()
- and then a select() or family on the file'des' I get from pcap_get_selectable_fd(), to pcap_dispatch(), but this is another topic.
pcap is an old API starting back in the 80's, and its really very very touchy. But don't get discouraged! It's great - once you get it right.
It would probably work better if you did
if (ntohs(arp_header->htype) == 1 && ntohs(arp_header->ptype) == 0x0800) {
rather than
if (ntohs(arp_header->htype) == 1 && ntohs(arp_header->ptype == 0x0800)) {
The latter evaluates arp_header->type == 0x0800, which, when running on a little-endian machine (such as a PC), will almost always evaluate to "false", because the value will look like 0x0008, not 0x0800, in an ARP packet - ARP types are big-endian, so they'll look byte-swapped on a little-endian machine). That means it'll evaluate to 0, and byte-swapping 0 gives you zero, so that if condition will evaluate to "false", and the printing code won't be called.
You'll still get lots of timeouts if you fix that, unless there's a flood of ARP packets, but at least you'll get the occasional ARP packet printed out. (I would advise printing nothing on a timeout; pcap-based programs doing live capturing should expect that timeouts should happen, and should not report them as unusual occurrences.)
I'm writing a Linux kernel module to read out a GPS device (a u-blox NEO-7) via USB by using the book Linux Device Drivers.
I already can probe and read out data from the device successfully. But, there is a problem when reading the device with multiple applications simultaneously (I used "cat /dev/ublox" to read indefinitely). When the active/reading applications is cancelled via "Ctrl + C", the next reading attempt from the other application fails (exactly method call usb_submit_urb(...) returns -EINVAL).
I use following ideas for my implementation:
The kernel module methods should be re-entrant. Therefore, I use a mutex to protect critical sections. E.g. allowing only one reader simultaneously.
To safe ressources, I reuse the struct urb for different reading requests (see an explanation)
Device-specific data like USB endpoint address and so on is held in a device-specific struct called ublox_device.
After submitting the USB read request, the calling process is sent to sleep until the asynchronous complete handler is called.
I verified that the ideas are implemented correctly: I have run two instances of "cat /dev/ublox" simultaneously and I got the correct output (only one instance accessed the critical read section at a time). And also reusing the "struct urb" is working. Both instances read out data alternatively.
The problem only occurs if the currently active instance is cancelled via "Ctrl + C". I can solve the problem by not reusing the "struct urb" but I would like to avoid that. I.e. by allocating a new "struct urb" for each read request via usb_alloc_urb(...) (usually it is allocated once when probing the USB device).
My code follows the USB skeleton driver from Greg Kroah-Hartman who also reuse the "struct urb" for different reading requests.
Maybe someone has a clue what's going wrong here.
The complete code can be found on pastebin. Here is a small excerpt of the read method and the USB request complete handler.
static ssize_t ublox_read(struct file *file, char *buffer, size_t count, loff_t *pos)
{
struct ublox_device *ublox_device = file->private_data;
...
return_value = mutex_lock_interruptible(&ublox_device->bulk_in_mutex);
if (return_value < 0)
return -EINTR;
...
retry:
usb_fill_bulk_urb(...);
ublox_device->read_in_progress = 1;
/* Next call fails if active application is cancelled via "Ctrl + C" */
return_value = usb_submit_urb(ublox_device->bulk_in_urb, GFP_KERNEL);
if (return_value) {
printk(KERN_ERR "usb_submit_urb(...) failed!\n");
ublox_device->read_in_progress = 0;
goto exit;
}
/* Go to sleep until read operation has finished */
return_value = wait_event_interruptible(ublox_device->bulk_in_wait_queue, (!ublox_device->read_in_progress));
if (return_value < 0)
goto exit;
...
exit:
mutex_unlock(&ublox_device->bulk_in_mutex);
return return_value;
}
static void ublox_read_bulk_callback(struct urb *urb)
{
struct ublox_device *ublox_device = urb->context;
int status = urb->status;
/* Evaluate status... */
...
ublox_device->transferred_bytes = urb->actual_length;
ublox_device->read_in_progress = 0;
wake_up_interruptible(&ublox_device->bulk_in_wait_queue);
}
Now, I allocate a new struct urb for each read request. This avoids the problem with the messed up struct urb after an active read request is cancelled by the calling application. The allocated struct is freed in the complete handler.
I will come back to LKML when I optimize my code. For now, it is okay to allocate a new struct urb for each single read request. The complete code of the kernel module is on pastebin.
static ssize_t ublox_read(struct file *file, char *buffer, size_t count, loff_t *pos)
{
struct ublox_device *ublox_device = file->private_data;
...
retry:
ublox_device->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL);
...
usb_fill_bulk_urb(...);
...
return_value = usb_submit_urb(ublox_device->bulk_in_urb, GFP_KERNEL);
...
}
static void ublox_read_bulk_callback(struct urb *urb)
{
struct ublox_device *ublox_device = urb->context;
...
usb_free_urb(ublox_device->bulk_in_urb);
...
}