File: lirc_mceusb.c

package info (click to toggle)
lirc 0.7.1pre2-2
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 3,852 kB
  • ctags: 2,924
  • sloc: ansic: 31,205; sh: 12,021; makefile: 631
file content (1030 lines) | stat: -rw-r--r-- 28,227 bytes parent folder | download
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
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
/*
 * USB Microsoft IR Transceiver driver - 0.2
 *
 * Copyright (c) 2003-2004 Dan Conti (dconti@acm.wwu.edu)
 *
 * This driver is based on the USB skeleton driver packaged with the
 * kernel, and the notice from that package has been retained below.
 *
 * The Microsoft IR Transceiver is a neat little IR receiver with two
 * emitters on it designed for Windows Media Center. This driver might
 * work for all media center remotes, but I have only tested it with
 * the philips model. The first revision of this driver only supports
 * the receive function - the transmit function will be much more
 * tricky due to the nature of the hardware. Microsoft chose to build
 * this device inexpensively, therefore making it extra dumb.
 * There is no interrupt endpoint on this device; all usb traffic
 * happens over two bulk endpoints. As a result of this, poll() for
 * this device is an actual hardware poll (instead of a receive queue
 * check) and is rather expensive.
 *
 * All trademarks property of their respective owners. This driver was
 * originally based on the USB skeleton driver, although significant
 * portions of that code have been removed as the driver has evolved.
 *
 * 2003_11_11 - Restructured to minimalize code interpretation in the
 *              driver. The normal use case will be with lirc.
 *
 * 2004_01_01 - Removed all code interpretation. Generate mode2 data
 *              for passing off to lirc. Cleanup
 *
 * 2004_01_04 - Removed devfs handle. Put in a temporary workaround
 *              for a known issue where repeats generate two
 *              sequential spaces (last_was_repeat_gap)
 *
 * 2004_02_17 - Changed top level api to no longer use fops, and
 *              instead use new interface for polling via
 *              lirc_thread. Restructure data read/mode2 generation to
 *              a single pass, reducing number of buffers. Rev to .2
 *
 * 2004_02_27 - Last of fixups to plugin->add_to_buf API. Properly
 *              handle broken fragments from the receiver. Up the
 *              sample rate and remove any pacing from
 *              fetch_more_data. Fixes all known issues.
 * 
 * TODO
 *   - Fix up minor number, registration of major/minor with usb subsystem
 *
 */
/*
 * USB Skeleton driver - 1.1
 *
 * Copyright (C) 2001-2003 Greg Kroah-Hartman (greg@kroah.com)
 *
 *	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.
 *
 *
 * This driver is to be used as a skeleton driver to be able to create a
 * USB driver quickly.  The design of it is based on the usb-serial and
 * dc2xx drivers.
 *
 * Thanks to Oliver Neukum, David Brownell, and Alan Stern for their help
 * in debugging this driver.
 *
 *
 * History:
 *
 * 2003-05-06 - 1.1 - changes due to usb core changes with usb_register_dev()
 * 2003-02-25 - 1.0 - fix races involving urb->status, unlink_urb(), and
 *			disconnect.  Fix transfer amount in read().  Use
 *			macros instead of magic numbers in probe().  Change
 *			size variables to size_t.  Show how to eliminate
 *			DMA bounce buffer.
 * 2002_12_12 - 0.9 - compile fixes and got rid of fixed minor array.
 * 2002_09_26 - 0.8 - changes due to USB core conversion to struct device
 *			driver.
 * 2002_02_12 - 0.7 - zero out dev in probe function for devices that do
 *			not have both a bulk in and bulk out endpoint.
 *			Thanks to Holger Waechtler for the fix.
 * 2001_11_05 - 0.6 - fix minor locking problem in skel_disconnect.
 *			Thanks to Pete Zaitcev for the fix.
 * 2001_09_04 - 0.5 - fix devfs bug in skel_disconnect. Thanks to wim delvaux
 * 2001_08_21 - 0.4 - more small bug fixes.
 * 2001_05_29 - 0.3 - more bug fixes based on review from linux-usb-devel
 * 2001_05_24 - 0.2 - bug fixes based on review from linux-usb-devel people
 * 2001_05_01 - 0.1 - first version
 *
 */

