Related
I am learning Wayland client programming and now I'm trying to make transparent surface.
I created a buffer and paint each pixels to shared memory. I am using WL_SHM_FORMAT_ARGB8888 format but it is not working as I expected.
As I understand, 0x00ff0000 means 255 red, 0 green, 0 blue and 0 alpha so the pixel should not shown. But it shows semi-transparent red surface to me.
Below is entire source code working on Weston compositor.
// main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wayland-client.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include <unistd.h>
#include "xdg-shell.h"
struct wl_display *display = NULL;
struct wl_compositor *compositor = NULL;
struct wl_surface *surface;
struct zxdg_shell_v6 *xdg_shell = NULL;
struct zxdg_surface_v6 *xdg_surface;
struct zxdg_toplevel_v6 *xdg_toplevel;
struct wl_shm *shm;
struct wl_buffer *buffer;
void *shm_data;
int WIDTH = 480;
int HEIGHT = 360;
//===============
// Xdg
//===============
static void xdg_shell_ping_handler(void *data, struct zxdg_shell_v6 *xdg_shell,
uint32_t serial)
{
zxdg_shell_v6_pong(xdg_shell, serial);
printf("ping pong!\n");
}
static const struct zxdg_shell_v6_listener xdg_shell_listener = {
.ping = xdg_shell_ping_handler,
};
static void xdg_toplevel_configure_handler(void *data,
struct zxdg_toplevel_v6 *xdg_toplevel, int32_t width, int32_t height,
struct wl_array *states)
{
printf("Configure: %dx%d\n", width, height);
}
static void xdg_toplevel_close_handler(void *data,
struct zxdg_toplevel_v6 *xdg_toplevel)
{
printf("Close\n");
}
static const struct zxdg_toplevel_v6_listener xdg_toplevel_listener = {
.configure = xdg_toplevel_configure_handler,
.close = xdg_toplevel_close_handler,
};
static void xdg_surface_configure_handler(void *data,
struct zxdg_surface_v6 *xdg_surface, uint32_t serial)
{
fprintf(stderr, "xdg_surface_configure_handler().\n");
zxdg_surface_v6_ack_configure(xdg_surface, serial);
}
static const struct zxdg_surface_v6_listener xdg_surface_listener = {
.configure = xdg_surface_configure_handler,
};
//=================
// File system
//=================
static int set_cloexec_or_close(int fd)
{
long flags;
if (fd == -1) {
return -1;
}
flags = fcntl(fd, F_GETFD);
if (flags == -1)
goto err;
if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
goto err;
return fd;
err:
close(fd);
return -1;
}
static int create_tmpfile_cloexec(char *tmpname)
{
int fd;
#ifdef HAVE_MKOSTEMP
fd= mkostemp(tmpname, O_CLOEXEC);
if (fd >= 0) {
unlink(tmpname);
}
#else
fd = mkstemp(tmpname);
if (fd >= 0) {
fd = set_cloexec_or_close(fd);
unlink(tmpname);
}
#endif
return fd;
}
int os_create_anonymous_file(off_t size)
{
static const char template[] = "/blusher-shared-XXXXXX";
const char *path;
char *name;
int fd;
path = getenv("XDG_RUNTIME_DIR");
if (!path) {
errno = ENOENT;
return -1;
}
name = malloc(strlen(path) + sizeof(template));
if (!name) {
return -1;
}
strcpy(name, path);
strcat(name, template);
fd = create_tmpfile_cloexec(name);
free(name);
if (fd < 0) {
return -1;
}
if (ftruncate(fd, size) < 0) {
close(fd);
return -1;
}
return fd;
}
//==============
// Painting
//==============
static void paint_pixels()
{
uint32_t *pixel = shm_data;
fprintf(stderr, "Painting pixels.\n");
for (int n = 0; n < WIDTH * HEIGHT; ++n) {
if (n > 1100 && n < 1200) {
pixel[n] = 0xffff0000; // please ignore this
} else {
pixel[n] = 0x00ff0000;
}
}
}
static struct wl_buffer* create_buffer()
{
struct wl_shm_pool *pool;
int stride = WIDTH * 4; // 4 bytes per pixel
int size = stride * HEIGHT;
int fd;
struct wl_buffer *buff;
fd = os_create_anonymous_file(size);
if (fd < 0) {
fprintf(stderr, "Creating a buffer file for %d B filed: %m\n",
size);
exit(1);
}
shm_data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (shm_data == MAP_FAILED) {
fprintf(stderr, "mmap filed: %m\n");
close(fd);
exit(1);
}
pool = wl_shm_create_pool(shm, fd, size);
buff = wl_shm_pool_create_buffer(
pool,
0,
WIDTH,
HEIGHT,
stride,
WL_SHM_FORMAT_ARGB8888
);
wl_shm_pool_destroy(pool);
return buff;
}
static void create_window()
{
buffer = create_buffer();
wl_surface_attach(surface, buffer, 0, 0);
// wl_surface_damage(surface, 0, 0, WIDTH, HEIGHT);
wl_surface_commit(surface);
}
static void shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
{
// struct display *d = data;
// d->formats |= (1 << format);
fprintf(stderr, "Format %d\n", format);
}
struct wl_shm_listener shm_listener = {
shm_format,
};
//==============
// Global
//==============
static void global_registry_handler(void *data, struct wl_registry *registry,
uint32_t id, const char *interface, uint32_t version)
{
if (strcmp(interface, "wl_compositor") == 0) {
fprintf(stderr, "Interface is <wl_compositor>.\n");
compositor = wl_registry_bind(
registry,
id,
&wl_compositor_interface,
version
);
} else if (strcmp(interface, "zxdg_shell_v6") == 0) {
fprintf(stderr, "Interface is <zxdg_shell_v6>.\n");
xdg_shell = wl_registry_bind(registry, id, &zxdg_shell_v6_interface, 1);
} else if (strcmp(interface, "wl_shm") == 0) {
fprintf(stderr, "Interface is <wl_shm>.\n");
shm = wl_registry_bind(registry, id, &wl_shm_interface, 1);
wl_shm_add_listener(shm, &shm_listener, NULL);
} else {
// printf("(%d) Got a registry event for <%s> id <%d>\n",
// version, interface, id);
}
}
static void global_registry_remover(void *data, struct wl_registry *registry,
uint32_t id)
{
printf("Got a registry losing event for <%d>\n", id);
}
static const struct wl_registry_listener registry_listener = {
global_registry_handler,
global_registry_remover
};
//==============
// Main
//==============
int main(int argc, char *argv[])
{
(void)argc;
(void)argv;
display = wl_display_connect(NULL);
if (display == NULL) {
fprintf(stderr, "Can't connect to display.\n");
exit(1);
}
printf("Connected to display.\n");
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, ®istry_listener, NULL);
wl_display_dispatch(display);
// wl_display_roundtrip(display);
// Check compositor.
fprintf(stderr, " - Checking compositor...\n");
if (compositor == NULL) {
fprintf(stderr, "Can't find compositor.\n");
exit(1);
}
// Check surface.
fprintf(stderr, " - Checking surface...\n");
surface = wl_compositor_create_surface(compositor);
if (surface == NULL) {
fprintf(stderr, "Can't create surface.\n");
exit(1);
}
if (xdg_shell == NULL) {
fprintf(stderr, "Haven't got a Xdg shell.\n");
exit(1);
}
zxdg_shell_v6_add_listener(xdg_shell, &xdg_shell_listener, NULL);
// Check shell surface.
xdg_surface =
zxdg_shell_v6_get_xdg_surface(xdg_shell, surface);
zxdg_surface_v6_add_listener(xdg_surface, &xdg_surface_listener, NULL);
xdg_toplevel = zxdg_surface_v6_get_toplevel(xdg_surface);
zxdg_toplevel_v6_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL);
// Signal that the surface is ready to be configured.
wl_surface_commit(surface);
// Wait for the surface to be configured.
wl_display_roundtrip(display);
create_window();
paint_pixels();
// wl_surface_attach(surface, buffer, 0, 0);
// wl_surface_commit(surface);
while (wl_display_dispatch(display) != -1) {
;
}
wl_display_disconnect(display);
printf("Disconnected from display.\n");
return 0;
}
# Makefile
default:
wayland-scanner client-header /usr/share/wayland-protocols/unstable/xdg-shell/xdg-shell-unstable-v6.xml xdg-shell.h
wayland-scanner public-code /usr/share/wayland-protocols/unstable/xdg-shell/xdg-shell-unstable-v6.xml xdg-shell.c
gcc -lwayland-client -lwayland-egl -lEGL -lGLESv2 main.c xdg-shell.c
But when I set pixel to 0x00000000, then it is fully transparent.
And in some tests, subsurfaces are completely transparent even there are color bits set. I want to know what is prevent the transparent toplevel surface with color bits.
If your wayland compositor supports alpha-compositing-v1 protocol you can use it do what you want.
In short; get a reference to zwp_alpha_compositing_v1 struct in you registry handler then after creating your surface you set it like this:
struct zwp_blending_v1* blending = zwp_alpha_compositing_v1_get_blending(<YOUR_ZWP_ALPHA_COMPOSITING>, <YOUR_WL_SURFACE>);
zwp_blending_v1_set_blending(blending, ZWP_BLENDING_V1_BLENDING_EQUATION_STRAIGHT);
This is an equivalent to (src_alpha, one_minus_src_alpha) blending.
I attach here my kernel module I developed and the test I am using at application level
memalloc.c
/*
* DMA memory allocation
* This kernel module allocates coherent, non-cached memory
* and returns the physical and virtual address of the allocated buffer
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include "memalloc.h"
#define DEVICE_NAME "memalloc"
// Max number of buffers
#define BUFFER_MAX_NUMBER 16
// Structure for buffer information
struct bufferInfo {
int active;
int size;
dma_addr_t handle;
int virtualAddress;
int *kernelAddress;
};
static struct bufferInfo bufferInfoTable[BUFFER_MAX_NUMBER];
// Defines which buffer is currently active - for mmap
static int activeBufferID;
struct memAllocIF {
struct device *device_p;
dev_t dev_node;
struct cdev cdev;
struct class *class_p;
};
static struct memAllocIF interface;
// Methods
static int releaseBuffer(int i)
{
if (i > BUFFER_MAX_NUMBER)
{
printk("Wrong bufferID %d\n", i);
return -1;
}
printk("Releasing buffer %d\n", i);
bufferInfoTable[i].active = 0;
dma_free_coherent(NULL, bufferInfoTable[i].size, bufferInfoTable[i].kernelAddress, bufferInfoTable[i].handle);
return 0;
}
static int reserveBuffer(size_t size)
{
int i;
for (i = 0; i < BUFFER_MAX_NUMBER; i++)
{
if (bufferInfoTable[i].active == 0)
{
printk("Reserving buffer %d\n", i);
bufferInfoTable[i].active = 1;
break;
}
}
if (i < BUFFER_MAX_NUMBER)
{
bufferInfoTable[i].kernelAddress = dma_alloc_coherent(NULL, size, &bufferInfoTable[i].handle, GFP_KERNEL);
if (bufferInfoTable[i].kernelAddress == NULL)
{
printk("Allocation failure\n");
return -1;
}
bufferInfoTable[i].size = (int)size;
return i;
}
else
{
printk("No buffer available\n");
return -1;
}
}
static void cleanup(void)
{
int i;
for (i = 0; i < BUFFER_MAX_NUMBER; i++)
{
if (bufferInfoTable[i].active != 0)
{
dma_free_coherent(NULL, bufferInfoTable[i].size, bufferInfoTable[i].kernelAddress, bufferInfoTable[i].handle);
bufferInfoTable[i].active = 0;
}
}
}
static unsigned int memAllocGetVirtual (int i)
{
if (i > BUFFER_MAX_NUMBER)
{
printk("Wrong bufferID %d\n", i);
return -1;
}
if (bufferInfoTable[i].active == 0)
{
printk("Inactive buffer - ID %d\n", i);
return -1;
}
printk("request for buffer %d: vaddr = %X\n", i, (unsigned int)bufferInfoTable[i].virtualAddress);
return bufferInfoTable[i].virtualAddress;
}
static unsigned int memAllocGetPhysical (int i)
{
if (i > BUFFER_MAX_NUMBER)
{
printk("Wrong bufferID %d\n", i);
return -1;
}
return (unsigned int)bufferInfoTable[i].handle;
}
static long memAllocIoctl (struct file *fd, unsigned int cmd, unsigned long arg)
{
printk("received command %u arg %lu\n", cmd, arg);
switch(cmd)
{
case MEMALLOC_RESERVE:
return reserveBuffer(arg);
break;
case MEMALLOC_RELEASE:
return releaseBuffer(arg);
break;
case MEMALLOC_GET_VIRTUAL:
return memAllocGetVirtual(arg);
break;
case MEMALLOC_GET_PHYSICAL:
return memAllocGetPhysical(arg);
break;
case MEMALLOC_ACTIVATE_BUFFER:
if (arg > BUFFER_MAX_NUMBER || bufferInfoTable[arg].active == 0)
{
printk("Wrong bufferID %lu\n", arg);
return -1;
}
activeBufferID = arg;
return arg;
break;
default:
printk("Wrong command: %d\n", cmd);
return -1;
break;
}
}
static int memAllocMmap (struct file *fd, struct vm_area_struct *vma)
{
bufferInfoTable[activeBufferID].virtualAddress = dma_common_mmap(interface.device_p, vma, bufferInfoTable[activeBufferID].kernelAddress, bufferInfoTable[activeBufferID].handle, vma->vm_end-vma->vm_start);
printk("mmap for idx %d: vaddr = %X\n", activeBufferID, (int)bufferInfoTable[activeBufferID].virtualAddress);
return bufferInfoTable[activeBufferID].virtualAddress;
}
static int memAllocRelease(struct inode *in, struct file *fd)
{
cleanup();
return 0;
}
static int memAllocOpen(struct inode *ino, struct file *file)
{
file->private_data = container_of(ino->i_cdev, struct memAllocIF, cdev);
return 0;
}
static struct file_operations fops = {
.unlocked_ioctl = memAllocIoctl,
.mmap = memAllocMmap,
.release = memAllocRelease,
.open = memAllocOpen
};
static int __init memAllocInit(void)
{
int rc;
int i;
static struct class *local_class_p = NULL;
printk("Loading DMA allocation module\n");
// Allocate a character device from the kernel for this driver
rc = alloc_chrdev_region(&interface.dev_node, 0, 1, DEVICE_NAME);
if (rc)
{
printk("Unable to get a char device number\n");
return rc;
}
// Initialize the ter device data structure before registering the character device with the kernel
cdev_init(&interface.cdev, &fops);
rc = cdev_add(&interface.cdev, interface.dev_node, 1);
if (rc)
{
printk("Unable to add char device\n");
cdev_del(&interface.cdev);
return rc;
}
// Create the device in sysfs which will allow the device node in /dev to be created
local_class_p = class_create(THIS_MODULE, DEVICE_NAME);
interface.class_p = local_class_p;
// Create the device node in /dev so the device is accessible as a character device
interface.device_p = device_create(interface.class_p, NULL, interface.dev_node, NULL, DEVICE_NAME);
if (IS_ERR(interface.device_p))
{
printk("Unable to create the device\n");
class_destroy(interface.class_p);
cdev_del(&interface.cdev);
return rc;
}
for (i = 0; i < BUFFER_MAX_NUMBER; i++)
{
bufferInfoTable[activeBufferID].active = 0;
}
return 0;
}
static void __exit my_memAllocExit(void)
{
printk("Module unloading\n");
cleanup();
cdev_del(&interface.cdev);
device_destroy(interface.class_p, interface.dev_node);
class_destroy(interface.class_p);
unregister_chrdev_region(interface.dev_node, 1);
}
module_init(memAllocInit);
module_exit(my_memAllocExit);
MODULE_AUTHOR("me");
MODULE_DESCRIPTION("Create a buffer and return physical and virtual address, for DMA userspace driver");
MODULE_LICENSE("GPL");
memalloc.h
#ifndef MEMALLOC_H
#define MEMALLOC_H
#ifdef __cplusplus
extern "C" {
#endif
#include <linux/types.h>
#include <asm/ioctl.h>
static long memAllocIoctl (struct file *, unsigned int, unsigned long);
static int memAllocMmap (struct file *, struct vm_area_struct *);
static int memAllocRelease (struct inode *, struct file *);
static int memAllocOpen(struct inode *, struct file *);
enum memAllocCmd
{
MEMALLOC_RESERVE = 0,
MEMALLOC_RELEASE = 1,
MEMALLOC_GET_VIRTUAL = 2,
MEMALLOC_GET_PHYSICAL = 3,
MEMALLOC_ACTIVATE_BUFFER = 4,
};
#ifdef __cplusplus
}
#endif
#endif /* MEMALLOC_H */
test.c
#include <stdlib.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdio.h>
// derive this from memalloc.h
enum memAllocCmd
{
MEMALLOC_RESERVE = 0,
MEMALLOC_RELEASE = 1,
MEMALLOC_GET_VIRTUAL = 2,
MEMALLOC_GET_PHYSICAL = 3,
MEMALLOC_ACTIVATE_BUFFER = 4,
};
int main ()
{
int memAllocFd;
volatile int iVaddr;
volatile int oVaddr;
volatile int iVaddr_2;
volatile int oVaddr_2;
volatile void * iPaddr;
volatile void * oPaddr;
int iBufID;
int oBufID;
int size = 2048;
memAllocFd = open("/dev/memalloc", O_RDWR);
// create iBuffer
iBufID = ioctl(memAllocFd, MEMALLOC_RESERVE, size);
iPaddr = (void *)ioctl(memAllocFd, MEMALLOC_GET_PHYSICAL, iBufID);
ioctl(memAllocFd, MEMALLOC_ACTIVATE_BUFFER, iBufID);
iVaddr = (int)mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, memAllocFd, 0);
ioctl(memAllocFd, MEMALLOC_GET_VIRTUAL, iBufID);
/*
if (iVaddr != iVaddr_2)
{
printf("Error: virtual addresses for buffer %d don't match: %X %X\n", iBufID, iVaddr, iVaddr_2);
}
*/
// create oBuffer
oBufID = ioctl(memAllocFd, MEMALLOC_RESERVE, size);
oPaddr = (void *)ioctl(memAllocFd, MEMALLOC_GET_PHYSICAL, oBufID);
ioctl(memAllocFd, MEMALLOC_ACTIVATE_BUFFER, oBufID);
oVaddr = (int)mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, memAllocFd, 0);
ioctl(memAllocFd, MEMALLOC_GET_VIRTUAL, oBufID);
/*
if (oVaddr != oVaddr_2)
{
printf("Error: virtual addresses for buffer %d don't match: %X %X\n", oBufID, oVaddr, oVaddr_2);
}
*/
ioctl(memAllocFd, MEMALLOC_RELEASE, iBufID);
ioctl(memAllocFd, MEMALLOC_RELEASE, oBufID);
return 0;
}
Result of the test is
received command 0 arg 2048
Reserving buffer 0
received command 3 arg 0
received command 4 arg 0
mmap for idx 0: vaddr = 0
received command 0 arg 2048
Reserving buffer 1
received command 3 arg 1
received command 4 arg 1
mmap for idx 1: vaddr = 0
received command 1 arg 0
Releasing buffer 0
received command 1 arg 1
Releasing buffer 1
Which means that all ioctl calls with arg=MEMALLOC_GET_VIRTUAL are not executed, while all the others are.
What can be the reason for that?
Thanks,
Max
I'm trying to increment and decrement the index of a list of readings, however, everytime I run update(), m_notifIndex gets set back to zero. I'm sorry if this type of question has been asked before, but after looking for the answer for an hour, I gave up and went on to ask my first question here. Sorry for the bad formatting too, again, this is my first time posting.
Display.cpp
#include "display.h"
#include <time.h>
#include <unistd.h>
#include <iostream>
Display::Display(unsigned int width, unsigned int height, unsigned int num_sensors, std::string* sensors, const std::string& dir, Message* messages) {
m_notifIndex = 0;
}
void Display::update() {
if (m_showNotif) {
//when there are no more messages, show the notifications
while (!m_notifications->isEmpty()) {
//first draw the notification if there is a warning in the current index
if (m_notifications->sensors[m_notifIndex] != NULL) {
m_oled->clear(PAGE);
drawSensor();
m_oled->display();
sleep(1);
//keep updating the message and notif stack when there are no inputs
while (m_a->pinRead() == HIGH && m_right->pinRead() == HIGH && m_left->pinRead() == HIGH && m_b->pinRead() == HIGH) {
m_messages->updateMsgStack();
updateNotif();
if (!m_messages->isEmpty() || m_notifications->warnings[m_notifIndex])
return; //break away from the loop if a new message comes in
}
//listen in for inputs
if (m_right->pinRead() == LOW) {
//wait until the button is released
while (m_right->pinRead() == LOW) {}
if (m_notifIndex == m_num_sensors-1)
m_notifIndex = 0; //wrap around when at the end
else m_notifIndex++;
}
else if (m_left->pinRead() == LOW) {
while (m_left->pinRead() == LOW) {}
if (m_notifIndex == 0)
m_notifIndex = m_num_sensors-1; //wrap around when at the beginning
else m_notifIndex--;
}
else if (m_a->pinRead() == LOW) {
while (m_a->pinRead() == LOW) {}
m_showNotif = false;
return;
}
checkForPanic();
}
else { //when the current index has no warning
if (m_notifIndex == m_num_sensors-1)
m_notifIndex = 0; //wrap around when at the end
else m_notifIndex++;
}
}
}
}
void Display::updateNotif() {
std::string line;
for (unsigned int i = 0; i < sizeof(m_sensors)/sizeof(m_sensors[0]); i++) {
const char* filePath = (m_notifDir + m_sensors[i] + ".txt").c_str();
if (m_messages->exists(filePath)) {
usleep(10000);
try {
m_messages->m_incMsg.open(filePath);
while(std::getline(m_messages->m_incMsg, line)) {
m_notifications->addWarning (line, i);
}
m_messages->m_incMsg.close();
}
catch (std::ios_base::failure& e) {
std::cerr << e.what() << '\n' << std::endl;
}
}
else m_notifications->removeWarning (i); //delete the warning when the file doesnt exist
}
}
void Display::checkForPanic() {
if (m_b->pinRead() == LOW) {
time_t hold;
time(&hold);
while (m_b->pinRead() == LOW) {
if (time(NULL) > hold + 3) { //if the timer reaches 3 seconds
//m_messages->sendReply("Miner_Emergency");
return;
}
}
}
}
Display.h
#ifndef OLED_DISPLAY_H_
#define OLED_DISPLAY_H_
#include <sstream>
#include <string>
#include "image/image.h"
#include "oled/Edison_OLED.h"
#include "message.h"
#include "DataStructs/Notif.h"
#include "gpio/gpio.h"
#define SLEEPTIMEOUT 20
class Display {
public:
Display(unsigned int width, unsigned int height, unsigned int num_sensors, std::string* sensors, const std::string& dir, Message* messages);
void run();
~Display();
private:
edOLED* m_oled;
const unsigned int m_height, m_width;
Image* m_miner;
Image* m_checkin;
Image* m_reminder;
Image* m_blast;
Image* m_go_to;
Image* m_goto_zone;
bool m_isSleeping, m_showNotif;
time_t m_timer;
Message* m_messages;
std::string* m_sensors;
std::string m_notifDir;
NotifHandler* m_notifications;
unsigned int m_notifIndex;
const unsigned int m_num_sensors;
gpio* m_up;
gpio* m_down;
gpio* m_left;
gpio* m_right;
gpio* m_a;
gpio* m_b;
void drawImage(Image* image);
void update();
void drawMessage(MsgNode* message);
void drawTime();
void drawSensor();
void updateNotif();
void checkForPanic();
};
#endif //OLED_DISPLAY_H_
I am trying to write a SPI driver for Angstrom Linux 2.6.36 for Gumstix Overo Fire. My driver keeps on crashing in the interrupt handler. Here is the full code
#include <linux/init.h>
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/cdev.h>
#include <linux/spi/spi.h>
#include <linux/string.h>
#include <asm/uaccess.h>
#include <linux/kernel.h>
#include <mach/gpio.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/hrtimer.h>
#define IRQ_PIN 10
#define SPI_BUFF_SIZE 4
#define USER_BUFF_SIZE 128
#define SPI_BUS 1
#define SPI_BUS_CS1 1
#define SPI_BUS_SPEED 1500000
unsigned char *buff_even = 0;
unsigned char *buff_odd = 0;
unsigned char *temp_buff = 0;
unsigned int sample_counter = 0;
unsigned int buff_counter = 0;
unsigned int current_buffer = 0;
unsigned int local_current_buffer = 0;
unsigned int local_sample_counter = 0;
unsigned int num_reads = 0;
unsigned int num_miss_samples = 0;
unsigned int regval = 0;
#define LIMIT (4000)
#define BUFF_SIZE (4*LIMIT)
#define MAJOR_NUM 100
#define READ_CURR_COUNTER _IOWR(MAJOR_NUM, 1, int)
#define READ_BUFF _IOWR(MAJOR_NUM, 2, int)
#define READ_CURR_BUFF_NO _IOWR(MAJOR_NUM, 3, int)
#define READ_REGISTER _IOWR(MAJOR_NUM, 4, unsigned char)
#define WRITE_REGISTER _IOWR(MAJOR_NUM, 5, int)
#define START_READ _IOWR(MAJOR_NUM, 6, int)
#define STOP_READ _IOWR(MAJOR_NUM, 7, int)
const char this_driver_name[] = "adc";
static int running = 0;
static int resetting = 0;
static int reading = 0;
struct spike_control
{
struct spi_message msg;
struct spi_transfer transfer;
u8 *tx_buff;
u8 *rx_buff;
};
static struct spike_control spike_ctl;
struct spike_dev
{
struct semaphore spi_sem;
struct semaphore fop_sem;
dev_t devt;
struct cdev cdev;
struct class *class;
struct spi_device *spi_device;
char *user_buff;
u8 test_data;
int irq;
};
static struct spike_dev spike_dev;
static DEFINE_MUTEX(list_lock);
static DEFINE_MUTEX(count_lock);
static int status;
static void spike_completion_handler(void *arg)
{
local_sample_counter++;
if (sample_counter >= local_sample_counter + 1)
num_miss_samples++;
if (current_buffer == 0)
{
buff_even[buff_counter++] = spike_ctl.rx_buff[0];
buff_even[buff_counter++] = spike_ctl.rx_buff[1];
buff_even[buff_counter++] = spike_ctl.rx_buff[2];
buff_even[buff_counter++] = spike_ctl.rx_buff[3];
}
else if (current_buffer == 1)
{
buff_odd[buff_counter++] = spike_ctl.rx_buff[0];
buff_odd[buff_counter++] = spike_ctl.rx_buff[1];
buff_odd[buff_counter++] = spike_ctl.rx_buff[2];
buff_odd[buff_counter++] = spike_ctl.rx_buff[3];
}
memset(spike_ctl.rx_buff, 0, SPI_BUFF_SIZE);
if (sample_counter == LIMIT)
{
buff_counter = 0;
mutex_lock(&count_lock);
if (current_buffer == 0)
current_buffer = 1;
else
current_buffer = 0;
sample_counter = 0;
mutex_unlock(&count_lock);
local_sample_counter = 0;
}
}
static irqreturn_t adc_handler(int irq, void *dev_id)
{
sample_counter++;
spi_message_init(&spike_ctl.msg);
spike_ctl.msg.complete = spike_completion_handler;
spike_ctl.msg.context = NULL;
spike_ctl.transfer.tx_buf = NULL;
spike_ctl.transfer.rx_buf = spike_ctl.rx_buff;
spike_ctl.transfer.len = 4;
spi_message_add_tail(&spike_ctl.transfer, &spike_ctl.msg);
status = spi_async(spike_dev.spi_device, &spike_ctl.msg);
return IRQ_HANDLED;
}
static void resetbuffers(void)
{
local_sample_counter = 0;
sample_counter = 0;
num_miss_samples = 0;
current_buffer = 0;
buff_counter = 0;
memset(spike_ctl.rx_buff, 0, SPI_BUFF_SIZE);
memset(spike_ctl.tx_buff, 0, SPI_BUFF_SIZE);
memset(buff_even, 0, BUFF_SIZE);
memset(buff_odd, 0, BUFF_SIZE);
memset(temp_buff, 0, BUFF_SIZE);
}
static int read_register(unsigned char addr)
{
if (down_interruptible(&spike_dev.spi_sem))
return -ERESTARTSYS;
if (!spike_dev.spi_device)
{
up(&spike_dev.spi_sem);
return -ENODEV;
}
memset(spike_ctl.rx_buff, 0, SPI_BUFF_SIZE);
memset(spike_ctl.tx_buff, 0, SPI_BUFF_SIZE);
spike_ctl.transfer.tx_buf = spike_ctl.tx_buff;
spike_ctl.transfer.rx_buf = spike_ctl.rx_buff;
spike_ctl.transfer.len = 1;
spike_ctl.tx_buff[0] = 0x11;//Stop read data continuous
spi_message_init(&spike_ctl.msg);
spi_message_add_tail(&spike_ctl.transfer, &spike_ctl.msg);
status = spi_sync(spike_dev.spi_device, &spike_ctl.msg);
__asm__ __volatile__("ldr r0,=0x2710\n\t"
".LOOP1:\n\t"
"subs r0,r0,#1\n\t"
"bne .LOOP1\n\t");
spike_ctl.tx_buff[0] = 0x20 + addr;//Address of register
spi_message_init(&spike_ctl.msg);
spi_message_add_tail(&spike_ctl.transfer, &spike_ctl.msg);
status = spi_sync(spike_dev.spi_device, &spike_ctl.msg);
__asm__ __volatile__("ldr r0,=0x2710\n\t"
".LOOP2:\n\t"
"subs r0,r0,#1\n\t"
"bne .LOOP2\n\t");
spike_ctl.tx_buff[0] = 0;//Number of registers to read minus 1
spi_message_init(&spike_ctl.msg);
spi_message_add_tail(&spike_ctl.transfer, &spike_ctl.msg);
status = spi_sync(spike_dev.spi_device, &spike_ctl.msg);
__asm__ __volatile__("ldr r0,=0x2710\n\t"
".LOOP3:\n\t"
"subs r0,r0,#1\n\t"
"bne .LOOP3\n\t");
spi_message_init(&spike_ctl.msg);
spi_message_add_tail(&spike_ctl.transfer, &spike_ctl.msg);
status = spi_sync(spike_dev.spi_device, &spike_ctl.msg);
__asm__ __volatile__("ldr r0,=0x2710\n\t"
".LOOP4:\n\t"
"subs r0,r0,#1\n\t"
"bne .LOOP4\n\t");
printk(KERN_ALERT "%x\n",spike_ctl.rx_buff[0]);
regval = spike_ctl.rx_buff[0];
// spike_ctl.tx_buff[0] = 0x10;//Start read data continuous
// spi_message_init(&spike_ctl.msg);
// spi_message_add_tail(&spike_ctl.transfer, &spike_ctl.msg);
// status = spi_sync(spike_dev.spi_device, &spike_ctl.msg);
up(&spike_dev.spi_sem);
return status;
}
static int write_register(unsigned char addr, unsigned char val)
{
if (down_interruptible(&spike_dev.spi_sem))
return -ERESTARTSYS;
if (!spike_dev.spi_device)
{
up(&spike_dev.spi_sem);
return -ENODEV;
}
memset(spike_ctl.rx_buff, 0, SPI_BUFF_SIZE);
memset(spike_ctl.tx_buff, 0, SPI_BUFF_SIZE);
spike_ctl.transfer.tx_buf = spike_ctl.tx_buff;
spike_ctl.transfer.rx_buf = spike_ctl.rx_buff;
spike_ctl.transfer.len = 1;
spike_ctl.tx_buff[0] = 0x11;//Stop read data continuous
spi_message_init(&spike_ctl.msg);
spi_message_add_tail(&spike_ctl.transfer, &spike_ctl.msg);
status = spi_sync(spike_dev.spi_device, &spike_ctl.msg);
__asm__ __volatile__("ldr r0,=0x2710\n\t"
".LOOP5:\n\t"
"subs r0,r0,#1\n\t"
"bne .LOOP5\n\t");
spike_ctl.tx_buff[0] = 0x40 + addr;//Address of register
spi_message_init(&spike_ctl.msg);
spi_message_add_tail(&spike_ctl.transfer, &spike_ctl.msg);
status = spi_sync(spike_dev.spi_device, &spike_ctl.msg);
__asm__ __volatile__("ldr r0,=0x2710\n\t"
".LOOP6:\n\t"
"subs r0,r0,#1\n\t"
"bne .LOOP6\n\t");
spike_ctl.tx_buff[0] = 0;//Number of registers to read minus 1
spi_message_init(&spike_ctl.msg);
spi_message_add_tail(&spike_ctl.transfer, &spike_ctl.msg);
status = spi_sync(spike_dev.spi_device, &spike_ctl.msg);
__asm__ __volatile__("ldr r0,=0x2710\n\t"
".LOOP7:\n\t"
"subs r0,r0,#1\n\t"
"bne .LOOP7\n\t");
spike_ctl.tx_buff[0] = val;//Value to write
spi_message_init(&spike_ctl.msg);
spi_message_add_tail(&spike_ctl.transfer, &spike_ctl.msg);
status = spi_sync(spike_dev.spi_device, &spike_ctl.msg);
__asm__ __volatile__("ldr r0,=0x2710\n\t"
".LOOP8:\n\t"
"subs r0,r0,#1\n\t"
"bne .LOOP8\n\t");
spike_ctl.tx_buff[0] = 0x10;//Start read data continuous
spi_message_init(&spike_ctl.msg);
spi_message_add_tail(&spike_ctl.transfer, &spike_ctl.msg);
status = spi_sync(spike_dev.spi_device, &spike_ctl.msg);
// spike_ctl.tx_buff[0] = 0x10;//Start read data continuous
// spi_message_init(&spike_ctl.msg);
// spi_message_add_tail(&spike_ctl.transfer, &spike_ctl.msg);
// status = spi_sync(spike_dev.spi_device, &spike_ctl.msg);
up(&spike_dev.spi_sem);
return status;
}
static long adc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
unsigned char addr;
switch(cmd)
{
case READ_CURR_COUNTER:
mutex_lock(&count_lock);
local_current_buffer = current_buffer;
local_sample_counter = sample_counter;
mutex_unlock(&count_lock);
copy_to_user((void *)arg, &local_sample_counter, sizeof(unsigned int));
printk(KERN_ALERT "Read current counter %d\n",local_sample_counter);
break;
case READ_BUFF:
mutex_lock(&count_lock);
local_current_buffer = current_buffer;
local_sample_counter = sample_counter;
mutex_unlock(&count_lock);
if (local_current_buffer == 0)
{
memcpy(temp_buff,buff_odd, BUFF_SIZE);
// memset(temp_buff, 'Q', BUFF_SIZE);
}
else
{
memcpy(temp_buff,buff_even, BUFF_SIZE);
// memset(temp_buff, 'T', BUFF_SIZE);
}
copy_to_user((void *)arg, temp_buff, BUFF_SIZE);
num_reads++;
break;
case READ_CURR_BUFF_NO:
mutex_lock(&count_lock);
local_current_buffer = current_buffer;
local_sample_counter = sample_counter;
mutex_unlock(&count_lock);
copy_to_user((void *)arg, &local_current_buffer, sizeof(int));
break;
case READ_REGISTER:
get_user(addr, (unsigned char *)arg);
disable_irq(spike_dev.irq);
read_register(addr);
enable_irq(spike_dev.irq);
copy_to_user((void *)arg, ®val, sizeof(int));
break;
case WRITE_REGISTER:
disable_irq(spike_dev.irq);
enable_irq(spike_dev.irq);
//copy_to_user((void *)arg, &local_current_buffer, sizeof(int));
break;
default:
return -ENOTTY;
}
return 1;
}
static int spike_open(struct inode *inode, struct file *filp)
{
int status = 0;
if (down_interruptible(&spike_dev.fop_sem))
return -ERESTARTSYS;
if (!spike_dev.user_buff) {
spike_dev.user_buff = kmalloc(USER_BUFF_SIZE, GFP_KERNEL);
if (!spike_dev.user_buff)
status = -ENOMEM;
}
up(&spike_dev.fop_sem);
return status;
}
static int spike_probe(struct spi_device *spi_device)
{
if (down_interruptible(&spike_dev.spi_sem))
return -EBUSY;
spike_dev.spi_device = spi_device;
printk(KERN_ALERT "SPI[%d] max_speed_hz %d Hz\n", spi_device->chip_select, spi_device->max_speed_hz);
up(&spike_dev.spi_sem);
return 0;
}
static int spike_remove(struct spi_device *spi_device)
{
if (down_interruptible(&spike_dev.spi_sem))
return -EBUSY;
spike_dev.spi_device = NULL;
up(&spike_dev.spi_sem);
return 0;
}
static int __init add_spike_device_to_bus(void)
{
struct spi_master *spi_master;
struct spi_device *spi_device;
struct device *pdev;
char buff[64];
int status = 0;
spi_master = spi_busnum_to_master(SPI_BUS);
if (!spi_master)
{
printk(KERN_ALERT "spi_busnum_to_master(%d) returned NULL\n",
SPI_BUS);
printk(KERN_ALERT "Missing modprobe omap2_mcspi?\n");
return -1;
}
spi_device = spi_alloc_device(spi_master);
if (!spi_device)
{
put_device(&spi_master->dev);
printk(KERN_ALERT "spi_alloc_device() failed\n");
return -1;
}
spi_device->chip_select = SPI_BUS_CS1;
/* Check whether this SPI bus.cs is already claimed */
snprintf(buff, sizeof(buff), "%s.%u",
dev_name(&spi_device->master->dev),
spi_device->chip_select);
pdev = bus_find_device_by_name(spi_device->dev.bus, NULL, buff);
if (pdev)
{
/* We are not going to use this spi_device, so free it */
spi_dev_put(spi_device);
/*
* There is already a device configured for this bus.cs
* It is okay if it us, otherwise complain and fail.
*/
if (pdev->driver && pdev->driver->name && strcmp(this_driver_name, pdev->driver->name))
{
printk(KERN_ALERT
"Driver [%s] already registered for %s\n",
pdev->driver->name, buff);
status = -1;
}
}
else
{
spi_device->max_speed_hz = SPI_BUS_SPEED;
spi_device->mode = SPI_MODE_0;
spi_device->bits_per_word = 8;
spi_device->irq = -1;
spi_device->controller_state = NULL;
spi_device->controller_data = NULL;
strlcpy(spi_device->modalias, this_driver_name, SPI_NAME_SIZE);
status = spi_add_device(spi_device);
if (status < 0)
{
spi_dev_put(spi_device);
printk(KERN_ALERT "spi_add_device() failed: %d\n",
status);
}
}
put_device(&spi_master->dev);
return status;
}
static struct spi_driver spike_driver = {
.driver = {
.name = this_driver_name,
.owner = THIS_MODULE,
},
.probe = spike_probe,
.remove = __devexit_p(spike_remove),
};
static int __init spike_init_spi(void)
{
int error;
spike_ctl.tx_buff = kmalloc(SPI_BUFF_SIZE, GFP_KERNEL | GFP_DMA);
if (!spike_ctl.tx_buff)
{
error = -ENOMEM;
goto spike_init_error;
}
spike_ctl.rx_buff = kmalloc(SPI_BUFF_SIZE, GFP_KERNEL | GFP_DMA);
if (!spike_ctl.rx_buff)
{
error = -ENOMEM;
goto spike_init_error;
}
error = spi_register_driver(&spike_driver);
if (error < 0)
{
printk(KERN_ALERT "spi_register_driver() failed %d\n", error);
goto spike_init_error;
}
error = add_spike_device_to_bus();
if (error < 0)
{
printk(KERN_ALERT "add_spike_to_bus() failed\n");
spi_unregister_driver(&spike_driver);
goto spike_init_error;
}
spike_dev.irq = OMAP_GPIO_IRQ(IRQ_PIN);
return 0;
spike_init_error:
if (spike_ctl.tx_buff) {
kfree(spike_ctl.tx_buff);
spike_ctl.tx_buff = 0;
}
if (spike_ctl.rx_buff) {
kfree(spike_ctl.rx_buff);
spike_ctl.rx_buff = 0;
}
return error;
}
static ssize_t spike_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
size_t len;
ssize_t status = 0;
if (!buff)
return -EFAULT;
if (*offp > 0)
return 0;
if (down_interruptible(&spike_dev.fop_sem))
return -ERESTARTSYS;
printk(KERN_ALERT "Interrupt triggered %d Missed packet %d\n", sample_counter, num_miss_samples);
up(&spike_dev.fop_sem);
return status;
}
static ssize_t spike_write(struct file *filp, const char __user *buff, size_t count, loff_t *f_pos)
{
size_t len;
ssize_t status = 0;
if (down_interruptible(&spike_dev.fop_sem))
return -ERESTARTSYS;
memset(spike_dev.user_buff, 0, 16);
len = count > 8 ? 8 : count;
if (copy_from_user(spike_dev.user_buff, buff, len))
{
status = -EFAULT;
goto spike_write_done;
}
/* we'll act as if we looked at all the data */
status = count;
/* but we only care about the first 5 characters */
if (!strnicmp(spike_dev.user_buff, "inc", 3))
{
disable_irq(spike_dev.irq);
write_register(1,0x62);
resetbuffers();
printk(KERN_ALERT "4000 samples per second\n");
enable_irq(spike_dev.irq);
}
if (!strnicmp(spike_dev.user_buff, "dec", 3))
{
disable_irq(spike_dev.irq);
write_register(1,0x52);
resetbuffers();
printk(KERN_ALERT "1000 samples per second\n");
enable_irq(spike_dev.irq);
}
if (!strnicmp(spike_dev.user_buff, "stop", 4))
{
disable_irq(spike_dev.irq);
resetbuffers();
printk(KERN_ALERT "Driver stopped\n");
}
spike_write_done:
up(&spike_dev.fop_sem);
return status;
}
static const struct file_operations spike_fops = {
.owner = THIS_MODULE,
.open = spike_open,
.read = spike_read,
.write = spike_write,
.unlocked_ioctl = adc_ioctl,
};
static int __init spike_init_cdev(void)
{
int error;
spike_dev.devt = MKDEV(0, 0);
error = alloc_chrdev_region(&spike_dev.devt, 0, 1, this_driver_name);
if (error < 0)
{
printk(KERN_ALERT "alloc_chrdev_region() failed: %d \n",
error);
return -1;
}
cdev_init(&spike_dev.cdev, &spike_fops);
spike_dev.cdev.owner = THIS_MODULE;
error = cdev_add(&spike_dev.cdev, spike_dev.devt, 1);
if (error)
{
printk(KERN_ALERT "cdev_add() failed: %d\n", error);
unregister_chrdev_region(spike_dev.devt, 1);
return -1;
}
return 0;
}
static int __init spike_init_class(void)
{
spike_dev.class = class_create(THIS_MODULE, this_driver_name);
if (!spike_dev.class)
{
printk(KERN_ALERT "class_create() failed\n");
return -1;
}
if (!device_create(spike_dev.class, NULL, spike_dev.devt, NULL, this_driver_name))
{
printk(KERN_ALERT "device_create(..., %s) failed\n",
this_driver_name);
class_destroy(spike_dev.class);
return -1;
}
return 0;
}
static int __init spike_init(void)
{
int result;
memset(&spike_dev, 0, sizeof(spike_dev));
memset(&spike_ctl, 0, sizeof(spike_ctl));
sema_init(&spike_dev.spi_sem, 1);
sema_init(&spike_dev.fop_sem, 1);
buff_even = kmalloc(BUFF_SIZE, GFP_KERNEL);
buff_odd = kmalloc(BUFF_SIZE, GFP_KERNEL);
temp_buff = kmalloc(BUFF_SIZE, GFP_KERNEL);
if ( buff_even == 0 )
printk(KERN_ALERT "Failed to allocate buffer even\n");
if ( buff_odd == 0 )
printk(KERN_ALERT "Failed to allocate buffer odd\n");
if ( temp_buff == 0 )
printk(KERN_ALERT "Failed to temp buffer\n");
if (spike_init_cdev() < 0)
goto fail_1;
if (spike_init_class() < 0)
goto fail_2;
if (spike_init_spi() < 0)
goto fail_3;
result = request_irq(spike_dev.irq, adc_handler, IRQF_TRIGGER_RISING, "adc", &spike_dev);
if (result < 0)
{
printk(KERN_ALERT "request_irq failed: %d\n", result);
return -1;
}
return 0;
fail_3:
device_destroy(spike_dev.class, spike_dev.devt);
class_destroy(spike_dev.class);
fail_2:
cdev_del(&spike_dev.cdev);
unregister_chrdev_region(spike_dev.devt, 1);
fail_1:
return -1;
}
module_init(spike_init);
static void __exit spike_exit(void)
{
disable_irq(spike_dev.irq);
free_irq(spike_dev.irq, &spike_dev);
gpio_free(IRQ_PIN);
spi_unregister_device(spike_dev.spi_device);
spi_unregister_driver(&spike_driver);
device_destroy(spike_dev.class, spike_dev.devt);
class_destroy(spike_dev.class);
cdev_del(&spike_dev.cdev);
unregister_chrdev_region(spike_dev.devt, 1);
if (spike_ctl.tx_buff)
kfree(spike_ctl.tx_buff);
if (spike_ctl.rx_buff)
kfree(spike_ctl.rx_buff);
if (spike_dev.user_buff)
kfree(spike_dev.user_buff);
if ( buff_even != 0 )
kfree(buff_even);
if ( buff_odd != 0 )
kfree(buff_odd);
if ( temp_buff != 0 )
kfree(temp_buff);
printk(KERN_ALERT "Interrupt triggered %d Missed packet %d\n", sample_counter, num_miss_samples);
}
module_exit(spike_exit);
The interrupt handler crashes on the line
status = spi_async(spike_dev.spi_device, &spike_ctl.msg);
If i comment out this line everything runs fine but of course no data is read from SPI.
Muhammad, maybe you solved this issue by the time.
However, what I see is that you're using mutexes in the SPI completion handler (spike_completion_handler), which is executed in a context that cannot sleep.
Therefore, I would advise to use spinlocks instead.
This is safe to do, because count_lock always protects code that does not sleep.
That may not be the cause of your problem (did not read the whole code), but it's still better to do it this way.
I know there is an Objective-C wrapper around YAJL, but this is a really fat thing which blows up the whole JASON parser to a ridiculous huge amount of 21 files, many of them with tiny scroll bars.
So to keep my app binary small I'd like to stick with the C-version of that parser. But I'm having a hard time finding any useful documentation for this rather than the wrapper.
Maybe someone who used the C-base can point out such a tutorial or documentation?
The documentation with C examples can be found here: http://lloyd.github.com/yajl/
The github repository with examples can be found here : https://github.com/lloyd/yajl
Here is a C example that reformats JSON from stdin:
#include <yajl/yajl_parse.h>
#include <yajl/yajl_gen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int reformat_null(void * ctx)
{
yajl_gen g = (yajl_gen) ctx;
return yajl_gen_status_ok == yajl_gen_null(g);
}
static int reformat_boolean(void * ctx, int boolean)
{
yajl_gen g = (yajl_gen) ctx;
return yajl_gen_status_ok == yajl_gen_bool(g, boolean);
}
static int reformat_number(void * ctx, const char * s, size_t l)
{
yajl_gen g = (yajl_gen) ctx;
return yajl_gen_status_ok == yajl_gen_number(g, s, l);
}
static int reformat_string(void * ctx, const unsigned char * stringVal,
size_t stringLen)
{
yajl_gen g = (yajl_gen) ctx;
return yajl_gen_status_ok == yajl_gen_string(g, stringVal, stringLen);
}
static int reformat_map_key(void * ctx, const unsigned char * stringVal,
size_t stringLen)
{
yajl_gen g = (yajl_gen) ctx;
return yajl_gen_status_ok == yajl_gen_string(g, stringVal, stringLen);
}
static int reformat_start_map(void * ctx)
{
yajl_gen g = (yajl_gen) ctx;
return yajl_gen_status_ok == yajl_gen_map_open(g);
}
static int reformat_end_map(void * ctx)
{
yajl_gen g = (yajl_gen) ctx;
return yajl_gen_status_ok == yajl_gen_map_close(g);
}
static int reformat_start_array(void * ctx)
{
yajl_gen g = (yajl_gen) ctx;
return yajl_gen_status_ok == yajl_gen_array_open(g);
}
static int reformat_end_array(void * ctx)
{
yajl_gen g = (yajl_gen) ctx;
return yajl_gen_status_ok == yajl_gen_array_close(g);
}
static yajl_callbacks callbacks = {
reformat_null,
reformat_boolean,
NULL,
NULL,
reformat_number,
reformat_string,
reformat_start_map,
reformat_map_key,
reformat_end_map,
reformat_start_array,
reformat_end_array
};
static void
usage(const char * progname)
{
fprintf(stderr, "%s: reformat json from stdin\n"
"usage: json_reformat [options]\n"
" -m minimize json rather than beautify (default)\n"
" -u allow invalid UTF8 inside strings during parsing\n",
progname);
exit(1);
}
int
main(int argc, char ** argv)
{
yajl_handle hand;
static unsigned char fileData[65536];
/* generator config */
yajl_gen g;
yajl_status stat;
size_t rd;
int retval = 0;
int a = 1;
g = yajl_gen_alloc(NULL);
yajl_gen_config(g, yajl_gen_beautify, 1);
yajl_gen_config(g, yajl_gen_validate_utf8, 1);
/* ok. open file. let's read and parse */
hand = yajl_alloc(&callbacks, NULL, (void *) g);
/* and let's allow comments by default */
yajl_config(hand, yajl_allow_comments, 1);
/* check arguments.*/
while ((a < argc) && (argv[a][0] == '-') && (strlen(argv[a]) > 1)) {
unsigned int i;
for ( i=1; i < strlen(argv[a]); i++) {
switch (argv[a][i]) {
case 'm':
yajl_gen_config(g, yajl_gen_beautify, 0);
break;
case 'u':
yajl_config(hand, yajl_dont_validate_strings, 1);
break;
default:
fprintf(stderr, "unrecognized option: '%c'\n\n",
argv[a][i]);
usage(argv[0]);
}
}
++a;
}
if (a < argc) {
usage(argv[0]);
}
for (;;) {
rd = fread((void *) fileData, 1, sizeof(fileData) - 1, stdin);
if (rd == 0) {
if (!feof(stdin)) {
fprintf(stderr, "error on file read.\n");
retval = 1;
}
break;
}
fileData[rd] = 0;
stat = yajl_parse(hand, fileData, rd);
if (stat != yajl_status_ok) break;
{
const unsigned char * buf;
size_t len;
yajl_gen_get_buf(g, &buf, &len);
fwrite(buf, 1, len, stdout);
yajl_gen_clear(g);
}
}
stat = yajl_complete_parse(hand);
if (stat != yajl_status_ok) {
unsigned char * str = yajl_get_error(hand, 1, fileData, rd);
fprintf(stderr, "%s", (const char *) str);
yajl_free_error(hand, str);
retval = 1;
}
yajl_gen_free(g);
yajl_free(hand);
return retval;
}