Linux控制器驱动程序,同步来自多个用户模式应用程序的访问

时间:2018-12-03 21:18:26

标签: c linux synchronization usb driver

我有USB设备,命令通过默认控制管道发送(收到响应)。 SetIllumLed命令用于设置led的亮度(从0到255)。两个用户模式应用程序在循环中将ioctl发送到我们的驱动程序。第一个应用程序将亮度设置为0,另一个应用程序将其设置为255。目标是观察LED以恒定频率闪烁。但是,如果我杀死一个应用程序并重新启动它,而另一个应用程序正在运行,则会发生不同步的情况,并且闪烁频率会发生变化。

设备上下文和排队的请求结构的定义:

struct smx900_packet
{
    int in_size;
    unsigned char *in_buffer;
    int out_size;
    unsigned char *out_buffer;
};

#define IOCTL_SEND_COMMAND          _IOWR('c', 0, struct smx900_packet*)
#define IOCTL_GET_STATUS            _IOWR('c', 1, int*)

#define BUFFER_MIN_LEN          2
#define BUFFER_MAX_LEN          64

#define MAX(a, b)       ((a > b) ? (a) : (b))
#define MIN(a, b)       ((a < b) ? (a) : (b))

#define TRUE    1
#define FALSE   0

struct smx900_queue_item
{
    struct smx900_queue_item *next;                         // memset
    _Bool occupied;                                         // memset
    _Bool finished;                                         // memset
    _Bool discard;                                          // memset
    int result;                                             // memset
    unsigned char *buffer;
    struct file *pfile;
    struct smx900_packet packet;
};

struct smx900_context
{
    struct usb_device *pdevice;
    struct usb_interface *pinterface;
    struct mutex lock;
    struct kref cref;
    struct task_struct *thread;
    wait_queue_head_t wait_queue;
    wait_queue_head_t wait_queue_2;
    atomic_t pending_count;                                 // kzalloc
    atomic_t discard_count;                                 // kzalloc
    struct smx900_queue_item *first;                        // kzalloc
    struct smx900_queue_item *last;                         // kzalloc
    _Bool terminate;                                        // kzalloc
    struct smx900_queue_item queue[MAX_REQUEST_COUNT];      // kzalloc
};

功能:

struct smx900_context* smx900_alloc_context(struct usb_interface *pinterface)
{
    struct task_struct *thread;
    struct smx900_context *context;

    context = kzalloc(sizeof(*context), GFP_KERNEL);

    if (!context)
    {
        printk(KERN_ERR "smx900: failed to allocate memory");
        return NULL;
    }

    thread = kthread_create((int (*)(void*))request_thread, context, "smx900 request thread");

    if (!thread)
    {
        printk(KERN_ERR "smx900: failed to create thread");
        kfree(context);
        return NULL;
    }

    context->pdevice = interface_to_usbdev(pinterface);
    context->pinterface = pinterface;
    mutex_init(&context->lock);
    kref_init(&context->cref);
    init_waitqueue_head(&context->wait_queue);
    init_waitqueue_head(&context->wait_queue_2);
    context->thread = thread;

    wake_up_process(thread);
    return context;
}

void smx900_free_context(struct kref *cref)
{
    struct smx900_context *context;

    context = container_of(cref, typeof(*context), cref);

    mutex_lock(&context->lock);

    context->terminate = TRUE;
    wake_up_interruptible(&context->wait_queue);

    mutex_unlock(&context->lock);

    wait_event_interruptible(context->wait_queue_2, TRUE);

    kfree(context);
}