#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/smp_lock.h>
#include <linux/usb.h>
#ifdef KERNEL_2_5
#include <linux/completion.h>
#include <asm/uaccess.h>
#else
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/fcntl.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/signal.h>
#endif

#ifdef CONFIG_USB_DEBUG
	static int debug = 1;
#else
	static int debug = 0;
#endif

#include "drivers/kcompat.h"
#include "drivers/lirc.h"
#include "drivers/lirc_dev/lirc_dev.h"

/* Use our own dbg macro */
#define dprintk(fmt, args...)                             \
	do{                                               \
		if(debug) printk(KERN_DEBUG __FILE__ ": " \
				 fmt "\n", ## args);      \
	}while(0)

/* Version Information */
#define DRIVER_VERSION "v0.2"
#define DRIVER_AUTHOR "Dan Conti, dconti@acm.wwu.edu"
#define DRIVER_DESC "USB Microsoft IR Transceiver Driver"
#define DRIVER_NAME "lirc_mceusb"

/* Define these values to match your device */
#define USB_MCEUSB_VENDOR_ID	0x045e
#define USB_MCEUSB_PRODUCT_ID	0x006d

/* table of devices that work with this driver */
static struct usb_device_id mceusb_table [] = {
	{ USB_DEVICE(USB_MCEUSB_VENDOR_ID, USB_MCEUSB_PRODUCT_ID) },
	{ }					/* Terminating entry */
};

/* we can have up to this number of device plugged in at once */
#define MAX_DEVICES		16

/* Structure to hold all of our device specific stuff */
struct usb_skel {
	struct usb_device *	    udev;		/* save off the usb device pointer */
	struct usb_interface *	interface;		/* the interface for this device */
	unsigned char   minor;				/* the starting minor number for this device */
	unsigned char   num_ports;			/* the number of ports this device has */
	char            num_interrupt_in;		/* number of interrupt in endpoints we have */
	char            num_bulk_in;			/* number of bulk in endpoints we have */
	char            num_bulk_out;			/* number of bulk out endpoints we have */

	unsigned char *    bulk_in_buffer;		/* the buffer to receive data */
	int                bulk_in_size;		/* the size of the receive buffer */
	__u8               bulk_in_endpointAddr;	/* the address of the bulk in endpoint */

	unsigned char *    bulk_out_buffer;		/* the buffer to send data */
	int	               bulk_out_size;		/* the size of the send buffer */
	struct urb *       write_urb;			/* the urb used to send data */
	__u8               bulk_out_endpointAddr;	/* the address of the bulk out endpoint */

	atomic_t		write_busy;		/* true iff write urb is busy */
	struct completion	write_finished;		/* wait for the write to finish */
	
	wait_queue_head_t  wait_q;			/* for timeouts */
	int                open_count;			/* number of times this port has been opened */
	struct semaphore   sem;				/* locks this structure */
	
	int			present;		/* if the device is not disconnected */		

	struct lirc_plugin* plugin;
	
	lirc_t lircdata[256];          			/* place to store values until lirc processes them */
	int    lircidx;                			/* current index */
	int    lirccnt;                			/* remaining values */
	
	int    usb_valid_bytes_in_bulk_buffer;		/* leftover data from a previous read */
	int    mce_bytes_left_in_packet;		/* for packets split across multiple reads */
	
	/* Value to hold the last received space; 0 if last value
	 * received was a pulse
	 */
	int    last_space;
	
#ifdef KERNEL_2_5
	dma_addr_t dma_in;
	dma_addr_t dma_out;
#endif
};

#define MCE_TIME_UNIT 50

/* driver api */
#ifdef KERNEL_2_5
static int mceusb_probe		(struct usb_interface *interface, const struct usb_device_id *id);
static void mceusb_disconnect	(struct usb_interface *interface);
static void mceusb_write_bulk_callback	(struct urb *urb, struct pt_regs *regs);
#else
static void * mceusb_probe	(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id);
static void mceusb_disconnect	(struct usb_device *dev, void *ptr);
static void mceusb_write_bulk_callback	(struct urb *urb);
#endif

/* read data from the usb bus; convert to mode2 */
static int msir_fetch_more_data( struct usb_skel* dev, int dont_block );

/* helper functions */
static void msir_cleanup( struct usb_skel* dev );
static void set_use_dec( void* data );
static int set_use_inc( void* data );
    
/* array of pointers to our devices that are currently connected */
static struct usb_skel		*minor_table[MAX_DEVICES];

/* lock to protect the minor_table structure */
static DECLARE_MUTEX (minor_table_mutex);
static void mceusb_setup( struct usb_device *udev );

/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver mceusb_driver = {
	.owner =	THIS_MODULE,
	.name =		DRIVER_NAME,
	.probe =	mceusb_probe,
	.disconnect =	mceusb_disconnect,
	.id_table =	mceusb_table,
};


/**
 *	usb_mceusb_debug_data
 */
static inline void usb_mceusb_debug_data (const char *function, int size,
					  const unsigned char *data)
{
	int i;
	if (!debug)
		return;
	
	printk(KERN_DEBUG __FILE__": %s - length = %d, data = ", 
	       function, size);
	for (i = 0; i < size; ++i) {
		printk(KERN_DEBUG "%.2x ", data[i]);
	}
	printk(KERN_DEBUG "\n");
}

/**
 *mceusb_delete
 */
static inline void mceusb_delete (struct usb_skel *dev)
{
	dprintk("%s", __func__);
	minor_table[dev->minor] = NULL;
#ifdef KERNEL_2_5
	usb_buffer_free(dev->udev, dev->bulk_in_size, dev->bulk_in_buffer, dev->dma_in);
	usb_buffer_free(dev->udev, dev->bulk_out_size, dev->bulk_out_buffer, dev->dma_out);
#else
	if (dev->bulk_in_buffer != NULL)
		kfree (dev->bulk_in_buffer);
	if (dev->bulk_out_buffer != NULL)
		kfree (dev->bulk_out_buffer);
#endif
	if (dev->write_urb != NULL)
		usb_free_urb (dev->write_urb);
	kfree (dev);
}

static void mceusb_setup( struct usb_device *udev )
{
	char data[8];
	int res;
	
	memset( data, 0, 8 );

	/* Get Status */
	res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
			      USB_REQ_GET_STATUS, USB_DIR_IN,
			      0, 0, data, 2, HZ * 3);
    
	/*    res = usb_get_status( udev, 0, 0, data ); */
	dprintk("%s - res = %d status = 0x%x 0x%x", __func__,
		res, data[0], data[1]);
    
	/* This is a strange one. They issue a set address to the device
	 * on the receive control pipe and expect a certain value pair back
	 */
	memset( data, 0, 8 );

	res = usb_control_msg( udev, usb_rcvctrlpipe(udev, 0),
			       5, USB_TYPE_VENDOR, 0, 0,
			       data, 2, HZ * 3 );
	dprintk("%s - res = %d, devnum = %d", __func__, res, udev->devnum);
	dprintk("%s - data[0] = %d, data[1] = %d", __func__,
		data[0], data[1] );

    
	/* set feature */
	res = usb_control_msg( udev, usb_sndctrlpipe(udev, 0),
			       USB_REQ_SET_FEATURE, USB_TYPE_VENDOR,
			       0xc04e, 0x0000, NULL, 0, HZ * 3 );
    
	dprintk("%s - res = %d", __func__, res);

	/* These two are sent by the windows driver, but stall for
	 * me. I dont have an analyzer on the linux side so i can't
	 * see what is actually different and why the device takes
	 * issue with them
	 */
#if 0
	/* this is some custom control message they send */
	res = usb_control_msg( udev, usb_sndctrlpipe(udev, 0),
			       0x04, USB_TYPE_VENDOR,
			       0x0808, 0x0000, NULL, 0, HZ * 3 );
    
	dprintk("%s - res = %d", __func__, res);
    
	/* this is another custom control message they send */
	res = usb_control_msg( udev, usb_sndctrlpipe(udev, 0),
			       0x02, USB_TYPE_VENDOR,
			       0x0000, 0x0100, NULL, 0, HZ * 3 );
    
	dprintk("%s - res = %d", __func__, res);
#endif
}

