我有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];
}
驱动程序代码是否存在问题,即请求队列的管理。是否可以实现恒定频率闪烁?谢谢。