_Bool smx900_add_queue_item(struct smx900_context *pcontext, struct file *pfile, struct smx900_packet *ppacket)
{
    int i;
    unsigned char *buffer;

    for (i = 0; i < MAX_REQUEST_COUNT; ++i)
    {
        if (pcontext->queue[i].occupied)
        {
            if (pcontext->queue[i].pfile == pfile) return FALSE;
        }
    }

    for (i = 0; i < MAX_REQUEST_COUNT; ++i)
    {
        if (!pcontext->queue[i].occupied)
        {
            buffer = kmalloc(BUFFER_MAX_LEN, GFP_KERNEL);

            if (!buffer)
            {
                printk(KERN_ERR "smx900: failed to allocate memory");
                return FALSE;
            }

            if (ppacket->in_size)
            {
                if (copy_from_user(buffer, ppacket->in_buffer, ppacket->in_size))
                {
                    printk(KERN_ERR "smx900: copy_from_user failed");
                    kfree(buffer);
                    return FALSE;
                }
            }

            pcontext->queue[i].buffer = buffer;
            pcontext->queue[i].pfile = pfile;
            pcontext->queue[i].packet = *ppacket;
            pcontext->queue[i].occupied = TRUE;

            if (!pcontext->first)
            {
                pcontext->first = &pcontext->queue[i];
                pcontext->last = &pcontext->queue[i];
            }
            else
            {
                pcontext->last->next = &pcontext->queue[i];
                pcontext->last = &pcontext->queue[i];
            }

            atomic_inc(&pcontext->pending_count);
            wake_up_interruptible(&pcontext->wait_queue);

            return TRUE;
        }
    }

    return FALSE;
}

_Bool smx900_discard_queue_item(struct smx900_context *pcontext, struct file *pfile)
{
    int i;
    _Bool b;

    b = FALSE;

    for (i = 0; i < MAX_REQUEST_COUNT; ++i)
    {
        if (pcontext->queue[i].occupied)
        {
            if (pcontext->queue[i].pfile == pfile)
            {
                pcontext->queue[i].discard = TRUE;

                atomic_inc(&pcontext->discard_count);
                wake_up_interruptible(&pcontext->wait_queue);

                b = TRUE;
                break;
            }
        }
    }

    return b;
}

_Bool smx900_remove_queue_item(struct smx900_context *pcontext, struct file *pfile, int *presult)
{
    int i, count;
    _Bool b, error;

    b = FALSE;
    error = FALSE;

    for (i = 0; i < MAX_REQUEST_COUNT; ++i)
    {
        if (pcontext->queue[i].occupied)
        {
            if (pcontext->queue[i].pfile == pfile)
            {
                if (pcontext->queue[i].finished)
                {
                    if (pcontext->queue[i].packet.out_size)
                    {
                        if (pcontext->queue[i].result > pcontext->queue[i].packet.out_size)
                        {
                            printk(KERN_ERR "smx900: insufficient output buffer size. Read = %d, Available = %d", pcontext->queue[i].result, pcontext->queue[i].packet.out_size);
                            count = pcontext->queue[i].packet.out_size;
                        }
                        else count = pcontext->queue[i].result;

                        if (copy_to_user(pcontext->queue[i].packet.out_buffer, pcontext->queue[i].buffer, count))
                        {
                            printk(KERN_ERR "smx900: copy_to_user failed");
                            error = TRUE;
                        }
                    }

                    *presult = pcontext->queue[i].result;

                    kfree(pcontext->queue[i].buffer);
                    memset(&pcontext->queue[i], 0, sizeof(pcontext->queue[i]));

                    if (!error) b = TRUE;
                }

                break;
            }
        }
    }

    return b;
}

struct smx900_queue_item* smx900_get_pending_queue_item(struct smx900_context *pcontext)
{
    struct smx900_queue_item *item;

    item = pcontext->first;

    if (item)
    {
        pcontext->first = item->next;
        item->next = NULL;

        atomic_dec(&pcontext->pending_count);
    }

    return item;
}

void smx900_discard_queue_items(struct smx900_context *pcontext)
{
    int i;

    for (i = 0; i < MAX_REQUEST_COUNT; ++i)
    {
        if (pcontext->queue[i].occupied)
        {
            if (pcontext->queue[i].discard)
            {
                if (pcontext->queue[i].finished)
                {
                    kfree(pcontext->queue[i].buffer);
                    memset(&pcontext->queue[i], 0, sizeof(pcontext->queue[i]));
                    atomic_dec(&pcontext->discard_count);
                }
            }
        }
    }
}

int smx900_open(struct inode *pinode, struct file *pfile)
{
    int minor;
    struct usb_interface *interface;
    struct smx900_context *context;

    minor = iminor(pinode);
    interface = usb_find_interface(&g_driver, minor);
    if (!interface) return -ENODEV;
    context = usb_get_intfdata(interface);
    if (!context) return -ENODEV;
    kref_get(&context->cref);
    pfile->private_data = context;
    return 0;
}