static void msir_cleanup( struct usb_skel* dev )
{
	memset( dev->bulk_in_buffer, 0, dev->bulk_in_size );

	dev->usb_valid_bytes_in_bulk_buffer = 0;

	dev->last_space = PULSE_MASK;
    
	dev->mce_bytes_left_in_packet = 0;
	dev->lircidx = 0;
	dev->lirccnt = 0;
	memset( dev->lircdata, 0, sizeof(dev->lircdata) );
}

static int set_use_inc(void* data)
{
	MOD_INC_USE_COUNT;
	return 0;
}

static void set_use_dec(void* data)
{
	/* check for unplug here */
	struct usb_skel* dev = (struct usb_skel*) data;
	if( !dev->udev )
	{ 
		lirc_unregister_plugin( dev->minor );
		lirc_buffer_free( dev->plugin->rbuf );
		kfree( dev->plugin->rbuf );
		kfree( dev->plugin );
	}
	
	MOD_DEC_USE_COUNT;
}

/*
 * msir_fetch_more_data
 *
 * The goal here is to read in more remote codes from the remote. In
 * the event that the remote isn't sending us anything, the caller
 * will block until a key is pressed (i.e. this performs phys read,
 * filtering, and queueing of data) unless dont_block is set to 1; in
 * this situation, it will perform a few reads and will exit out if it
 * does not see any appropriate data
 *
 * dev->sem should be locked when this function is called - fine grain
 * locking isn't really important here anyways
 *
 * This routine always returns the number of words available
 *
 */
