-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathusbdpfp.c
More file actions
2481 lines (2156 loc) · 82.1 KB
/
usbdpfp.c
File metadata and controls
2481 lines (2156 loc) · 82.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* usbdpfp.c - DigitalPersona Fingerprint Sensors Device Driver
*
* Copyright 1996-2011 DigitalPersona, Inc. All rights reserved.
*
*
************
* Changelog:
************
* (March/2011)
* - Add 2.6.36 kernel support
*
* (July/2010)
* - Add 2.6.32 kernel support
*
* (March/2010)
* - Release image buffer on device file close.
* - Add mdbg macro and mdebug variable for memory tracing.
*
* (Jan/2010)
* - Port to kernel 2.6.28 (for Ubuntu distribution)
* - add abort state to device.
*
* (03/08)
* - SlackWare supprot is added.
* - Replace class_dev with usb_dev and class_id with bus_id
*
* (07/07)
* - Upgrade to kernel 2.6.18 (SUSE 10.2 and Slackware 11.0)
* - Replace class_simple_device with class_device.
*
* (06/07) PowerManagement:
* - Suspend handler implemented to support Standby and hibernate system states.
* - Changes to event IOCTL and Channel mechanism to support Power management.
*
* (05/07) Modified:
* - USBDPFP_IOCTL_ABORT_BULK_READ.
* - Aborts pending bulk URB and prevents further streaming with
* respect to channel mechanism.
* - Channel mechanism to support wide range of devices tranparently.
* Basically, the channel specifies the size of the frame to expect
* and the number of frames to be streamed while the channel is activated
* - USBDPFP_IOCTL_CONFIG_CHANNEL:
* Configures a channel with No. of frames to capture and
* the size of the frames.
* - USBDPFP_IOCTL_SET_ACTIVE_CHANNEL:
* Activates the specified channel to capture the image.
* By default First channel with a single frame is activated.
* - Read entry point
* First read operation allocates the kernel buffer for the images. Starts
* streaming if active channel is configured with frames > 1.
* - Maximum of USBDPFP_MAX_CHANNELS is supported for each device.
* - Maximum No. of devices limited by Kernel's support for USB devices.
* - USBDPFP_IOCTL_SET_STREAMED_READ replaced with channel mechanism.
*
* (04/07) Aborting of bulk pipe transfer from user mode:
* - USBDPFP_IOCTL_ABORT_BULK_READ:
* Abort the submitted bulk URB from usermode driver supported.
* - Streaming fingerprint images:
* Implemented Streaming image read and Patchprint image read.
* A device independent implementation.
*
* (03/07) Enhanced device information to user mode driver:
* USBDPFP_IOCTL_GET_INFO
* Device name and Serial No. fields added into usbdpfp_
* device_info struct.
*
* (03/07) PnP event hook to user mode driver:
* - USBDPFP_IOCTL_WAIT_PNP_EVENT
* Provides mechanism to notify usermode driver thread that device
* Plug-in and plug-out has occured.
* - USBDPFP_IOCTL_CANCEL_WAIT_PNP_EVENT
* Provides mechanism to abort usermode driver's wait for the event.
*
*****************
* Kernel support:
*****************
* The driver Supports Linux Kernel 2.6.5-2.6.18, 2.6.28-2.6.37.
*
**********************
* Future enhancements:
**********************
*
* Channel implementation could be enhanced to support
* any no. of channels dynamically.
*
***********************
* Areas of Improvement:
***********************
*
*
*************
* Known Bugs:
*************
* None
*
*/
#ifndef __KERNEL__
# error "The __KERNEL__ preprocessor symbol must be defined"
#endif
#ifndef MODULE
# error "This driver must be built as a module due to licensing issues"
#endif
#include <linux/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
#include <linux/config.h>
#endif
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/completion.h>
#include <linux/delay.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)
#include <linux/semaphore.h>
#endif
#include <asm/uaccess.h>
#include "usbdpfp.h"
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,5)
# error "This module needs kernel version 2.6.5 or greater"
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)
# ifndef init_MUTEX
# define init_MUTEX(x) sema_init(x,1)
# endif
#endif
#define MODULE_NAME "mod_usbdpfp"
#define DRIVER_AUTHOR "DigitalPersona, Inc. <www.digitalpersona.com>"
#define DRIVER_DESC "DigitalPersona Fingerprint Reader USB Driver"
// We get the DRIVER_VERSION macro from the main Makefile
#ifndef DRIVER_VERSION
#define DRIVER_VERSION "no version"
#endif
// USB char driver device minor for DigitalPersona fingerprint scanner: 176-191
// see www.LANANA.ORG
// The USB char driver major number is: 180
// For example
// To create a node for DP fingerprint char driver manually:
// mknod -m 666 /dev/usbdpfp0 c 180 176
// To create a node for the PnP driver:
// mknod -m 666 /dev/usbdpfpPnp c 254 0
#define USB_USBDPFP_MINOR_BASE 176
// USB driver entry points.
static int usbdpfp_probe(struct usb_interface *interface, const struct usb_device_id *id);
static void usbdpfp_disconnect(struct usb_interface *interface);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
static int usbdpfp_suspend(struct usb_interface *interface, u32 state);
#else
static int usbdpfp_suspend(struct usb_interface *interface, pm_message_t message);
#endif
static int usbdpfp_resume(struct usb_interface *interface);
// Char driver file operations routines for the USB device.
static int usbdpfp_open(struct inode *inode, struct file *file);
static int usbdpfp_close(struct inode *inode, struct file *file);
static ssize_t usbdpfp_read(struct file *file, char *buffer, size_t count, loff_t * ppos);
static ssize_t usbdpfp_write(struct file *file, const char *buffer, size_t count, loff_t * ppos);
static loff_t usbdpfp_llseek(struct file *filp, loff_t offset, int whence);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
static int usbdpfp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
#else
static long usbdpfp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
#endif
// Helper routines
static __u16 get_product_id(struct usbdpfp_device *dev);
static __u16 get_vendor_id(struct usbdpfp_device *dev);
static __u16 get_revision(struct usbdpfp_device *dev);
static int get_device_info(struct usbdpfp_device *dev, struct usbdpfp_device_info *dev_info);
// USB pipe operations
static int usbdpfp_control_pipe_write(struct usbdpfp_device *dev, unsigned long arg);
static int usbdpfp_control_pipe_read(struct usbdpfp_device *dev, unsigned long arg);
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)
static void usbdpfp_interrupt_callback(struct urb *urb);
#else
static void usbdpfp_interrupt_callback(struct urb *urb, struct pt_regs*);
#endif
static int usbdpfp_interrupt_pipe_read(struct usbdpfp_device *dev, unsigned long arg);
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)
static void usbdpfp_bulk_callback(struct urb *urb);
#else
static void usbdpfp_bulk_callback(struct urb *urb, struct pt_regs *dummy);
#endif
static int usbdpfp_bulk_pipe_read(struct usbdpfp_device *dev, unsigned char *buf, size_t count, int nowait,
int mem_flags);
// Forward reference for kref_init in usbdpfp_new.
// (kref require kernel 2.6.5-rc1 or later).
static inline void usbdpfp_delete(struct kref *kref);
static inline struct usbdpfp_device *usbdpfp_new(void);
// PNP event notification functions.
static int usbdpfp_add_attach_event( struct usb_interface *interface );
static int usbdpfp_handle_detach_event( struct usb_interface *interface );
static int usbdpfp_add_resume_event( struct usb_interface *interface );
// USB device file operation structure.
static struct file_operations usbdpfp_fops =
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
.owner = THIS_MODULE,
#endif
.open = usbdpfp_open,
.release = usbdpfp_close,
.read = usbdpfp_read,
.write = usbdpfp_write,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
.ioctl = usbdpfp_ioctl,
#else
.unlocked_ioctl = usbdpfp_ioctl,
#endif
.llseek = usbdpfp_llseek,
};
// Our device Id table
static struct usb_device_id usbdpfp_id_table[] =
{
{USB_DEVICE(DP_VID, URU4000B)},
{USB_DEVICE(DP_VID, URU4000)},
{.match_flags=USB_DEVICE_ID_MATCH_DEVICE|USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = DP_VID,
.idProduct = URU4000KB,
.bInterfaceClass = 0xff,
.bInterfaceSubClass = 0xff,
.bInterfaceProtocol = 0xff
},
{0}
};
MODULE_DEVICE_TABLE(usb, usbdpfp_id_table);
// USB driver entry points.
static struct usb_driver usbdpfp_usb_driver =
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
.owner = THIS_MODULE,
#endif
.name = MODULE_NAME,
.probe = usbdpfp_probe,
.disconnect = usbdpfp_disconnect,
.id_table = usbdpfp_id_table,
.suspend = usbdpfp_suspend,
.resume = usbdpfp_resume,
};
// USB class driver structure.
static struct usb_class_driver usbdpfp_class = {
.name = "usb/usbdpfp%d",
.fops = &usbdpfp_fops,
// .mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH,
.minor_base = USB_USBDPFP_MINOR_BASE,
};
static struct mutex drv_mutex;
// Some helper functions.
#define usbdpfp_kmalloc(size, flags) \
({ \
void* p = kmalloc((size), (flags)); \
mdbg("usbdpfp_kmalloc(%d, %d)=%p", (int)(size), (flags), p); \
(void*)p; \
})
#define usbdpfp_kfree(p) \
{ \
mdbg("usbdpfp_kfree(%p)", (p)); \
kfree(p); \
}
inline void usbdpfp_kref_init(struct kref *kref, void (*release)(struct kref *kref))
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,9)
kref_init(kref, release);
#else
kref_init(kref);
#endif
}
inline void usbdpfp_kref_put(struct kref *kref, void (*release)(struct kref *kref))
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,9)
kref_put(kref);
#else
kref_put(kref, release);
#endif
}
static __u16 get_product_id(struct usbdpfp_device *dev)
{
return dev->udev->descriptor.idProduct;
}
static __u16 get_vendor_id(struct usbdpfp_device * dev)
{
return dev->udev->descriptor.idVendor;
}
static __u16 get_revision(struct usbdpfp_device * dev)
{
return dev->udev->descriptor.bcdDevice;
}
// Copies the device name into the buffer from the dev struct, stored during probe
static void get_device_name(struct usbdpfp_device *dev, char *buffer)
{
if(!buffer)
return;
strncpy(buffer, dev->dev_name, 80);
dev->dev_name[79]='\0';
}
// Copies the device serial number into the buffer from the dev struct, stored during probe
static void get_device_serial_no(struct usbdpfp_device *dev, char *buffer)
{
if(!buffer)
return;
strncpy(buffer, dev->serial_no, 80);
dev->dev_name[79]='\0';
}
// Get all the device info and fill up the dev_info structure
static int get_device_info(struct usbdpfp_device *dev, struct usbdpfp_device_info *dev_info)
{
if(!dev || !dev_info)
return -EINVAL;
dev_info->idProduct = (unsigned int) get_product_id(dev);
dev_info->idVendor = (unsigned int) get_vendor_id(dev);
dev_info->bcdDevice = (unsigned int) get_revision(dev);
get_device_name(dev, dev_info->nameDevice);
get_device_serial_no(dev, dev_info->snDevice);
return 0;
}
/*!
frame utilities:-
These functions should be called with the bulk_sem held.
*/
/*!
Initialize a frame. Does not allocate the data buffer.
*/
static void inline init_frame(struct usbdpfp_frame *frame)
{
frame->buffer = NULL;
frame->bulk_read_status = 0;
frame->bulk_read_count = 0;
frame->short_packet_detected = 0;
frame->valid = USBDPFP_FRAME_INVALID;
}
/*!
Frees the previously allocated data buffers and initializes the frame
*/
static inline void cleanup_and_init_frame(struct usbdpfp_frame *frame)
{
if(frame->buffer){
usbdpfp_kfree(frame->buffer); //Free any allocated buffer
}
frame->buffer = NULL;
frame->bulk_read_status = 0;
frame->bulk_read_count = 0;
frame->short_packet_detected = 0;
frame->valid = USBDPFP_FRAME_INVALID;
}
/*!
Initializes all the frames of a channel.
*/
static inline void init_all_frames(struct usbdpfp_channel_config *channel)
{
int index;
for(index=0; index<USBDPFP_MAX_FRAMES; index++){
init_frame(&(channel->frame[index]));
}
}
/*!
Cleans-up an initializes the frames of a channel.
*/
static inline void cleanup_and_init_all_frames(struct usbdpfp_channel_config *channel)
{
int index;
for(index=0; index<USBDPFP_MAX_FRAMES; index++){
cleanup_and_init_frame(&(channel->frame[index]));
}
}
/*!
Invalidates all the frames of the channel.
Does not allocate or deallocate the data buffer.
*/
static inline void invalidate_frames(struct usbdpfp_channel_config *channel)
{
int index;
for (index=0; index<channel->max_frames; index++) {
channel->frame[index].valid=USBDPFP_FRAME_INVALID;
channel->frame[index].bulk_read_status=0;
channel->frame[index].bulk_read_count=0;
channel->frame[index].short_packet_detected=0;
}
}
/*!
Channel Utilities.
These function should be called with
the bulk_sem held;
*/
/*!
Initializes the channel for fresh use.
*/
static void init_channel(struct usbdpfp_channel_config *channel, unsigned int max_frames,
unsigned max_bytes_per_frame)
{
channel->valid = USBDPFP_CHANNEL_NOT_CONFIGURED;
channel->max_frames = max_frames;
channel->max_bytes_per_frame = max_bytes_per_frame;
channel->frame_write_index = 0;
channel->frame_read_index = 0;
init_all_frames(channel);
}
/*!
Cleanup the channel.
If frame data buffers are allocated for the channel releases them.
*/
static inline void cleanup_channel(struct usbdpfp_channel_config *channel)
{
channel->valid = USBDPFP_CHANNEL_NOT_CONFIGURED;
channel->max_frames = 0; //0 is an invalid value
channel->max_bytes_per_frame = 0;//0 is an invalid value
channel->frame_write_index=0;
channel->frame_read_index = 0;
cleanup_and_init_all_frames(channel);
}
/*!
Cleanup all the channels for the device.
*/
static inline void init_all_channels(struct usbdpfp_device *dev)
{
int index;
for(index=0; index < USBDPFP_MAX_CHANNELS; index++) {
init_channel(&dev->channel[index], 0, 0);
}
}
/*!
allocate the frame buffers for the channel
succeeds in allocating all the frame buffers
or fails and allocates none.
*/
static int allocate_frame_buffers(struct usbdpfp_channel_config *active_channel)
{
int alloc_index;
unsigned long alloc_count;
alloc_count=active_channel->max_bytes_per_frame;
for(alloc_index=0; alloc_index < active_channel->max_frames; alloc_index++)
{
//if there was a frame left out, free it
if(active_channel->frame[alloc_index].buffer) {
usbdpfp_kfree(active_channel->frame[alloc_index].buffer);
active_channel->frame[alloc_index].buffer = NULL;
}
active_channel->frame[alloc_index].buffer=usbdpfp_kmalloc(alloc_count, GFP_KERNEL);
dbg("frame # %d: address=%p", alloc_index, active_channel->frame[alloc_index].buffer);
if(!active_channel->frame[alloc_index].buffer)
break;
}
if(alloc_index!=active_channel->max_frames) {//all frames not be allocated
//free the allocated frame buffers here
for(--alloc_index; alloc_index>=0; alloc_index--) {
usbdpfp_kfree(active_channel->frame[alloc_index].buffer);
active_channel->frame[alloc_index].buffer = NULL;
}
}
return alloc_index;
}
/*!
Deallocate all the buffers of the channel.
Checks all the frames independent of how channel is configured.
May be redundant after 'max frames' but helps keep cleanups simple.
*/
static inline void deallocate_frame_buffers(struct usbdpfp_channel_config *
active_channel)
{
int index;
for(index=0; index<USBDPFP_MAX_FRAMES; index++)
if(active_channel->frame[index].buffer) {
usbdpfp_kfree(active_channel->frame[index].buffer);
active_channel->frame[index].buffer=NULL;
}
}
static inline int is_empty_frames(struct usbdpfp_channel_config *channel)
{
int is_empty = 1;
if (channel && (channel->frame_write_index != channel->frame_read_index)) {
is_empty = 0;
}
return is_empty;
}
static inline int is_full_frames(struct usbdpfp_channel_config *channel)
{
int full_empty = 0;
if (channel &&
(channel->frame_write_index - channel->frame_read_index) == USBDPFP_MAX_FRAMES -1)
{
full_empty = 1;
}
return full_empty;
}
static inline void adv_read_frame(struct usbdpfp_channel_config *channel)
{
if (channel) {
channel->frame_read_index += 1;
if (channel->frame_read_index >= channel->max_frames)
channel->frame_read_index = 0;
}
}
static inline void adv_write_frame(struct usbdpfp_channel_config *channel)
{
if (channel) {
channel->frame_write_index += 1;
if (channel->frame_write_index >= channel->max_frames)
channel->frame_write_index = 0;
}
}
static inline void reset_frames(struct usbdpfp_channel_config *channel)
{
if (channel) {
//int i;
/* These will cause the queue to be empty */
channel->frame_write_index = 0;
channel->frame_read_index = 0;
/* These code will be done by invalidate_frames()
for (i = 0; i < channel->max_frames; i++) {
channel->frame[i].bulk_read_count = 0;
channel->frame[i].bulk_read_status = 0;
channel->frame[i].short_packet_detected = 0;
channel->frame[i].valid = USBDPFP_FRAME_INVALID;
}
*/
}
}
/* USB pipes code */
// Synchronous io write to control pipe
static int usbdpfp_control_pipe_write(struct usbdpfp_device *dev, unsigned long arg)
{
int result = 0;
struct usbdpfp_device_data dev_data;
struct usbdpfp_device_data* p = (struct usbdpfp_device_data*) arg;
if (!dev || !dev->udev || !p) {
err("bad parameter (dev=%p, arg=%p)", dev, p);
return -EFAULT;
}
if (!p->data || p->length <= 0 ||
!access_ok(VERIFY_READ, (void __user*) p->data, p->length)) {
err("device minor %d: bad parameter (.length=%d, .data=%p)",
dev->minor, p->length, p->data);
return -EFAULT;
}
// all the user data seems to be ok, copy them to kernel space
if (copy_from_user(&dev_data, (const void __user*)arg,
sizeof(struct usbdpfp_device_data))) {
err("device minor %d: copy_from_user() 1 failed", dev->minor);
return -EFAULT;
}
// Optimize frequent allocation for small memory
if (dev_data.length > dev->size_ctldata) {
dev_data.data = usbdpfp_kmalloc(dev_data.length * sizeof(unsigned char), GFP_KERNEL);
} else {
// use the preallocated the buffer (must be used one thread at the time)
dev_data.data = dev->ctldata;
}
if(dev_data.data == NULL) {
err("device minor %d: Out of memory", dev->minor);
return -EFAULT;
}
if (copy_from_user(dev_data.data, (const void __user*)p->data, dev_data.length)) {
err("device minor %d: copy_from_user() 2 failed", dev->minor);
result = -EFAULT;
goto io_write_exit; // free memory and exit
}
dbg("device minor %d: .type=%d, .address=0x%x, .length=%d, .data[0]=0x%02x",
dev->minor, dev_data.type, dev_data.address, dev_data.length,
((unsigned char*)dev_data.data)[0]);
// issue urb to device - synchronous operation!!
result = usb_control_msg(
dev->udev, /* USB device to send */
usb_sndctrlpipe(dev->udev, 0),/* pipe, end point of USB device */
0x04, /* USB request */
USB_TYPE_VENDOR|USB_RECIP_DEVICE|USB_DIR_OUT, /* USB request type 0x40 */
dev_data.address, /* USB message value */
0, /* USB message index value */
dev_data.data, /* data buffer */
dev_data.length, /* size of buffer */
HZ * 5 /* amount of timeout */
);
if (result < dev_data.length) {
err("device minor %d: %d out of %d bytes was written",
dev->minor, result, dev_data.length);
}
io_write_exit:
if(dev_data.data && (dev_data.data != dev->ctldata)) {
usbdpfp_kfree(dev_data.data);
dev_data.data = NULL;
}
return result;
}
// Synchronous io read from control pipe
static int usbdpfp_control_pipe_read(struct usbdpfp_device *dev, unsigned long arg)
{
int result = 0;
struct usbdpfp_device_data dev_data;
struct usbdpfp_device_data* p = (struct usbdpfp_device_data*) arg;
if (!dev || !dev->udev || !p) {
err("bad parameter (dev=%p, arg=%p)", dev, p);
return -EFAULT;
}
if (!p->data || p->length <= 0 ||
!access_ok(VERIFY_WRITE, (void __user*) p->data, p->length)) {
err("device minor %d: bad parameter (.length=%d, .data=%p)",
dev->minor, p->length, p->data);
return -EFAULT;
}
// the user data seems to be ok, copy them to kernel space
if (copy_from_user(&dev_data, (const void __user*)arg,
sizeof(struct usbdpfp_device_data))) {
err("device minor %d: copy_from_user() failed", dev->minor);
return -EFAULT;
}
// Optimize frequent allocation for small memory
if (dev_data.length > dev->size_ctldata) {
dev_data.data = usbdpfp_kmalloc(dev_data.length * sizeof(unsigned char), GFP_KERNEL);
} else {
// use the preallocated the buffer (must be used one thread at the time)
dev_data.data = dev->ctldata;
}
if(NULL == dev_data.data) {
err("device minor %d: Out of memory", dev->minor);
return -EFAULT;
}
// issue urb to device - synchronous operation!!
result = usb_control_msg(
dev->udev, /* USB device to send */
usb_rcvctrlpipe(dev->udev, 0),/* end point of USB device*/
0x04, /* USB request value */
USB_TYPE_VENDOR | USB_RECIP_DEVICE
| USB_DIR_IN, /* USB request type 0xc0 */
dev_data.address, /* USB message value */
0, /* USB message index value*/
dev_data.data, /* data buffer */
dev_data.length, /* size of buffer */
HZ * 5 /* amount of timeout */
);
if (result < dev_data.length) {
err("device minor %d: only %d was read, but %d bytes was expected.",
dev->minor, result, dev_data.length);
goto io_read_exit;
}
else { // copy the kernel data to user space
dbg("device minor %d: copy data to user space, .address=%x, .length=%d, .data[0]=0x%02x",
dev->minor, dev_data.address, result, ((unsigned char*)dev_data.data)[0]);
if (copy_to_user((void __user*)p->data, dev_data.data, dev_data.length*sizeof(unsigned char))) {
err("device minor %d: copy_to_user() failed", dev->minor);
result = -EFAULT;
goto io_read_exit; // free memory and exit on error
}
}
io_read_exit:
if(dev_data.data && (dev_data.data != dev->ctldata)) {
usbdpfp_kfree(dev_data.data);
dev_data.data = NULL;
}
return result;
}
/*!
Callback routine for interrupt pipe urb.
May be called after the following:
When a URB is unlinked by the Suspend ]----> No process is waiting at this point.
When a URB is successful. -------------|
When a URB fails due to other reasons |--->wake up the Sleeping process.
When a URB is unlinked by abort IOCTL---|
*/
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)
static void usbdpfp_interrupt_callback(struct urb *urb)
#else
static void usbdpfp_interrupt_callback(struct urb *urb, struct pt_regs*dummy)
#endif
{
if (urb) {
struct usbdpfp_device* dev = urb->context;
if (dev) {
atomic_set(&dev->cancelable_int_urb, 0); // No urbs to cancel
// The dev->event_data->data now carries the raw data from device
// but we need to update other fields to reflect in the caller.
dev->event_data->status = urb->status;
dev->event_data->size_returned = urb->actual_length;
//URB was unlinked through suspend.
//Already come out of waiting and restarted the IOCTL system cal.
//No need to Up the cpmpletion sem.
if(atomic_read(&dev->suspended)) {
atomic_set(&dev->suspended, 0);
dbg("driver was suspended");
}
//End waiting for IOCTL completion for any thing other than suspend.
else {
dbg("up completion sem in int callback");
up(&dev->event_compl_sem);
}
}
else err("device %d doesn't exist", dev->minor);
}
}
static int usbdpfp_interrupt_pipe_read(struct usbdpfp_device *dev, unsigned long arg)
{
int result = 0, i;
struct usbdpfp_device_event* p = NULL;
p = (struct usbdpfp_device_event*) arg;
if (!dev || !dev->udev || !p) {
err("bad parameter (dev=%p, arg=%p)", dev, p);
result = -EFAULT;
goto event_read_error;
}
/* allow one thread at a time */
if (down_interruptible(&dev->event_sem)) {
dbg("device minor %d: acquiring event->sem failed", dev->minor);
result = -ERESTARTSYS;
goto event_read_error;
}
if (p->size_requested <= 0 || p->size_requested > USBDPFP_MAX_EVENT_SIZE ||
!access_ok(VERIFY_WRITE, (void __user*) p,
sizeof(struct usbdpfp_device_event))) {
err("device minor %d: bad parameter or inaccessible memory", dev->minor);
result = -EFAULT;
goto event_read_exit;
}
if(0==atomic_read(&dev->cancelable_int_urb)) { //interrupt URB is not pending
dev->event_data->size_requested = p->size_requested = USBDPFP_MAX_EVENT_SIZE;
dev->event_data->size_returned = p->size_returned = 0;
dev->event_data->status = -EINVAL; /* set to non-zero please*/
usb_fill_int_urb(
dev->int_in_urb, /* urb for this USB transaction*/
dev->udev, /* USB device object */
usb_rcvintpipe(dev->udev, dev->int_in_ep),/* endpoint IN */
dev->event_data->data, /* transfer buffer */
dev->event_data->size_requested,/* transfer buffer size */
(usb_complete_t)usbdpfp_interrupt_callback, /*completion routine */
dev, /* context */
dev->int_in_int /* polling interval */
);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
dev->int_in_urb->transfer_flags |= URB_ASYNC_UNLINK;/*allow immediate abort*/
#endif
result = usb_submit_urb(dev->int_in_urb, GFP_KERNEL);
if (result) {
err("device minor %d: usb_submit_urb failed (%d)", dev->minor, result);
goto event_read_exit;
}
/* set the cancelable flag to true to signal a pending urb.
this flag is cleared in the urb's completion routine. */
atomic_set(&dev->cancelable_int_urb, 1);
}
/* Wait for event data to arrive in an interruptible manner.
The dev->event_compl_sem should be, ideally, signaled by the completion
routine. If a signal has interrupted the wait, cleanup whatever is done
and try to restart the system call.
*/
if ((i=down_interruptible(&dev->event_compl_sem))) {
dbg("wait event_compl_sem interrupted by %x", i);
result = -ERESTARTSYS;
goto event_read_exit;
}
/* copy the data back to user regardless status condition,
but check for user space again before copying data to it
*/
if (!access_ok(VERIFY_WRITE, (void __user*) p, sizeof(struct usbdpfp_device_event))) {
err("device minor %d: inaccessible memory, p=%p)", dev->minor, p);
result = -EFAULT;
goto event_read_exit;
}
if (copy_to_user((void __user*)arg, dev->event_data, sizeof(struct usbdpfp_device_event))) {
err("device minor %d: copy_to_user failed", dev->minor);
result = -EFAULT;
goto event_read_exit;
}
/* The dev->event_data->status should be updated by the completion routine.
If not,something has gone wrong and it should contain the initial value,
which should be non-zero.
*/
if (dev->event_data->status != 0) { // something went wrong
if (dev->event_data->status == -ENOENT) { // was killed
dbg("device minor %d: urb status error code -ENOENT)",
dev->minor);
} else if (dev->event_data->status == -ECONNRESET) { // was unlinked
dbg("device minor %d: urb status error code -ECONNRESET)",
dev->minor);
}
else {
dbg("device minor %d: urb status error 0x%x or 0x%x)", dev->minor,
dev->event_data->status, -dev->event_data->status);
}
result = dev->event_data->status; // send the status back to IOCTL.
}
event_read_exit:
up(&dev->event_sem);
event_read_error:
dbg("device minor %d: result=0x%x", dev->minor, result);
return result;
}
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)
static void usbdpfp_bulk_callback(struct urb *urb)
#else
static void usbdpfp_bulk_callback(struct urb *urb, struct pt_regs *dummy)
#endif
{
// Data arrives or error occurs from bulk pipe in. Store the data into the
// active channel's current frame buffer, continue stream then copy data
// to user buffer and wake up the caller of usbdpfp_read.
struct usbdpfp_device *dev = NULL;
struct usbdpfp_channel_config *active_channel=NULL;
struct usbdpfp_frame *cur_frame=NULL;
if (!urb) {
err("invalid URB");
goto callback_exit;
}
dev = urb->context;
if (!dev) {
err("invalid Device");
goto callback_exit;
}
atomic_set(&dev->cancelable_bulk_urb, 0); // URB is done, can NOT be cancelled
active_channel=dev->active_channel;
dbg("device minor %d: callback status=0x%0x, actual_length=%d",
dev->minor, urb->status, urb->actual_length);
// Locate the current frame
cur_frame = &active_channel->frame[active_channel->frame_write_index];
cur_frame->bulk_read_status = urb->status;
cur_frame->bulk_read_count = urb->actual_length;
if (cur_frame->bulk_read_status == 0) { // Successful
cur_frame->valid = USBDPFP_FRAME_VALID; //mark valid data
if (urb->actual_length == 0) {
//we treat short packet or end of packet as non-error and valid frame
cur_frame->short_packet_detected = 1;
}
adv_write_frame(active_channel);
}
else { //error occured or urb aborted.
cur_frame->valid = USBDPFP_FRAME_ERROR;
}
// wake_up_interruptible(&dev->inq); // Trigger the waiting read to wakeup.
// Continue streaming if ...
if (cur_frame->bulk_read_status == 0 && // no urb error
dev->do_streaming_read && // streaming flag
!is_full_frames(active_channel)) // has free frame
{
dbg("device minor %d: resubmit bulk read from callback", dev->minor);
if (usbdpfp_bulk_pipe_read(dev,
active_channel->frame[active_channel->frame_write_index].buffer,
active_channel->max_bytes_per_frame,
O_NONBLOCK, GFP_ATOMIC))
{
// Failed to stream, report this through frame's read status.
// we cannot do much here, especially not to touch the unread data.
err("device minor %d: failed to stream at frame # %d",
dev->minor, active_channel->frame_write_index);
}
}
wake_up_interruptible(&dev->inq); // Trigger the waiting read to wakeup.
callback_exit:
return;
}
static int usbdpfp_bulk_pipe_read(struct usbdpfp_device *dev, unsigned char *kbuf, size_t count,
int nowait, int mem_flags)
{
int result = 0;
if (!dev || !dev->udev || !kbuf || count <= 0) {
err("bad parameter (dev=%p, buf=%p, count=%d)", dev, kbuf, (int)count);
result = -EFAULT;
goto bulk_read_error;
}
dbg("device minor %d: kbuf=%p, count=%d", dev->minor, kbuf, (int)count);
usb_fill_bulk_urb(
dev->bulk_in_urb, /* urb for bulk pipe read */
dev->udev, /* USB device object */
usb_rcvbulkpipe(dev->udev, dev->bulk_in_ep),/* endpoint bulk in*/
kbuf, /* xfer buffer */
count, /* xfer buffer size */
(usb_complete_t)usbdpfp_bulk_callback, /* completion routine */
dev /* context */
);
result = usb_submit_urb(dev->bulk_in_urb, mem_flags);
if (result) {
err("device minor %d:usb_submit_urb failed (%d)", dev->minor, result);
} else {
atomic_set(&dev->cancelable_bulk_urb, 1);
}
bulk_read_error:
dbg("device minor %d: result=0x%x or %d", dev->minor, result, result);
return result;
}
static void abort_bulk_read(struct usbdpfp_device *dev)
{
if (dev) {
if (atomic_read(&dev->cancelable_bulk_urb) == 1) {
//usb_kill_urb(dev->bulk_in_urb);
usb_unlink_urb(dev->bulk_in_urb); //TODO: old way
while (atomic_read(&dev->cancelable_bulk_urb) == 1)
;
dev->do_streaming_read = 0;
//atomic_set(&dev->cancelable_bulk_urb, 0);
}
reset_frames(dev->active_channel);
}