int smx900_close(struct inode *pinode, struct file *pfile)
{
    struct smx900_context *context;

    context = pfile->private_data;
    if (!context) return -ENODEV;

    mutex_lock(&context->lock);
    smx900_discard_queue_item(context, pfile);
    mutex_unlock(&context->lock);

    kref_put(&context->cref, smx900_free_context);
    return 0;
}

int request_thread(struct smx900_context *context)
{
    _Bool terminate;
    int result, request;
    unsigned char *buffer;
    struct smx900_queue_item *item;

    terminate = FALSE;
    buffer = kmalloc(BUFFER_MAX_LEN, GFP_KERNEL);

    if (!buffer)
    {
        printk(KERN_ERR "smx900: failed to allocate memory");
        return 0;
    }

    while (1)
    {
        if (!terminate)
        {
            wait_event_interruptible(context->wait_queue, ((atomic_sub_and_test(0, &context->pending_count)) || (atomic_sub_and_test(0, &context->discard_count)) || (context->terminate)));

            if (context->terminate) terminate = TRUE;
        }

        mutex_lock(&context->lock);

        item = smx900_get_pending_queue_item(context);

        mutex_unlock(&context->lock);

        if (item)
        {
            if (!pfile)
            {
                pfile = item->pfile;
            }
            else
            {
                if (pfile == item->pfile)
                {
                    printk(KERN_ERR "smx900: SYNC ISSUE!");
                }

                pfile = item->pfile;
            }

            if (item->packet.in_size)
            {
                while (1)
                {
                    request = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE;

                    result = usb_control_msg(context->pdevice, usb_sndctrlpipe(context->pdevice, 0), 0, request, 0, 0, item->buffer, item->packet.in_size, 5000);

                    if (result < 0)
                    {
                        printk(KERN_ERR "smx900: usb_control_msg failed. Error number %d", result);
                        goto end;
                    }

                    if (result == item->packet.in_size) break;

                    do
                    {
                        request = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE;

                        result = usb_control_msg(context->pdevice, usb_rcvctrlpipe(context->pdevice, 0), 0, request, 0, 0, buffer, BUFFER_MAX_LEN, 5000);

                        if (result < 0)
                        {
                            printk(KERN_ERR "smx900: usb_control_msg failed. Error number %d", result);
                            goto end;
                        }

                        if (result < BUFFER_MIN_LEN)
                        {
                            result = -EFAULT;
                            goto end;
                        }
                    }
                    while (buffer[0] != 0xFD);      // ERR_Idle
                }

                do
                {
                    request = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE;

                    result = usb_control_msg(context->pdevice, usb_rcvctrlpipe(context->pdevice, 0), 0, request, 0, 0, item->buffer, BUFFER_MAX_LEN, 5000);

                    if (result < 0)
                    {
                        printk(KERN_ERR "smx900: usb_control_msg failed. Error number %d", result);
                        goto end;
                    }

                    if (result < BUFFER_MIN_LEN)
                    {
                        result = -EFAULT;
                        goto end;
                    }
                }
                while ((item->buffer[0] == 0xFE) || (item->buffer[0] == 0xFC));             // Err_Busy, Err_Ready
            }
            else
            {
                request = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE;

                result = usb_control_msg(context->pdevice, usb_rcvctrlpipe(context->pdevice, 0), 0, request, 0, 0, item->buffer, BUFFER_MAX_LEN, 5000);

                if (result < 0)
                {
                    printk(KERN_ERR "smx900: usb_control_msg failed. Error number %d", result);
                    goto end;
                }

                if (result < BUFFER_MIN_LEN)
                {
                    result = -EFAULT;
                    goto end;
                }
            }
    end:
            mutex_lock(&context->lock);

            item->result = result;
            item->finished = TRUE;

            smx900_discard_queue_items(context);

            mutex_unlock(&context->lock);
        }

        if (terminate)
        {
            if ((!atomic_sub_and_test(0, &context->pending_count)) && (!atomic_sub_and_test(0, &context->discard_count)))
            {
                wake_up_interruptible(&context->wait_queue_2);
                break;
            }
            else msleep(100);
        }
    }

    kfree(buffer);
    return 0;
}