static int msir_fetch_more_data( struct usb_skel* dev, int dont_block )
{
	int retries = 0;
	int words_to_read = 
		(sizeof(dev->lircdata)/sizeof(lirc_t)) - dev->lirccnt;
	int partial, this_read = 0;
	int bulkidx = 0;
	int bytes_left_in_packet = 0;
	signed char* signedp = (signed char*)dev->bulk_in_buffer;
	
	if( words_to_read == 0 )
		return dev->lirccnt;

	/* this forces all existing data to be read by lirc before we
	 * issue another usb command. this is the only form of
	 * throttling we have
	 */
	if( dev->lirccnt )
	{
		return dev->lirccnt;
	}

	/* reserve room for our leading space */
	if( dev->last_space )
		words_to_read--;
		
	while( words_to_read )
	{
		/* handle signals and USB disconnects */
		if( signal_pending(current) )
		{
			return dev->lirccnt ? dev->lirccnt : -EINTR;
		}
		if( !dev->udev )
		{
			return -ENODEV;
		}

		bulkidx = 0;

		/*
		 * perform data read (phys or from previous buffer)
		 */
        
		/* use leftovers if present, otherwise perform a read */
		if( dev->usb_valid_bytes_in_bulk_buffer )
		{
			this_read = partial = 
				dev->usb_valid_bytes_in_bulk_buffer;
			dev->usb_valid_bytes_in_bulk_buffer = 0;
		}
		else
		{
			int retval;
            
			this_read = dev->bulk_in_size;
			partial = 0;
			retval = usb_bulk_msg
				(dev->udev,
				 usb_rcvbulkpipe
				 (dev->udev, dev->bulk_in_endpointAddr),
				 (unsigned char*)dev->bulk_in_buffer,
				 this_read, &partial, HZ*10);
			
			/* retry a few times on overruns; map all
			   other errors to -EIO */
			if( retval )
			{
				if( retval == -EOVERFLOW && 
				    retries < 5 )
				{
					retries++;
					interruptible_sleep_on_timeout
						( &dev->wait_q, HZ );
					continue;
				}
				else
				{
					return -EIO;
				}
			}
            
			retries = 0;
			if( partial )
				this_read = partial;

			/* skip the header */
			bulkidx += 2;
            
			/* check for empty reads (header only) */
			if( this_read == 2 )
			{
				/* assume no data */
				if( dont_block )
				{
					break;
				}

				/* sleep for a bit before performing
				   another read */
				interruptible_sleep_on_timeout
					( &dev->wait_q, 1 );
				continue;
			}
		}

		/*
		 * process data
		 */
        
		/* at this point this_read is > 0 */
		while( bulkidx < this_read &&
		       (words_to_read > (dev->last_space ? 1 : 0)) )
			//while( bulkidx < this_read && words_to_read )
		{
			int keycode;
			int pulse = 0;
            
			/* read packet length if needed */
			if( !bytes_left_in_packet )
			{
				
				/* we assume we are on a packet length
				 * value. it is possible, in some
				 * cases, to get a packet that does
				 * not start with a length, apparently
				 * due to some sort of fragmenting,
				 * but occaisonally we do not receive
				 * the second half of a fragment
				 */
				bytes_left_in_packet = 
					128 + signedp[bulkidx++];

				/* unfortunately rather than keep all
				 * the data in the packetized format,
				 * the transceiver sends a trailing 8
				 * bytes that aren't part of the
				 * transmittion from the remote,
				 * aren't packetized, and dont really
				 * have any value. we can basically
				 * tell we have hit them if 1) we have
				 * a loooong space currently stored
				 * up, and 2) the bytes_left value for
				 * this packet is obviously wrong
				 */
				if( bytes_left_in_packet > 4  )
				{
					if( dev->mce_bytes_left_in_packet )
					{
						bytes_left_in_packet = dev->mce_bytes_left_in_packet;
						bulkidx--;
					}
					bytes_left_in_packet = 0;
					bulkidx = this_read;
				}

				/* always clear this if we have a
				   valid packet */
				dev->mce_bytes_left_in_packet = 0;
                    
				/* continue here to verify we haven't
				   hit the end of the bulk_in */
				continue;
				
			}

			/*
			 * generate mode2
			 */
            
			keycode = signedp[bulkidx++];
			if( keycode < 0 )
			{
				pulse = 1;
				keycode += 128;
			}
			keycode *= MCE_TIME_UNIT;

			bytes_left_in_packet--;
            
			if( pulse )
			{
				if( dev->last_space )
				{
					dev->lircdata[dev->lirccnt++] =
						dev->last_space;
					dev->last_space = 0;
					words_to_read--;

					/* clear the lirc_t for the pulse */
					dev->lircdata[dev->lirccnt] = 0;
				}
				dev->lircdata[dev->lirccnt] += keycode;
				dev->lircdata[dev->lirccnt] |= PULSE_BIT;
			}
			else
			{
				/* on pulse->space transition, add one
				   for the existing pulse */
				if( dev->lircdata[dev->lirccnt] &&
				    !dev->last_space )
				{
					dev->lirccnt++;
					words_to_read--;
				}
                
				dev->last_space += keycode;
			}
		}
	}
	
	/* save off some info if we are exiting mid-packet, or with
	   leftovers */
	if( bytes_left_in_packet )
	{
		dev->mce_bytes_left_in_packet = bytes_left_in_packet;
	}
	if( bulkidx < this_read )
	{
		dev->usb_valid_bytes_in_bulk_buffer = (this_read - bulkidx);
		memcpy( dev->bulk_in_buffer, &(dev->bulk_in_buffer[bulkidx]),
			dev->usb_valid_bytes_in_bulk_buffer );
	}
	return dev->lirccnt;
}

