What causes a segmentation fault on a call to inb_p()? - c

I am getting a segmentation fault when trying to read a port with inb_p( ). I'm compiling this on a Debian system running 2.6.6 kernel on an Intel D525 dual-core system (Advantech PCM 9389 SBC). Here is a sample program which illustrates the segfault.
What is the probable cause? How do I fix this?
Currently, I don't have any devices hooked up. Could this cause the segfault? I would have expected to get either a zero or some random byte, but not a segfault.
Other things I tried:
1) Declared the input variable as int instead of char.
2) Used iopl() instead of ioperm()
/*
* ioexample.c: very simple ioexample of port I/O
* very simple port i/o
* Compile with `gcc -O2 -o ioexample ioexample.c',
* and run as root with `./ioexample'.
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/io.h>
#define BASEPORT 0x0100 /* iobase for sample system */
#define FLIPC 0x01
#define FLIPST 0x0
#define DIPSWITCH 0x25
int main()
{
char cinput;
cinput = 0xff;
setuid(0);
printf("begin\n");
/* Get access to the ports */
if (ioperm(BASEPORT+DIPSWITCH, 10, 1))
{
perror("ioperm");
exit(EXIT_FAILURE);
}
printf("read the dipswitch with pause\n");
cinput = inb_p(BASEPORT+DIPSWITCH); // <=====SEGFAULT HERE
/* We don't need the ports anymore */
if (ioperm(BASEPORT+DIPSWITCH, 10, 0))
{
perror("ioperm");
exit(EXIT_FAILURE);
}
printf("Dipswitch setting: 0x%X", cinput);
exit(EXIT_SUCCESS);
}
/* end of ioexample.c */
Output:
root#debian:/home/howard/sources# ./ioexample
begin
read the dipswitch with pause
Segmentation fault
Edit: /proc/ioports did not list anything at address 0x100, so I tried several other port addresses that were listed, with the same result. Then I decided to try an output to a known parallel port location (0x0378), and outb did not cause a segfault. However, trying to read either 0x378 or 0x379 did cause a segfault. I am beginning to suspect that the problem is hardware related.

I found the problem. The call to inb_p() requires access to port 0x80 in addition to the port to be read.
Apparently, when I tried iopl(), I didn't call it correctly, because that should have worked.
The following code eliminated the segfault:
/* Get access to the ports */
if (ioperm(0x80, 1, 1))
{
perror("ioperm");
exit(EXIT_FAILURE);
}
if (ioperm(BASEPORT+DIPSWITCH, 10, 1))
{
perror("ioperm");
exit(EXIT_FAILURE);
}

Related

Writing to an I/O Address