long smx900_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg)
{
    _Bool b;
    int result;
    struct smx900_packet packet;
    struct smx900_context *context;

    if (cmd == IOCTL_SEND_COMMAND)
    {
        if (copy_from_user(&packet, (unsigned char*)arg, sizeof(packet)))
        {
            printk(KERN_ERR "smx900: copy_from_user failed");
            return -EFAULT;
        }

        if (!(packet.in_size) && !(packet.out_size))
        {
            printk(KERN_ERR "smx900: invalid buffers size");
            return -EFAULT;
        }

        if (packet.in_size)
        {
            if ((packet.in_size < BUFFER_MIN_LEN) || (packet.in_size > BUFFER_MAX_LEN))
            {
                printk(KERN_ERR "smx900: invalid input buffer size");
                return -EFAULT;
            }
        }

        if (packet.out_size)
        {
            if (packet.out_size < BUFFER_MIN_LEN)
            {
                printk(KERN_ERR "smx900: invalid output buffer size");
                return -EFAULT;
            }
        }

        context = pfile->private_data;
        if (!context) return -ENODEV;

        mutex_lock(&context->lock);

        if (!context->pinterface)
        {
            mutex_unlock(&context->lock);
            return -ENODEV;
        }

        b = smx900_add_queue_item(context, pfile, &packet);

        mutex_unlock(&context->lock);

        if (!b) return -EFAULT;
        else return 0;
    }
    else if (cmd == IOCTL_GET_STATUS)
    {
        //msleep(100);

        context = pfile->private_data;
        if (!context) return -ENODEV;

        mutex_lock(&context->lock);

        if (!context->pinterface)
        {
            mutex_unlock(&context->lock);
            return -ENODEV;
        }

        b = smx900_remove_queue_item(context, pfile, &result);

        mutex_unlock(&context->lock);

        if (b)
        {
            if (copy_to_user((int*)arg, &result, sizeof(result)))
            {
                printk(KERN_ERR "smx900: copy_to_user failed");
                return -EFAULT;
            }
            else return 0;
        }
        else return -EBUSY;
    }
    else
    {
        printk(KERN_ERR "smx900: unknown ioctl code");
        return -EFAULT;
    }
}

以及在用户模式应用中的外观:

uint16_t smx900_hwcommand(smx900_handle device_handle, unsigned char Cmd, const void *InBuf, unsigned char InSize, void *OutBuf, unsigned char OutSize, unsigned long* RespSize)
{
    int result, status;
    struct smx900_packet packet;
    unsigned char in_buffer[BUFFER_MAX_LEN];
    unsigned char out_buffer[BUFFER_MAX_LEN];

    if ((InSize > (BUFFER_MAX_LEN - 2)) || (OutSize > (BUFFER_MAX_LEN - 2))) return ERR_BadLength;

    packet.in_size = InSize + 2;
    packet.in_buffer = in_buffer;
    packet.in_buffer[0] = Cmd;
    packet.in_buffer[1] = InSize;
    if ((InBuf) && (InSize)) memcpy(packet.in_buffer + 2, InBuf, InSize);

    packet.out_size = BUFFER_MAX_LEN;
    packet.out_buffer = out_buffer;

    result = ioctl(device_handle, IOCTL_SEND_COMMAND, &packet);

    if (result < 0) return ERR_Critical;

    while (1)
    {
        result = ioctl(device_handle, IOCTL_GET_STATUS, &status);

        if (result < 0)
        {
            if (errno != EBUSY) return ERR_Critical;
        }
        else
        {
            result = status;

            if (result < 0) return ERR_Critical;

            break;
        }
    }

    if (packet.out_buffer[0] == ERR_OK)
    {
        OutSize = MIN(OutSize, result - 2);

        if (OutBuf) memcpy(OutBuf, packet.out_buffer + 2, OutSize);

        if (RespSize) *RespSize = OutSize;
    }

    return packet.out_buffer[0];
}

驱动程序代码是否存在问题,即请求队列的管理。是否可以实现恒定频率闪烁?谢谢。

0 个答案:

没有答案