/* mceusb_add_to_buf: called by lirc_dev to fetch all available keys
 * this is used as a polling interface for us: since we set
 * plugin->sample_rate we will periodically get the below call to
 * check for new data returns 0 on success, or -ENODATA if nothing is
 * available
 */
static int mceusb_add_to_buf(void* data, struct lirc_buffer* buf )
{
	struct usb_skel* dev = (struct usb_skel*) data;

	down( &dev->sem );

	/* verify device still present */
	if( dev->udev == NULL )
	{
		up( &dev->sem );
		return -ENODEV;
	}

	if( !dev->lirccnt )
	{
		int res;
		dev->lircidx = 0;
        
		res = msir_fetch_more_data( dev, 1 );
		
		if( res == 0 )
			res = -ENODATA;
		if( res < 0 ) {
			up( &dev->sem );
			return res;
		}
	}

	if( dev->lirccnt )
	{
		int keys_to_copy;

		/* determine available buffer space and available data */
		keys_to_copy = lirc_buffer_available( buf );
		if( keys_to_copy > dev->lirccnt )
		{
			keys_to_copy = dev->lirccnt;
		}
        
		lirc_buffer_write_n( buf, (unsigned char*) &(dev->lircdata[dev->lircidx]), keys_to_copy );
		dev->lircidx += keys_to_copy;
		dev->lirccnt -= keys_to_copy;
        
		up( &dev->sem );
		return 0;
	}

	up( &dev->sem );
	return -ENODATA;
}

