Related
I have a Variscite DART-MX8M evaluation board that I'm trying to use as a USB gadget, so I've written a user-mode gadget driver which talks to gadgetfs (mounted on /dev/gadget). It always returns a device descriptor to the host machine, but sometimes fails when responding to a requests for a string descriptor. The symptom is that when I write the string back to endpoint 0, I get EBUSY; it doesn't help to retry, as the subsequent attempt gets ESRCH.
It doesn't happen on the first request for the string descriptor, as you see lower down where the host gets the manufacturer and vendor (via udev rules, I think) but lsusb fails to retrieve them. But when it does fail, it's always at the same point in the execution.
I've done some print-tracing in the kernel (5.4.85, Variscite's "Dunfell" release) and found where the EBUSY originates:
/* we share one TRB for ep0/1 */
if (!list_empty(&dep->pending_list)) {
ret = -EBUSY;
goto out;
}
But I don't understand why we might hit this.
I made the simplest driver I could to reproduce this - it has just one configuration, with no endpoints (not counting ep0):
#include <linux/usb/ch9.h>
#include <linux/usb/gadgetfs.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define USB_DEV "/dev/gadget/dwc3-gadget"
enum string_id {
STRINGID_MANUFACTURER = 1,
STRINGID_PRODUCT,
STRINGID_SERIAL,
};
static const char *get_string_descriptor(enum string_id sid)
{
static const char mfr[] = { 10, USB_DT_STRING, 84, 0, 111, 0, 98, 0, 121, 0};
static const char prod[] = { 16, USB_DT_STRING, 119, 0, 105, 0, 100, 0, 103, 0, 101, 0, 116, 0};
static const char ser[] = { 8, USB_DT_STRING, 118, 0, 49, 0};
switch (sid) {
case STRINGID_MANUFACTURER:
return mfr;
case STRINGID_PRODUCT:
return prod;
case STRINGID_SERIAL:
return ser;
}
static const char fallback[] = { 20, USB_DT_STRING, 40, 0, 109, 0, 105, 0,
115, 0, 115, 0, 105, 0, 110, 0, 103, 0, 41, 0 };
return fallback;
}
static int write_string_descriptor(int fd, enum string_id sid) {
const char *buf = get_string_descriptor(sid);
return write(fd, buf, buf[0]);
}
static void handle_setup_request(int fd, struct usb_ctrlrequest* setup)
{
int status;
uint8_t buffer[512];
pthread_t thread;
printf("Setup request %d\n", setup->bRequest);
switch (setup->bRequest) {
case USB_REQ_GET_DESCRIPTOR:
if (setup->bRequestType != USB_DIR_IN)
goto stall;
switch (setup->wValue >> 8) {
case USB_DT_STRING:
printf("Get string id #%d(max length %d)\n", setup->wValue & 0xff,
setup->wLength);
status = write_string_descriptor(fd, setup->wValue & 0xff);
// Error
if (status < 0) {
fprintf(stderr, "Failed to write string descriptor %d\n", setup->wValue & 0xff);
}
return;
default:
fprintf(stderr, "Cannot return descriptor %d\n", (setup->wValue >> 8));
}
break;
case USB_REQ_SET_CONFIGURATION:
printf("Ignoring configuration value %d\n", setup->wValue);
// Just ACK
status = read(fd, &status, 0);
return;
case USB_REQ_GET_INTERFACE:
printf("GET_INTERFACE\n");
buffer[0] = 0;
write(fd, buffer, 1);
return;
case USB_REQ_SET_INTERFACE:
printf("SET_INTERFACE\n");
// ACK
status = read(fd, &status, 0);
return;
}
stall:
fprintf(stderr, "Stalled\n");
// Error
if (setup->bRequestType & USB_DIR_IN)
read (fd, &status, 0);
else
write (fd, &status, 0);
}
static void handle_ep0(int fd)
{
while (1) {
struct usb_gadgetfs_event events[5];
int readsize = read(fd, &events, sizeof events);
if (readsize < 0) {
fprintf(stderr, "Read error %d(%m)\n", readsize);
return;
}
const int nevents = readsize / sizeof events[0];
int surplus = readsize % sizeof events[0];
printf("%d event(s) and %d extra\n", nevents, surplus);
for (int i = 0; i < nevents; ++i) {
switch (events[i].type) {
case GADGETFS_CONNECT:
printf("EP0 CONNECT\n");
break;
case GADGETFS_DISCONNECT:
printf("EP0 DISCONNECT\n");
break;
case GADGETFS_SETUP:
printf("EP0 SETUP\n");
handle_setup_request(fd, &events[i].u.setup);
break;
case GADGETFS_NOP:
case GADGETFS_SUSPEND:
break;
}
}
}
}
int main()
{
const int fd = open(USB_DEV, O_RDWR|O_SYNC|O_EXCL);
if (fd < 0) {
fprintf(stderr, "Unable to open %s(%m)\n", USB_DEV);
return EXIT_FAILURE;
}
const struct usb_device_descriptor device_descriptor =
{ .bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bDeviceClass = USB_CLASS_COMM,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
.idVendor = 0xAA00, // Fake vendor ID
.idProduct = 0xBB, // Fake product ID
.bcdDevice = 0x0200, // Version
.iManufacturer = STRINGID_MANUFACTURER,
.iProduct = STRINGID_PRODUCT,
.iSerialNumber = STRINGID_SERIAL,
.bNumConfigurations = 1
};
const struct usb_interface_descriptor if_descriptor = {
.bLength = sizeof if_descriptor,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_COMM,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
.iInterface = 0
};
const struct usb_config_descriptor config = {
.bLength = sizeof config,
.bDescriptorType = USB_DT_CONFIG,
.wTotalLength = sizeof config + sizeof if_descriptor,
.bNumInterfaces = 1,
.bConfigurationValue = 1,
.iConfiguration = 0,
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
.bMaxPower = 1
};
uint8_t init_config[2048];
uint8_t* p = init_config;
printf("Start init\n");
*p++ = 0;
*p++ = 0;
*p++ = 0;
*p++ = 0;
#define FETCH(desc) \
memcpy(p, &desc, desc.bLength); \
p += desc.bLength;
FETCH(config);
FETCH(if_descriptor);
FETCH(config); /* again for a high-speed i/f */
FETCH(if_descriptor);
FETCH(device_descriptor);
// Configure ep0
int send_size = p - init_config;
int sent = write(fd, init_config, send_size);
if (sent != send_size) {
fprintf(stderr, "Write error %d/%d (%m)\n", sent/send_size);
return EXIT_FAILURE;
}
printf("ep0 configured\n");
handle_ep0(fd);
}
Without changing the code, I unpredictably get two different results as seen from a Linux host using lsusb -v -d aa00:00bb. When it works, I get:
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 2 Communications
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 64
idVendor 0xaa00
idProduct 0x00bb
bcdDevice 2.00
iManufacturer 1 Toby
iProduct 2 widget
iSerial 3 v1
bNumConfigurations 1
Device Qualifier (for other device speed):
bLength 10
bDescriptorType 6
bcdUSB 2.00
bDeviceClass 2 Communications
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 64
bNumConfigurations 1
Device Status: 0x0000
(Bus Powered)
And when it fails:
Bus 001 Device 027: ID aa00:00bb Toby widget
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 2 Communications
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 64
idVendor 0xaa00
idProduct 0x00bb
bcdDevice 2.00
iManufacturer 1 (error)
iProduct 2 (error)
iSerial 3 (error)
bNumConfigurations 1
And it won't work again until I unload the dwc3 driver and re-load it.
Here's the relevant kernel log messages for the failing case (I enabled CONFIG_USB_GADGET_VERBOSE to get these):
[342748.061482] gadgetfs: bound to dwc3-gadget driver
[342748.066303] gadgetfs: bound to gadget device
[342748.071482] gadgetfs: ep0_read wait
[342748.344072] gadgetfs: connected
[342748.347308] gadgetfs: event[0] = 1
[342748.347929] gadgetfs: ep0_read wait
[342748.352265] gadgetfs: disconnected
[342748.355755] gadgetfs: event[0] = 2
[342748.356315] gadgetfs: ep0_read wait
[342748.504041] gadgetfs: connected
[342748.507269] gadgetfs: event[0] = 1
[342748.507842] gadgetfs: ep0_read wait
[342748.510596] gadgetfs: delegate req80.06 v0300 i0000 l255
[342748.510600] gadgetfs: event[0] = 3
[342748.511208] gadgetfs: usb_ep_queue returned 0
[342748.511213] gadgetfs: ep0_read wait
[342748.511557] gadgetfs: delegate req80.06 v0302 i0028 l255
[342748.511563] gadgetfs: event[0] = 3
[342748.511630] gadgetfs: usb_ep_queue returned 0
[342748.511635] gadgetfs: ep0_read wait
[342748.511934] gadgetfs: delegate req80.06 v0301 i0028 l255
[342748.511940] gadgetfs: event[0] = 3
[342748.511989] gadgetfs: usb_ep_queue returned 0
[342748.511995] gadgetfs: ep0_read wait
[342748.512181] gadgetfs: delegate req80.06 v0303 i0028 l255
[342748.512187] gadgetfs: event[0] = 3
[342748.512232] gadgetfs: usb_ep_queue returned 0
[342748.512238] gadgetfs: ep0_read wait
[342748.512711] gadgetfs: configuration #1
[342748.516551] gadgetfs: event[0] = 3
[342748.517139] gadgetfs: ep0_read wait
[342748.953968] gadgetfs: delegate req80.06 v0300 i0000 l4
[342748.953974] gadgetfs: event[0] = 3
[342748.954544] dwc3 38100000.usb: ep0out: pending_list empty
[342748.960062] gadgetfs: usb_ep_queue returned -16
[342748.960108] gadgetfs: ep0_read wait
(The line from dwc is the one I added to the ret = -EBUSY shown above).
A Wireshark trace perhaps points the finger at SET_CONFIGURATION, as it all works until that point:
No. Time delta Source Length Info Descriptor no
1 0.000000 0.000000 host 64 GET DESCRIPTOR Request DEVICE 0x00
2 0.004048 0.004048 1.113.0 82 GET DESCRIPTOR Response DEVICE
3 0.004074 0.000026 host 64 GET DESCRIPTOR Request CONFIGURATION 0x00
4 0.004669 0.000595 1.113.0 73 GET DESCRIPTOR Response CONFIGURATION
5 0.004682 0.000013 host 64 GET DESCRIPTOR Request CONFIGURATION 0x00
6 0.005918 0.001236 1.113.0 110 GET DESCRIPTOR Response CONFIGURATION
7 0.005948 0.000030 host 64 GET DESCRIPTOR Request STRING 0x00
8 0.570791 0.564843 1.113.0 68 GET DESCRIPTOR Response STRING
9 0.570822 0.000031 host 64 GET DESCRIPTOR Request STRING 0x02
10 0.571913 0.001091 1.113.0 86 GET DESCRIPTOR Response STRING
11 0.571933 0.000020 host 64 GET DESCRIPTOR Request STRING 0x01
12 0.572662 0.000729 1.113.0 84 GET DESCRIPTOR Response STRING
13 0.572680 0.000018 host 64 GET DESCRIPTOR Request STRING 0x03
14 0.573539 0.000859 1.113.0 98 GET DESCRIPTOR Response STRING
15 0.573806 0.000267 host 64 SET CONFIGURATION Request
16 0.578297 0.004491 1.113.0 64 SET CONFIGURATION Response
17 0.578325 0.000028 host 64 GET DESCRIPTOR Request STRING 0x04
18 5.703230 5.124905 1.113.0 64 GET DESCRIPTOR Response
19 5.703244 0.000014 host 64 GET DESCRIPTOR Request STRING 0x04
20 10.823171 5.119927 1.113.0 64 GET DESCRIPTOR Response
What could be causing this failure, and what do I need to do to avert it?
I found something that seems to fix this. I don't much like it, and there ought to be something not involving an arbitrary sleep!
I changed the set-configuration handler to wait for 10ms before replying, and also to test the result of writing the ACK. Either one of these alone was insufficient.
case USB_REQ_SET_CONFIGURATION:
printf("Ignoring configuration value %d\n", setup->wValue);
usleep(10'000);
// Just ACK
status = read(fd, &status, 0);
if (status < 0) {
goto stall;
}
return;
I have a Qt project running on a icoremx6solo with linux.
I've setted up the graphics and run the code, but i can't handle touch inputs.
Enabling the input logging with
export QT_LOGGING_RULES="qt.qpa.input=true"
i discovered that the coordinates ar not setted, i think that this is the main problem with that:
qt.qpa.input: evdevtouch: /dev/input/event0: Protocol type B (multi)
qt.qpa.input: evdevtouch: /dev/input/event0: min X: 0 max X: -1
qt.qpa.input: evdevtouch: /dev/input/event0: min Y: 0 max Y: -1
qt.qpa.input: evdevtouch: /dev/input/event0: min pressure: 0 max pressure: 0
qt.qpa.input: evdevtouch: /dev/input/event0: device name: EP0790M09
but i can't find a way to calibrate that evdevtouch.
I tried runnin the executable with -plugin tslib attribute after executing the ts_calibrate command but the output is the same.
so, how can i fix that having a running touchscreen?
Thank you ianeperson!
From your answer I managed to get my xpt2046 5 inch touch screen working with Qt.
My code:
#include "setuptouchscreen.h"
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <linux/input.h>
#include <string.h>
#include <dirent.h>
#include <qdebug.h>
#define DEVICE_NAME "ADS7846 Touchscreen"
#define INPUT_PATH "/dev/input"
// got these values from file https://github.com/goodtft/LCD-show/blob/master/usr/99-calibration.conf-5-0
#define X_MIN 140
#define X_MAX 3951
#define Y_MIN 261
#define Y_MAX 3998
int setupTouchScreen() {
DIR* directory = opendir(INPUT_PATH);
if (!directory) {
qDebug("setupTouchScreen:: Failed to open %s.", INPUT_PATH);
return -1;
}
bool found = false;
struct dirent *entry = NULL;
while (!found && (entry = readdir(directory))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
char pathname[NAME_MAX + 1]; /* should always be big enough */
sprintf( pathname, "%s/%s", INPUT_PATH, entry->d_name );
qDebug("setupTouchScreen:: Path name: %s", entry->d_name);
int fd = open(pathname, O_RDONLY);
if (fd == NULL) {
puts ("setupTouchScreen:: Could not open device file - are you running as root");
}
else
{
char name[256] = "Unknown";
ioctl (fd, EVIOCGNAME(sizeof(name)), name);
qDebug("setupTouchScreen:: Input device name: %s", name);
if(strcmp(name, DEVICE_NAME ) != 0 ) {
qDebug("setupTouchScreen:: This is not the event file of the touchscreen: %s. Value is: %s", DEVICE_NAME, name);
} else {
qDebug("setupTouchScreen:: Found input file!");
found = true;
struct input_absinfo absval;
// Read the ioctl and display the current values
qDebug("setupTouchScreen:: Writing event struct ABS_X");
ioctl(fd, EVIOCGABS(ABS_X), &absval);
// check if a write is wanted - and do it
absval.minimum = X_MIN;
absval.maximum = X_MAX;
ioctl(fd, EVIOCSABS(ABS_X), &absval);
/////////////////
qDebug("setupTouchScreen:: Writing event struct ABS_Y");
ioctl(fd, EVIOCGABS(ABS_Y), &absval);
absval.minimum = Y_MIN;
absval.maximum = Y_MAX;
ioctl(fd, EVIOCSABS(ABS_Y), &absval);
}
}
close(fd);
}
closedir(directory);
if(!found)
{
qDebug("setupTouchScreen:: Could not find device file for device %s", DEVICE_NAME);
return -1;
}
qDebug("setupTouchScreen:: Success!");
return 0;
}
Looking at QT's source (qevdevtouchhandler.cpp) the calibration values (min, max, pressure…) are obtained directly from the device ioctl.
There doesn't seem to be a way in Qt to change these values.
Looking at the kernel source for my controller (ADS7843 on Atmel spi) there doesn't seem to a way to change the values from user space (/proc, /sys).
The following snippet seems to do the job – reading and writing the calibration values:
// check the parameter count
if (argc != 3 && argc != 5) {
puts ("Get usage: evgetset device absnumber");
puts ("Set usage: evgetset device absnumber valname value");
return (1);
}
// the first parameter is the device file name
fd = open(argv[1], O_RDONLY);
if (fd < 0) {
puts ("Could not open device file - are you running as root");
return (1);
}
// the second parameter is the parameter number
absnumber = atoi (argv[2]);
if (absnumber < 0 || absnumber > ABS_MAX) {
puts ("absnumber out of range");
return (1);
}
// Read the ioctl and display the current values
ioctl(fd, EVIOCGABS(absnumber), &absval);
printf ("Properties for %d\n", absnumber);
printf ("Value : %d\n", absval.value);
printf ("Minimum : %d\n", absval.minimum);
printf ("Maximum : %d\n", absval.maximum);
printf ("Fuzz : %d\n", absval.fuzz);
printf ("Flat : %d\n", absval.flat);
// printf ("Resolution : %d\n", absval.resolution);
// check if a write is wanted - and do it
if (argc == 5) {
valvalue = atoi (argv[4]);
if (!strcmp ("Value", argv[3])) absval.value = valvalue;
if (!strcmp ("Minimum", argv[3])) absval.minimum = valvalue;
if (!strcmp ("Maximum", argv[3])) puts ("Got Maximum");
if (!strcmp ("Maximum", argv[3])) absval.maximum = valvalue;
if (!strcmp ("Fuzz", argv[3])) absval.fuzz = valvalue;
if (!strcmp ("Flat", argv[3])) absval.flat = valvalue;
// if (!strcmp ("Resolution", argv[2]) absval.resolution = valvalue;
ioctl(fd, EVIOCSABS(absnumber), &absval);
}
// all done
close(fd);
I am struggling to find out, what steps are necessary to access a gpio-pin from a linux kernel module.
Maybe someone can explain it to me by a simple example. I like to use pin 4(input) and 33(output). My steps so far:
1.) Device Tree(dts): I leave the dts file untouched - Do I need to setup the pin 4 and 33 via pin control?
2.) kernel module: some pseudo code
gpio_is_valid(4)
gpio_request_one(4, GPIOF_DIR_IN | GPIOF_EXPORT_DIR_FIXED , "myPin4")
gpio_export(4, false)
gpio_get_value(4)
gpio_is_valid(33)
gpio_request_one(33, GPIOF_DIR_OUT | GPIOF_INIT_LOW | GPIOF_OPEN_SOURCE | GPIOF_EXPORT_DIR_FIXED , "myPin33")
gpio_export(33, false)
gpio_set_value(33, 1)
How to do it in a proper way?
I would suggest the combination of an own device tree file + a platform driver + character driver
0.) RTF
check how device trees(dts) are working
check how a platform device works
check how a character device works
gain some knowledge about gpios and dts
#gpio mappings
#subsystems using gpios
#Specifying GPIO information for devices
Read the informations provided by your SOC manufacturer.
The state-of-the-art way to access the gpios is via struct gpio_desc variables. They are created form the device tree.
1.) approach
To toggle a pin under linux you need to make shure, that 3 units are working togehter.
The pin-controller(pinctrl) defines how the output is driven. Open source, pull up etc.
The pin-multiplexer(pinmux) defines different functions for the pin.
The gpio-controller(gpioctrl) translates the gpio number. p.E.: 44 -> GPIO A 11
These components are implemented by the SOC manufacturer. For each platform there are differences. An example for the SAMA5D35xxx follows.
#the device tree
Define the pin controller
pinctrl#fffff200 {
pinctrl_myPins: myPins {
atmel,pins = <AT91_PIOA 2 AT91_PERIPH_GPIO AT91_PINCTRL_NONE // pin 1
AT91_PIOD 19 AT91_PERIPH_GPIO AT91_PINCTRL_NONE>; // pin 2
};
};
Create a node witch is linked to the own platform device:
myPins {
compatible = "myPlatformDevice";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_myPins>;
pin1 = <&pioA 2 GPIO_ACTIVE_HIGH>;
pin2 = <&pioD 19 GPIO_ACTIVE_HIGH>;
};
#create platform + char driver (pseudo code):
// ----------------------
// kernel message support(via dmesg)
// ----------------------
#define KMSG_DEBUG(fmt,args...) printk(KERN_DEBUG "myDrv" ": "fmt"\n", ##args)
#define KMSG_PERR(fmt,args...) printk(KERN_ERR "myDrv" ": "fmt"\n", ##args)
#define KMSG_PINFO(fmt,args...) printk(KERN_INFO "myDrv" ": "fmt"\n", ##args)
// ----------------------
// trace support via defining dMyDrvTrace
// ----------------------
#ifndef dMyDrvTrace
#define TRACE(...)
#else
#define TRACE(fmt,args...) printk(KERN_INFO "myDrv" ": [%s] "fmt"\n", __FUNCTION__, ##args)
#endif
typedef struct SMyDrvDrvData {
struct platform_device *pdev; //!< next device
// here goes the local/private data
int gpiod_pin1;
int gpiod_pin2;
u32 pin1;
u32 pin2;
} TMyDrvDrvData;
static struct dentry * gmyPlattformDrvDebugfsRootDir; //!< root dir at debugfs
static int myPlattformDrv_probe(struct platform_device *pdev);
static int myPlattformDrv_remove(struct platform_device *pdev);
#if defined(CONFIG_OF)
//! filter for the device tree class
static struct of_device_id gMyPlattformDrvdtsFilter[] = {
{.compatible = "myPlatformDevice"},
{}
};
MODULE_DEVICE_TABLE(of, gMyPlattformDrvdtsFilter);
#else
#define gmyPlattformDrvdtsFilter (NULL)
#endif
static struct platform_device *MyPlattformDrv_devs[] = {
};
static struct platform_driver myPlattformDrv_driver = {
.driver = {
.name = dMyPlattformDrvdriver,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(gMyPlattformDrvdtsFilter),
},
.probe = myPlattformDrv_probe,
.remove = myPlattformDrv_remove,
};
// char device
static dev_t gMyCharDev;
static struct class *gMyCharDevClass;
static struct cdev gMyCharDev_cdev;
static int dev_open (struct inode *, struct file *);
static int dev_release (struct inode *, struct file *);
static ssize_t dev_read (struct file *, char *, size_t, loff_t *);
static ssize_t dev_write (struct file *, const char *, size_t, loff_t *);
static const struct file_operations gMyCharDevOps =
{
.read = dev_read,
.open = dev_open,
.write = dev_write,
.release = dev_release
};
//! looks up for the gpio name and request it
static int get_gpio(struct platform_device *pdev, const char * name, int * pGPIOnum)
{
int n,i;
int r;
struct device_node * pDN;
TRACE("look at %s for %s ...", pdev->name, name);
// reset return value
*pGPIOnum = 0;
// parse device tree
// get device tree entries associated with the device
pDN = of_find_node_by_name(NULL, pdev->name);
// parse pins
n = of_gpio_named_count(pDN, name);
if (n <= 0) {
TRACE("no gpios found");
return -1;
}
for (i = 0; i < n; i++) {
// get pin number
*pGPIOnum = of_get_named_gpio(pDN,name, i);
if (*pGPIOnum == -EPROBE_DEFER) {
return r;
}
// check if pin number is valid
if (gpio_is_valid(*pGPIOnum)) {
// yes
// request pin
r = devm_gpio_request(&pdev->dev, *pGPIOnum, name);
if (r) {
return r;
} else {
r = gpio_direction_output(*pGPIOnum, 0);
}
if (r) return r;
}
}
}
return 0;
}
//! probes the platform driver
static int myPlattformDrv_probe(struct platform_device *pdev)
{
struct TMyDrvDrvData *priv;
int i,j,r,gpioNum, ret;
KMSG_PINFO("probe my driver ...");
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
KMSG_PERR("Failed to allocate memory for the private data structure");
return -ENOMEM;
}
priv->pdev = pdev;
platform_set_drvdata(pdev, priv);
TRACE("setup gpios ...");
r = get_gpio(pdev, "pin1", &gpioNum);
if (r) {
KMSG_PERR("Failed to find gpio \"pin1\" in device tree");
}
// save number
priv->gpiod_pin1 = gpioNum;
// create "pin1" debugfs entry
debugfs_create_u32("pin1", S_IRUGO, gmyPlattformDrvDebugfsRootDir, &priv->Pin1);
r = get_gpio(pdev, "pin2", &gpioNum);
if (r) {
KMSG_PERR("Failed to find gpio \"pin2\" in device tree");
}
// save number
priv->gpiod_pin2 = gpioNum;
// create "pin2" debugfs entry
debugfs_create_u32("pin1", S_IRUGO, gmyPlattformDrvDebugfsRootDir, &priv->Pin2);
// create device class
TRACE("create myCharDev char device class");
// create char dev region
ret = alloc_chrdev_region(&gMyCharDev, 0, 1, "myCharDev");
if( ret < 0) {
KMSG_PERR("alloc_chrdev_region error %i", ret);
goto error;
}
// create device class
if((gMyCharDevClass = class_create(THIS_MODULE, dSEK4DevClass)) == NULL)
{
KMSG_PERR("class_create error");
goto error_classCreate;
}
if(NULL == device_create(gMyCharDevClass, NULL, gMyCharDev, NULL, "myCharDev"))
{
KMSG_PERR("device_create error");
goto error_deviceCreate;
}
cdev_init(&gMyCharDev_cdev, &gMyCharDevOps);
ret = cdev_add(&gMyCharDev_cdev, gMyCharDev, 1);
if(-1 == ret) {
KMSG_PERR("cdev_add error %i", ret);
goto error_device_add;
return -1;
}
TRACE("added myCharDev char device");
return 0;
// error handling block
error_std:
error_device_add:
device_destroy(gMyCharDevClass, gMyCharDev);
error_deviceCreate:
class_destroy(gMyCharDevClass);
error_classCreate:
unregister_chrdev_region(gMyCharDev, 1);
error:
return -1;
}
For learning, I wrote 2 IPC programs, a server and a client that use message sending. For some reason, the server does not seem to be receiving the data it has been send. Could you give me some hints on how I could go about debugging such a problem?
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define CENTRAL_MAILBOX 1200
#define CLIENT_MAILBOX 1201
struct {
long priority;
int value;
int pid;
} msgp, cmbox;
int main(int argc, char **argv) {
int uid = 0; // Process id of the server(this process) set to 0
int length = -1; // Length of the structure we are going to send over
int result = -1; // Result of the msgrcv operation
int status = -1;
if ( argc != 2 ) {
fprintf(stderr, "Usage: ./server <temperature>\n");
exit(-1);
}
printf("[+] Starting server\n");
printf("[+] Creating the mailbox\n");
int server_msgid = msgget(CENTRAL_MAILBOX, 0600 | IPC_CREAT);
printf("[+] Creating a mailbox for the client process\n");
int client_msgid = msgget(CLIENT_MAILBOX, 0600 | IPC_CREAT);
printf("[+] Initializing data to be sent across\n");
msgp.priority = 1;
msgp.value = 31337;
msgp.pid = uid;
length = ( sizeof(msgp) > sizeof(long) ? sizeof(msgp)-sizeof(long) : sizeof(long)-sizeof(msgp) );
printf("[+] Calculating the size of the message we are about to send across=%d\n", length);
// msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
result = msgrcv(server_msgid, &cmbox, length, 1, 0);
printf("Result = %d\n", result);
printf("[+] Received message pid=%d, value=%d, priority=%ld\n", cmbox.pid, cmbox.value, cmbox.priority);
printf("[+] Sending message\n");
// int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
result = msgsnd(client_msgid, &msgp, length, 0);
printf("[+] Shutting down server\n");
status = msgctl(server_msgid, IPC_RMID, 0);
if ( status != 0 ) {
fprintf(stderr, "[*] ERROR: closing mailbox failed\n");
}
}
My Client :-
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define CENTRAL_MAILBOX 1200
#define CLIENT_MAILBOX 1201
struct {
long priority;
int value;
int pid;
} msgp, cmbox;
int main(int argc, char **argv) {
int temp = atoi(argv[1]);
int uid = getpid(); //7171;
int length, result, status;
if ( argc != 2 ) {
// TODO find actual process id
fprintf(stderr, "Usage: ./client <temperature>\n");
exit(-1);
}
printf("[+] Creating server mailbox\n");
int server_msgid = msgget(CENTRAL_MAILBOX, 0600 | IPC_CREAT);
printf("[+] Creating client mailbox\n");
int client_msgid = msgget(CLIENT_MAILBOX, 0600 | IPC_CREAT);
printf("[+] Initializing the data to be sent across\n");
cmbox.priority = 2;
cmbox.value = 1337;
cmbox.pid = uid;
length = ( sizeof(msgp) > sizeof(long) ? sizeof(msgp)-sizeof(long) : sizeof(long)-sizeof(msgp) );
printf("[+] Calculating the size of the message we are about to send across=%d\n", length);
printf("[+] Sending message to server\n");
result = msgsnd(server_msgid, &cmbox, length, 0);
printf("Result = %d\n", result);
result = msgrcv(client_msgid, &msgp, length, 1, 0);
printf("[+] Received message pid=%d, value=%d, priority=%ld\n", msgp.pid, msgp.value, msgp.priority);
printf("[+] Removing the mailbox\n");
status = msgctl(client_msgid, IPC_RMID, 0);
if ( status != 0 ) {
printf("Error when closing mailbox\n");
}
}
Your server blocks on the 1st msgrcv() because it expects message of type 1 (what you called priority, in your msgp, cmbox structures):
// msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
result = msgrcv(server_msgid, &cmbox, length, 1, 0);
When the client send a message of type 2:
printf("[+] Initializing the data to be sent across\n");
cmbox.priority = 2;
// [...]
printf("[+] Sending message to server\n");
result = msgsnd(server_msgid, &cmbox, length, 0);
Have a look at how to use the msgtyp value in the man page:
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
[...]
The msgp argument is a pointer to caller-defined structure of the following general form:
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[1]; /* message data */
};
[...]
msgrcv()
[...]
The argument msgtyp specifies the type of message requested as follows:
If msgtyp is 0, then the first message in the queue is read.
If msgtyp is greater than 0, then the first message in the queue of type msgtyp is read, unless MSG_EXCEPT was specified in msgflg, in which case the first message in the queue of type not equal to msgtyp will be read.
If msgtyp is less than 0, then the first message in the queue with the lowest type less than or equal to the absolute value of msgtyp will be read.
I have the following C program:
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
int main()
{
int fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NONBLOCK);
if(fd < 0)
{
perror("Could not open device");
}
printf("Device opened\n");
struct termios options;
tcgetattr(fd, &options);
cfmakeraw(&options);
cfsetispeed(&options, B19200);
cfsetospeed(&options, B19200);
tcsetattr(fd, TCSANOW, &options);
char txpacket[] = {0x23, 0x06, 0x00, 0x00, 0xdd, 0xf9};
ssize_t written = write(fd, txpacket, sizeof(txpacket));
printf("Written %d bytes\n", written);
printf("Starting to wait for target to respond\n");
while(1)
{
fd_set readset;
FD_ZERO(&readset);
FD_SET(fd, &readset);
int nCount = select(fd + 1, &readset, NULL, NULL, NULL);
if(nCount > 0)
{
if(FD_ISSET(fd, &readset))
{
int i;
char buffer[128];
ssize_t bytesread = read(fd, buffer, sizeof(buffer));
printf("Received %d bytes\n", bytesread);
for(i = 0; i < bytesread; i++)
{
printf(" %02x", buffer[i]);
}
}
}
}
}
This program opens the serial device /dev/ttyS0, writes a sequence of data to it and starts listening for a response. I get the following output:
Device opened
Written 6 bytes
Starting to wait for target to respond
Received 0 bytes
Received 0 bytes
Received 0 bytes
Received 0 bytes
Received 0 bytes
Received 0 bytes
...
And the application consumes 100% CPU. I'm not able to receive any data, even though the target hardware actually transmits it.
What is wrong?
read() returning 0 indicates the end-of-file condition. You should check for that and break out of the loop if it occurs.
As to what's causing that - end-of-file on a serial port indicates it has detected a hangup, meaning that the DCD line has been dropped.
You can set the CLOCAL flag in options.c_cflag to ignore the modem control lines, if your device doesn't set them properly.
You should try without the O_NONBLOCK flag. in raw mode, if the settings of c_cc[VMIN] and c_cc[VTIME] is 0, the serial port behave like this (according to man cfmakeraw) :
If data is available, read returns
immediately, with the lesser of the
number of bytes available, or the
number of bytes requested. If no data
is available, read returns 0
So what you should try is :
options->c_cc[VMIN]=1;