I am using a VersaLogic Osprey Board and I am running Lubuntu 16.04 Xenial with GCC compiler 6.2.0. Writing in C, I cannot compile C++.
I am trying to read from the I/O address of the Watchdog timer, with the intent of later enabling it. According to the data sheet the I/O address is 1CA8 (WDT_CTL is the identifier of this address)
The code I have thus far is as follows:
#define WDT_CTL 0x1CA8
int main(void)
{
char *p_CTL = (char*) WDT_CTL;
printf("Attempting to print contents %c\n"),*p_CTL);
}
The code compiles, but I get a segmentation fault (core dumped) when I get to the important line, there are no other errors or warnings. As far as I know a seg fault is a "you can't access that memory location" error, but I am trying to read from the address that I found in the data sheet.
I have done some research and according to the datasheet the address I have is an I/O address, which seems to be different from a normal address. When I look at the address of a custom variable (char a) that address is 12 digits long as opposed to the four I am given for the watchdog timer.
Am I missing something? Is it a limitation of Lubuntu that I can no longer write directly to an I/O address? Do I need to use some specific command to do so? Do I need to enable something or change a setting?
Note that this thread is different from one with a similar title because that was done in Windows 7, not Lubuntu and that fix will not work here.
This is how I usually access the watchdog hardware:
This has worked on several different computers.
#include "watchdog.h"
#include <fcntl.h> /* open() */
#include <stdio.h> /* printf() */
#include <unistd.h> /* write() */
#include <string.h> /* strerror() */
#include <errno.h> /* errno */
#define APP_VERSION "1.1.0"
/* Look for the device in two places */
#define WD_KICK_DEV_NAME_1 "/dev/cs5535/gpio/5"
#define WD_KICK_DEV_NAME_2 "/dev/gpio/5"
#define WD_ENABLE_DEV_NAME_1 "/dev/cs5535/gpio/27"
#define WD_ENABLE_DEV_NAME_2 "/dev/gpio/27"
#define TEXT_WDOG_KICK "Ot0101"
#define TEXT_WDOG_ENABLE "1TO"
#define TEXT_WDOG_DISABLE "0TO"
static INT32 wd_kick_fd;
static INT32 wd_enable_fd;
/**
* Return the version
*/
const char *wd_get_version(void)
{
return(APP_VERSION);
}
/**
* Open GPIO-5 and GPIO-27
*/
INT32 wd_init(void)
{
if (((wd_kick_fd = open(WD_KICK_DEV_NAME_2, O_WRONLY)) < 0) &&
((wd_kick_fd = open(WD_KICK_DEV_NAME_1, O_WRONLY)) < 0))
{
printf("Failed to open '%s' or '%s': %s (%d)\n",
WD_KICK_DEV_NAME_1, WD_KICK_DEV_NAME_2, strerror(errno), errno);
return(FAILURE);
}
if (((wd_enable_fd = open(WD_ENABLE_DEV_NAME_2, O_WRONLY)) < 0) &&
((wd_enable_fd = open(WD_ENABLE_DEV_NAME_1, O_WRONLY)) < 0))
{
printf("Failed to open '%s' or '%s': %s (%d)\n",
WD_ENABLE_DEV_NAME_1, WD_ENABLE_DEV_NAME_2, strerror(errno), errno);
return(FAILURE);
}
return(SUCCESS);
}
/**
* Disabled via GPIO-27
*/
void wd_disable(void)
{
(void)write(wd_enable_fd, TEXT_WDOG_DISABLE, strlen(TEXT_WDOG_DISABLE));
}
/**
* Enabled via GPIO-27
*/
void wd_enable(void)
{
(void)write(wd_enable_fd, TEXT_WDOG_ENABLE, strlen(TEXT_WDOG_ENABLE));
}
/**
* Kick the watchdog by toggling GPIO 5
*/
void wd_kick(void)
{
(void)write(wd_kick_fd, TEXT_WDOG_KICK, strlen(TEXT_WDOG_KICK));
}
and the watchdog.h file contains:
#ifndef WATCHDOG_H_INCLUDED
#define WATCHDOG_H_INCLUDED
/**
* Grab the version of the kicker stuff
*/
const char *wd_get_version(void);
/**
* Do any initialization needed for the watchdog.
*
* #return SUCCESS or FAILURE
*/
INT32 wd_init(void);
/**
* Disables the watchdog.
* If watchdog cannot be disabled, then calls wd_kick()
*/
void wd_disable(void);
/**
* Enables the watchdog.
* If watchdog cannot be disabled, do nothing.
*/
void wd_enable(void);
/**
* Kicks the watchdog
*/
void wd_kick(void);
#endif /* WATCHDOG_H_INCLUDED */

Debian I2C driver for OLED screen not working. [ssd1306]