/**
 *	mceusb_write_bulk_callback
 */
#ifdef KERNEL_2_5 
static void mceusb_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
#else
static void mceusb_write_bulk_callback (struct urb *urb)
#endif
{
	struct usb_skel *dev = (struct usb_skel *)urb->context;

	dprintk("%s - minor %d", __func__, dev->minor);

	if ((urb->status != -ENOENT) && 
	    (urb->status != -ECONNRESET)) {
		dprintk("%s - nonzero write buld status received: %d",
			__func__, urb->status);
		return;
	}

	return;
}

/**
 *	mceusb_probe
 *
 *	Called by the usb core when a new device is connected that it 
 *	thinks this driver might be interested in.
 */
#ifdef KERNEL_2_5 
static int mceusb_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
	struct usb_device *udev = interface_to_usbdev(interface);
	struct usb_host_interface *iface_desc;
#else
static void * mceusb_probe(struct usb_device *udev, unsigned int ifnum,
			   const struct usb_device_id *id)
{
	struct usb_interface *interface = &udev->actconfig->interface[ifnum];
	struct usb_interface_descriptor *iface_desc;	
#endif
	struct usb_skel *dev = NULL;
	struct usb_endpoint_descriptor *endpoint;
	
	struct lirc_plugin* plugin;
	struct lirc_buffer* rbuf;

	int minor;
	size_t buffer_size;
	int i;
	int retval = -ENOMEM;
	
	/* See if the device offered us matches what we can accept */
	if ((udev->descriptor.idVendor != USB_MCEUSB_VENDOR_ID) ||
	    (udev->descriptor.idProduct != USB_MCEUSB_PRODUCT_ID)) {
	    	dprintk("Wrong Vendor/Product IDs");
#ifdef KERNEL_2_5
		return -ENODEV;
#else
		return NULL;
#endif
	}

	/* select a "subminor" number (part of a minor number) */
	down (&minor_table_mutex);
	for (minor = 0; minor < MAX_DEVICES; ++minor) {
		if (minor_table[minor] == NULL)
			break;
	}
	if (minor >= MAX_DEVICES) {
		info ("Too many devices plugged in, "
		      "can not handle this device.");
		goto error;
	}

	/* allocate memory for our device state and initialize it */
	dev = kmalloc (sizeof(struct usb_skel), GFP_KERNEL);
	if (dev == NULL) {
		err ("Out of memory");
#ifdef KERNEL_2_5
		retval = -ENOMEM;
#endif
		goto error;
	}
	minor_table[minor] = dev;
	
	memset (dev, 0x00, sizeof (*dev));
	init_MUTEX (&dev->sem);
	dev->udev = udev;
	dev->interface = interface;
	dev->minor = minor;

	/* set up the endpoint information */
	/* check out the endpoints */
	/* use only the first bulk-in and bulk-out endpoints */
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,4)
	iface_desc = interface->cur_altsetting;
