/* * Line6 Linux USB driver - 0.8.0 * * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, version 2. * */ #include "driver.h" #include "dumprequest.h" /* Set "dump in progress" flag. */ void line6_dump_started(struct line6_dump_request *l6dr, int dest) { l6dr->in_progress = dest; } /* Invalidate current channel, i.e., set "dump in progress" flag. Reading from the "dump" special file blocks until dump is completed. */ void line6_invalidate_current(struct line6_dump_request *l6dr) { line6_dump_started(l6dr, LINE6_DUMP_CURRENT); } /* Clear "dump in progress" flag and notify waiting processes. */ void line6_dump_finished(struct line6_dump_request *l6dr) { l6dr->in_progress = LINE6_DUMP_NONE; wake_up_interruptible(&l6dr->wait); } /* Send an asynchronous channel dump request. */ int line6_dump_request_async(struct line6_dump_request *l6dr, struct usb_line6 *line6, int num) { int ret; line6_invalidate_current(l6dr); ret = line6_send_raw_message_async(line6, l6dr->reqbufs[num].buffer, l6dr->reqbufs[num].length); if (ret < 0) line6_dump_finished(l6dr); return ret; } /* Send an asynchronous dump request after a given interval. */ void line6_startup_delayed(struct line6_dump_request *l6dr, int seconds, void (*function)(unsigned long), void *data) { l6dr->timer.expires = jiffies + seconds * HZ; l6dr->timer.function = function; l6dr->timer.data = (unsigned long)data; add_timer(&l6dr->timer); } /* Wait for completion. */ int line6_wait_dump(struct line6_dump_request *l6dr, int nonblock) { int retval = 0; DECLARE_WAITQUEUE(wait, current); add_wait_queue(&l6dr->wait, &wait); current->state = TASK_INTERRUPTIBLE; while (l6dr->in_progress) { if (nonblock) { retval = -EAGAIN; break; } if (signal_pending(current)) { retval = -ERESTARTSYS; break; } else schedule(); } current->state = TASK_RUNNING; remove_wait_queue(&l6dr->wait, &wait); return retval; } /* Initialize dump request buffer. */ int line6_dumpreq_initbuf(struct line6_dump_request *l6dr, const void *buf, size_t len, int num) { l6dr->reqbufs[num].buffer = kmalloc(len, GFP_KERNEL); if (l6dr->reqbufs[num].buffer == NULL) return -ENOMEM; memcpy(l6dr->reqbufs[num].buffer, buf, len); l6dr->reqbufs[num].length = len; return 0; } /* Initialize dump request data structure (including one buffer). */ int line6_dumpreq_init(struct line6_dump_request *l6dr, const void *buf, size_t len) { int ret; ret = line6_dumpreq_initbuf(l6dr, buf, len, 0); if (ret < 0) return ret; init_waitqueue_head(&l6dr->wait); init_timer(&l6dr->timer); return 0; } /* Destruct dump request data structure. */ void line6_dumpreq_destructbuf(struct line6_dump_request *l6dr, int num) { if (l6dr == NULL) return; if (l6dr->reqbufs[num].buffer == NULL) return; kfree(l6dr->reqbufs[num].buffer); l6dr->reqbufs[num].buffer = NULL; } /* Destruct dump request data structure. */ void line6_dumpreq_destruct(struct line6_dump_request *l6dr) { if (l6dr->reqbufs[0].buffer == NULL) return; line6_dumpreq_destructbuf(l6dr, 0); l6dr->ok = 1; del_timer_sync(&l6dr->timer); }