I have some driver code that I am testing out for use with an SSD1306 driven OLED screen which is 128x32 (same as the OLED adafruit model). I need this to run in debian (I am using Linario-4.4.9)
I have followed the Debian guides on how to start creating a file handler for the device, this can be seen as follows below. The only thing in oled.h is the device adress (0x3C) and the proto types. I followed the initialization approach taken on the adafruit github (as I tried their code out first on an Ardunio to ensure the screen does in fact work). I believe I may be doing something wrong but I'm not entirely sure what I am doing wrong. I have also listed my init process below.
#include <errno.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <linux/i2c-dev.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include "oled.h"
int oled;
int lcd_driver_init(void)
{
///< Begin the init proc.
int dloc = open("/dev/i2c-1", O_RDWR);
if (dloc < 0 )
{
fprintf(stderr, "Error opening i2c device\n");
return -1;
}
if(ioctl(dloc, I2C_SLAVE, SCR_ADDR) < 0)
{
fprintf(stderr, "Error in ioctl. Errno :%i\n",errno);
return -2;
}
oled = dloc;
fprintf(stderr, "init success, device open and local\n");
return EXIT_SUCCESS;
}
int oled_command( uint8_t cmd)
{
char command[2]= {0};
command[1] = cmd;
int check = (write(oled, command, 2));
return check;
}
void oled_cmd_start()
{
int check = (write(oled, 0x00, sizeof(uint8_t)));
if(check<0)
fprintf(stderr, "Errno set:: %i\n", errno);
return;
}
void oled_data_start()
{
uint8_t _data_start_[1] ={ 0x40 };
int check = (write(oled, _data_start_, sizeof(uint8_t)));
if(check<0)
fprintf(stderr, "Errno set oled_data_start:: %i\n", errno);
return;
}
int oled_data (uint8_t xmit)
{
int check = (write(oled, &xmit, (sizeof(uint8_t))));
if(check<0)
fprintf(stderr, "Errno set oled_data:: %i\n", errno);
return check;
}
INIT PROCESS
void sendcommand(unsigned char payload)
{
oled_data(0x00); //Control Byte - Command
oled_data(payload); //payload
}
void lcd_init(void)
{
sendcommand(0xAE);//--Set Display off
sendcommand(0x00);//--set low column address
sendcommand(0x10);//--set high column address
sendcommand(0x81);//--set contrast control register
sendcommand(0x7f);
sendcommand(0xa1);//--set segment re-map 95 to 0
sendcommand(0xA6);//--set normal display
sendcommand(0xa8);//--set multiplex ratio(1 to 16)
sendcommand(0x1f);//--duty 1/32
sendcommand(0xd3);//--set display offset
sendcommand(0x00);//--not offset
sendcommand(0xd5);//--set display clock divide ratio/oscillator frequency
sendcommand(0xf0);//--set divide ratio
sendcommand(0xd9);//--set pre-charge period
sendcommand(0x22);
sendcommand(0xda);//--set com pins hardware configuration
sendcommand(0x02);//disable left/right remap and set for sequential
sendcommand(0xdb);//--set vcomh
sendcommand(0x49);//--0.83*vref
sendcommand(0x8d);//--set DC-DC enable
sendcommand(0x14);//
sendcommand(0xAF);//--turn on oled panel
sendcommand(0xA4);//--Entire Display ON
}
Following this, I send alternating 0xFF to try and make stripes on the screen. The only thing that shows up is random pixels. Nothing coherent.
I have connected a logic analyzer to sniff the I2C lines, and it appears that when I have the LA connected, the I2C lines no longer function and ERRNO returns an IO fault (#5).
There doesn't ever seem to be an issue opening up the device to get the file pointer however.
I do get ERRNO as timeout sometimes, but I have read that this is just an issue with I2C devices using the protocal as write expects a quicker response than I2C might give.
I am also compiling with -std=c99 -O0 to ensure all of the inline functions are there as well as ensuring that loop variables are available.
If anyone can point me in the right direction of can point out some flaw in my approach it would be much appreciated. Thank you.
EDIT
I've checked the device tree and the i2c device is correctly enabled. However none of the i2c_freq speeds seem to be enabled. Could this be causing the timeouts and the garbage data transfer?
I have connected a logic analyzer to sniff the I2C lines, and it appears that when I have the LA connected, the I2C lines no longer function and ERRNO returns an IO fault (#5).
logic analyzer is just a device for measurement. It converts the captured data into timing diagrams, decodes protocol which you have set. So it will not be responsible for any I2C read write error (until your grounding and h/w connections are correct).
For timeout issue either you can try by decreasing the i2c clock-frequency or ioctl I2C_TIMEOUT.
It turns out the SOM has an internal regulator for the I2C lines to be 1V8 where as the SSD1306 chip is running at 3V3 causing information to be mishandled. This behavior wasn't documented on the SOM.
Applying a level shifting chip to the design allowed for proper communication.
If someone has this same problem, check your schematics for voltage mismatch levels.

Raspberry PI, raw reading a register value in C

Can someone please show me some example on how to read values from Raspberry Pi registers?
I need to check byte in AUX_MU_LSR_REG (0x7E21 5054) but i don't know how to do this without usage of any additional libraries.
I tried to:
(here i modify the oryginal post)
**************************Start of the code
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <errno.h>
#define BCM2708 0x20000000
#define UART_BASE (BCM2708+0x215000)
#define PAGE_SIZE (4*1024)
#define BLOCK_SIZE (4*1024)
int mem_fd;
void *uart_map;
volatile unsigned *uart;
int main()
{
printf("start\n");
if((mem_fd=open("/dev/mem",O_RDWR|O_SYNC))<0)
{
printf("can't open /dev/mem \n");
exit(-1);
}
else printf("stream_file open OK \n");
if((uart_map=malloc(BLOCK_SIZE))==0)
printf("malloc fail\n");
else printf("GPIO_mallocation OK %d \n", (int)uart_map );
uart_map=mmap (NULL, //any address in our space
BLOCK_SIZE, //map length
PROT_READ|PROT_WRITE, //Enable reading and writing to maped memmory
MAP_SHARED, //Shares with other processes
mem_fd, //File to map
UART_BASE //Offset toUART peripheral
);
if(uart_map==MAP_FAILED)
{
printf("mmap error %d", (int)uart_map);
exit(-1);
}
else printf("GPIO mapping OK \n");
uart=(volatile unsigned* )uart_map;
int i;
for(i=0;i<32;i++)
{
printf("adress:%x ",uart+i);
printf("value:%x\n",*(uart+i));
}
printf("stop\n");
return 0;
}
`
***********************end of code
Now i don't really remember how i display the 0 but in the above code i get pointer conflict.
What is the uart pointing to? In theory it should display the values but after few weeks i can't make it running.
Hope you can help me somehow
I've successfully compiled the example here and been able to read and write digital values from GPIO pins with a little modification to that code.
You're essentially heading in the right direction: mmap() a range specified in the datasheet for your particular GPIO, SPI or whichever register you need, and then read and write from that address. That's the basis of all the GPIO programming on the RPi.
There are some special bitfields that you should pay attention to, but as I mentioned, these are all in the datasheets for the Pi at elinux.org
note: to mmap() the system registers you need to be running as the superuser.

Reading and writing on I/O ports

I am trying to understand the following code:
#include<stdio.h>
#include<stdlib.h>
#include<sys/io.h>
#define baseport 0x378
int main()
{
int b;
if(ioperm(baseport,3,1))
{
perror("ioperm");
exit(1);
}
outb(0,baseport);
usleep(1000000);
printf("\n the status: %x,\n",inb(baseport));
if (ioperm(baseport,3,0)) {perror("ioperm"); exit(1);}
exit(0);
}
The output is 0xff, 255 in decimal, whether I write on Port 1 or Port 0 (using outb()). I cannot understand why it is 255 when I am writing 0 to it.
The result of doing inb(0x378) is hardware-dependent. Some chips return the value you have written previously with outb, and some other chips just return garbage. In any case, it is not the port to read bytes from a potentially connected device.
First you need to see the port's capabilities, input, output or both. If it can be configured as both, you have to set it to the respective mode and only then you can expect the right behavior.

Programming rs232 to lcd (Linux)

I'm using a pc1602f PowerTip directly connected to the PC parallel port using this scheme:
http://www.beyondlogic.org/parlcd/parlcd.htm
All well as energizes the LCD and shows me the front row with black blocks, until then fine but now I want to send information through the parallel port.
If you look at the page you will see that there is a source to send information to the lcd, but uses windows libraries: huh:
I leave my code attempted to become linux.
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/io.h>
#define PORTADDRESS 0x3f8
#define DATA PORTADDRESS+0
#define STATUS PORTADDRESS+1
#define CONTROL PORTADDRESS+2
main(int argc, char **argv)
{char string[] = {"Testing 1,2,3"};
int count;
int len;
char init[10];
init[0] = 0x0F; /* Init Display */
init[1] = 0x01; /* Clear Display */
init[2] = 0x38; /* Dual Line / 8 Bits */
if (ioperm(PORTADDRESS,1,1))
fprintf(stderr, "No se puede acceder al: %x\n", PORTADDRESS), exit(1);
outb(CONTROL, inb(CONTROL) & 0xDF);
outb(CONTROL, inb(CONTROL) & 0x08);
for (count = 0; count <= 2; count++)
{
outb(DATA, init[count]);
outb(CONTROL,inb(CONTROL) | 0x01);
sleep(20);
outb(CONTROL,inb(CONTROL) & 0xFE);
sleep(20);
}
outb(CONTROL, inb(CONTROL) & 0xF7);
len = strlen(string);
for (count = 0; count < len; count++)
{
outb(DATA, string[count]);
outb(CONTROL,inb(CONTROL) | 0x01);
sleep(2);
outb(CONTROL,inb(CONTROL) & 0xFE);
sleep(2);
}
}
Compiles perfectly but when I want to try it as root and run it throws me
root#ubuntu: /
media/E80C-30D5/LCD/build #./lcd
Segmentation fault (`core 'generated)
root#ubuntu: /media/E80C-30D5/LCD/build #
Looking at dmesg I find this.
[3176.691837] lcd [3867] general
protection ip: 400cb4 sp: 7fff887ad290 error: 0 in lcd [+2000 400 000]root#ubuntu: / media/E80C-30D5/LCD/build #
I put the dmesg log of ttyS*
root # ubuntu: / media/E80C-30D5/LCD/build # dmesg | grep ttyS
[2.335717] serial8250: ttyS0 at I / O 0x3f8 (irq = 4) is a 16550A
[2.335817] serial8250: ttyS1 at I / O 0x2f8 (irq = 3) is a 16550A
[2.336100] 00:0 b: ttyS1 at I / O 0x2f8 (irq = 3) is a 16550A
[2.336207] 00:0 c: ttyS0 at I / O 0x3f8 (irq = 4) is a 16550A
root # ubuntu: / media/E80C-30D5/LCD/build #
Do not get it to run, you can help me please?
The code you're trying to run will not run in a "modern" operating system. Linux and versions of Windows after 16-bit will not let you perform operations that directly access ports or memory. Access to memory goes through a memory manager so the addresses you're specifying are not really the ones you end up accessing; and ports are accessed only by device drivers that are part of the operating system's kernel.
I'm not very sure, but if your main purpose is just to output some bytes via the serial port, then most of your code is not necessary, as you can simply write to a file that wraps the device driver to the port. There's a little more detail on this in this article.
If you really need to do bit-twiddling with the serial port chips, you can find some more information in the bottom line of this serial port mini-howto. Be warned that writing your own device driver is considered advanced.
EDIT: I just noticed your question mentions the parallel port but the port number in your code (3F8) is that for one of the serial ports. Hint: If the plug you're connecting to is a 9 pin port, then it's definitely a serial port. If it's a 25 pin port and female (holes, not pins) then it's probably a parallel port, if male it's probably a serial port. Or you could measure voltages: Serial port signals go up to +/- 9V or so, while the parallel port signals are from 0 to 5V.
I agree with others that you should write to the appropriate device file rather than perform I/O directly, if possible.
However, for the sake of completeness:
ioperm(PORTADDRESS,1,1)
should be
ioperm(PORTADDRESS,4,1)
Also, use parentheses to avoid bugs that results from incorrect macro expansion, like this:
#define PORTADDRESS (0x3f8)
#define DATA (PORTADDRESS+0)
#define STATUS (PORTADDRESS+1)
#define CONTROL (PORTADDRESS+2)
Instead of writing to an address outside your address space (which would make it PC-only if it would work), just write to a file (/dev/lp0, or wherever your parport is) instead.
Your LCD seems to talk serial, so why not use the serial port instead? Then your program would write to /dev/ttyS0 or something similar.

Resources