#else
	iface_desc = &interface->altsetting[0];
#endif

#ifdef KERNEL_2_5
	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
		endpoint = &iface_desc->endpoint[i].desc;
#else
	for (i = 0; i < iface_desc->bNumEndpoints; ++i) {
		endpoint = &iface_desc->endpoint[i];
#endif
		if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) &&
		    ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
		     USB_ENDPOINT_XFER_BULK)) {
			dprintk("we found a bulk in endpoint");
			buffer_size = endpoint->wMaxPacketSize;
			dev->bulk_in_size = buffer_size;
			dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
#ifdef KERNEL_2_5
			dev->bulk_in_buffer = usb_buffer_alloc
				(udev, buffer_size, SLAB_ATOMIC, &dev->dma_in);
#else
			dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
#endif
			if (!dev->bulk_in_buffer) {
				err("Couldn't allocate bulk_in_buffer");
				goto error;
			}
		}
		
		if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == 0x00) &&
		    ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
		     USB_ENDPOINT_XFER_BULK)) {
			dprintk("we found a bulk out endpoint");
#ifdef KERNEL_2_5
			dev->write_urb = usb_alloc_urb(0, GFP_KERNEL);
#else
			dev->write_urb = usb_alloc_urb(0);
#endif
			if (!dev->write_urb) {
				err("No free urbs available");
				goto error;
			}
			buffer_size = endpoint->wMaxPacketSize;
			dev->bulk_out_size = buffer_size;
			dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
#ifdef KERNEL_2_5
			dev->bulk_out_buffer = usb_buffer_alloc(udev, buffer_size, SLAB_ATOMIC, &dev->dma_out);
#else
			dev->bulk_out_buffer = kmalloc (buffer_size, GFP_KERNEL);
#endif
			if (!dev->bulk_out_buffer) {
				err("Couldn't allocate bulk_out_buffer");
				goto error;
			}
#ifdef KERNEL_2_5
			usb_fill_bulk_urb(dev->write_urb, udev, 
				      usb_sndbulkpipe
				      (udev, endpoint->bEndpointAddress),
				      dev->bulk_out_buffer, buffer_size,
				      mceusb_write_bulk_callback, dev);
#else
			FILL_BULK_URB(dev->write_urb, udev,
				      usb_sndbulkpipe
				      (udev, endpoint->bEndpointAddress),
				      dev->bulk_out_buffer, buffer_size,
				      mceusb_write_bulk_callback, dev);
#endif 
		}
	}

	if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {
		err("Couldn't find both bulk-in and bulk-out endpoints");
		goto error;
	}

	/* init the waitq */
	init_waitqueue_head( &dev->wait_q );


	/* Set up our lirc plugin */
	if(!(plugin = kmalloc(sizeof(struct lirc_plugin), GFP_KERNEL))) {
		err("out of memory");
		goto error;
	}
	memset( plugin, 0, sizeof(struct lirc_plugin) );

	if(!(rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL))) {
		err("out of memory");
		kfree( plugin );
		goto error;
	}
    
	/* the lirc_atiusb module doesn't memset rbuf here ... ? */
	if( lirc_buffer_init( rbuf, sizeof(lirc_t), 128)) {
		err("out of memory");
		kfree( plugin );
		kfree( rbuf );
		goto error;
	}

	strcpy(plugin->name, DRIVER_NAME " ");
	plugin->minor       = minor;
	plugin->code_length = sizeof(lirc_t) * 8;
	plugin->features    = LIRC_CAN_REC_MODE2; // | LIRC_CAN_SEND_MODE2;
	plugin->data        = dev;
	plugin->rbuf        = rbuf;
	plugin->ioctl       = NULL;
	plugin->set_use_inc = &set_use_inc;
	plugin->set_use_dec = &set_use_dec;
	plugin->sample_rate = 80;   // sample at 100hz (10ms)
	plugin->add_to_buf  = &mceusb_add_to_buf;
	//    plugin->fops        = &mceusb_fops;
	plugin->owner       = THIS_MODULE;
	if( lirc_register_plugin(plugin) < 0 )
	{
		kfree( plugin );
		lirc_buffer_free( rbuf );
		kfree( rbuf );
		goto error;
	}
	dev->plugin = plugin;
	
	/* clear off the first few messages. these look like
	 * calibration or test data, i can't really tell
	 * this also flushes in case we have random ir data queued up
	 */
	{
		char junk[64];
		int partial = 0, retval, i;
		for( i = 0; i < 40; i++ )
		{
			retval = usb_bulk_msg
				(udev, usb_rcvbulkpipe
				 (udev, dev->bulk_in_endpointAddr),
				 junk, 64,
				 &partial, HZ*10);
		}
	}
    
	msir_cleanup( dev );
	mceusb_setup( udev );
	
