I'm trying to create FSG-3 buttons driver (currently only for power button) and need some help. The code is shown below. The problem is that after calling "netlink_broadcast(uevent_sock, skb, 0, 1, GFP_ATOMIC);", system craches with console message:

Synchronizing SCSI cache for disk sda: 
System halted.

Can anybody help me? I'm out of ideas:(.


/*
 * arch/arm/mach-ixp4xx/fsg-power.c
 *
 * Copyright (C) 2007 Zintis Petersons
 *
 * FSG buttons driver
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * ----------------------------------
 * 1.00  Oct 12, 2007 -- Initial version.
 *
 * 1.01  Oct 13, 2007 -- Code rewritten
 *             added "SEEN" and "SEQNUM" to hotplug event.
 * ----------------------------------
 *
 */

/*
 * Version Information
 */
#define DRIVER_VERSION "v1.01"
#define DRIVER_NAME "fsg-power.c"
#define DRIVER_DESC "FSG buttons driver"
#define DRIVER_AUTHOR "Zintis Petersons <Zintis.Petersons@e-mail.lv>"

#include <linux/module.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <asm/mach-types.h>

#include <linux/timer.h>
#include <asm/uaccess.h>
#include <linux/workqueue.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <net/sock.h>
#include <linux/kobject.h>

#define BUFFER_SIZE    2048    /* buffer for the variables */

//signed long SyncJiffies;
extern struct sock *uevent_sock;
extern u64 uevent_next_seqnum(void);

struct event_t {
    struct work_struct wq;
    char *name, *action;
//    unsigned long seen;     
};

static void add_msg(struct sk_buff *skb, char *msg)
{
    char *scratch;

    scratch = skb_put(skb, strlen(msg) + 1);
    strcpy(scratch, msg);
}

static void hotplug_button(struct work_struct *wq)
{
    struct event_t *event;
    char *scratch;
    static char buf[128];
    struct sk_buff *skb;
    u64 seq;

    event = container_of(wq, struct event_t, wq);
    
    if (!uevent_sock) {
        printk(KERN_ERR DRIVER_NAME ": unable to create netlink socket!\n");
        return -ENODEV;
    }    

    skb = alloc_skb(BUFFER_SIZE, GFP_ATOMIC);
    if (!skb)
        return;

    scratch = skb_put(skb, strlen(event->action) + 2);
    sprintf(scratch, "%s@", event->action);

    add_msg(skb, "HOME=/");
    add_msg(skb, "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
    add_msg(skb, "SUBSYSTEM=button");

    snprintf(buf, 128, "ACTION=%s", event->action);
    add_msg(skb, buf);
    snprintf(buf, 128, "BUTTON=%s", event->name);
    add_msg(skb, buf);
//    snprintf(buf, 128, "SEEN=%ld", event->seen);
//    add_msg(skb, buf);

    /* we will send an event, request a new sequence number */
    seq = uevent_next_seqnum();
    snprintf(buf, 128, "SEQNUM=%llu", (unsigned long long)seq);
    add_msg(skb, buf);

printk("%s %l\n", event->name, (unsigned long long)seq);

  /* to mcast group 1<<0 */
    NETLINK_CB(skb).dst_group = 1;
    
    /*multicast the message to all listening processes*/
    // Kernel memory allocation type. 
    // Typically, GFP_ATOMIC is used if from interrupt context;
    // GFP_KERNEL if otherwise.
    netlink_broadcast(uevent_sock, skb, 0, 1, GFP_ATOMIC);

    kfree(event);
}

static irqreturn_t fsg_sync_button_handler(int irq, void *dev_id)
{
    int pressed;
    struct event_t *event;

    //check button status
    gpio_line_get(FSG_SB_GPIO, &pressed);

    //create event
    event = (struct event_t *)kzalloc (sizeof(struct event_t), GFP_ATOMIC);
    if (!event)
        return IRQ_NONE;

    event->action = pressed ? "released" : "pressed";
    event->name = "sync";
/*
    if (pressed) {
    //button released
        //calculate time seen
        event->seen = (jiffies - SyncJiffies)/HZ;
    }
    else {
    // button pressed
        //fix start time
        event->seen = 0;
        SyncJiffies = jiffies;
    }
*/
    INIT_WORK(&event->wq, (void *)(void *)hotplug_button);
    schedule_work(&event->wq);

    return IRQ_HANDLED;
}

static int __init fsg_buttons_init(void)
{
    if (!(machine_is_fsg()))
        return 0;

    printk("%s %s - %s\n", DRIVER_NAME, DRIVER_VERSION, DRIVER_DESC);

    /* Configure interrupt input for SYNC button */
    set_irq_type(FSG_SB_IRQ, IRQT_BOTHEDGE);
    if (request_irq(FSG_SB_IRQ, &fsg_sync_button_handler, IRQF_DISABLED, "SYNC", NULL) < 0) {
        printk(KERN_DEBUG DRIVER_NAME ": SYNC button IRQ %d not available\n", FSG_SB_IRQ);
        return -EIO;
    }
    else
        printk(DRIVER_NAME ": SYNC button registered on IRQ%d\n", FSG_SB_IRQ);

    return 0;
}

static void __exit fsg_buttons_exit(void)
{
    if (!(machine_is_fsg()))
        return;

    free_irq(FSG_SB_IRQ, NULL);
}

module_init(fsg_buttons_init);
module_exit(fsg_buttons_exit);

MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");