#ifdef KERNEL_2_5
	/* we can register the device now, as it is ready */
	usb_set_intfdata (interface, dev);
#endif	
	/* let the user know what node this device is now attached to */
	//info ("USB Microsoft IR Transceiver device now attached to msir%d", dev->minor);
	up (&minor_table_mutex);
#ifdef KERNEL_2_5
	return 0;
#else
	return dev;
#endif
 error:
	mceusb_delete (dev);
	dev = NULL;
	dprintk("%s: retval = %x", __func__, retval);
	up (&minor_table_mutex);
#ifdef KERNEL_2_5
	return retval;
#else
	return NULL;
#endif
}

/**
 *	mceusb_disconnect
 *
 *	Called by the usb core when the device is removed from the system.
 *
 *	This routine guarantees that the driver will not submit any more urbs
 *	by clearing dev->udev.  It is also supposed to terminate any currently
 *	active urbs.  Unfortunately, usb_bulk_msg(), used in skel_read(), does
 *	not provide any way to do this.  But at least we can cancel an active
 *	write.
 */
#ifdef KERNEL_2_5 
static void mceusb_disconnect(struct usb_interface *interface)
#else
static void mceusb_disconnect(struct usb_device *udev, void *ptr)
#endif
{
	struct usb_skel *dev;
	int minor;
#ifdef KERNEL_2_5
	dev = usb_get_intfdata (interface);
	usb_set_intfdata (interface, NULL);
#else
	dev = (struct usb_skel *)ptr;
#endif
	
	down (&minor_table_mutex);
	down (&dev->sem);
	minor = dev->minor;

	/* unhook lirc things */
	lirc_unregister_plugin( minor );
	lirc_buffer_free( dev->plugin->rbuf );
	kfree( dev->plugin->rbuf );
	kfree( dev->plugin );
#ifdef KERNEL_2_5
	/* terminate an ongoing write */
	if (atomic_read (&dev->write_busy)) {
		usb_unlink_urb (dev->write_urb);
		wait_for_completion (&dev->write_finished);
	}

	/* prevent device read, write and ioctl */
	dev->present = 0;
#endif
	
	mceusb_delete (dev);
	
	info("Microsoft IR Transceiver #%d now disconnected", minor);
	up (&dev->sem);
	up (&minor_table_mutex);
}



/**
 *	usb_mceusb_init
 */
static int __init usb_mceusb_init(void)
{
	int result;

	/* register this driver with the USB subsystem */
	result = usb_register(&mceusb_driver);
#ifdef KERNEL_2_5	
	if ( result ) {
#else
	if ( result < 0 ) {
#endif
		err("usb_register failed for the " DRIVER_NAME " driver. error number %d",result);
#ifdef KERNEL_2_5
		return result;
#else
		return -1;
#endif
	}

	info(DRIVER_DESC " " DRIVER_VERSION);
	return 0;
}


/**
 *	usb_mceusb_exit
 */
static void __exit usb_mceusb_exit(void)
{
	/* deregister this driver with the USB subsystem */
	usb_deregister(&mceusb_driver);
}

 module_init (usb_mceusb_init);
module_exit (usb_mceusb_exit);

MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE (usb, mceusb_table);

module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Debug enabled or not");

EXPORT_NO_SYMBOLS;