-
Notifications
You must be signed in to change notification settings - Fork 0
/
usb1401.c
1597 lines (1379 loc) · 66.4 KB
/
usb1401.c
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
/***********************************************************************************
CED1401 usb driver. This basic loading is based on the usb-skeleton.c code that is:
Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
Copyright (C) 2012 Alois Schloegl <alois.schloegl@ist.ac.at>
There is not a great deal of the skeleton left.
All the remainder dealing specifically with the CED1401 is based on drivers written
by CED for other systems (mainly Windows) and is:
Copyright (C) 2010 Cambridge Electronic Design Ltd
Author Greg P Smith (greg@ced.co.uk)
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; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Endpoints
*********
There are 4 endpoints plus the control endpoint in the standard interface
provided by most 1401s. The control endpoint is used for standard USB requests,
plus various CED-specific transactions such as start self test, debug and get
the 1401 status. The other endpoints are:
1 Characters to the 1401
2 Characters from the 1401
3 Block data to the 1401
4 Block data to the host.
inside the driver these are indexed as an array from 0 to 3, transactions
over the control endpoint are carried out using a separate mechanism. The
use of the endpoints is mostly straightforward, with the driver issuing
IO request packets (IRPs) as required to transfer data to and from the 1401.
The handling of endpoint 2 is different because it is used for characters
from the 1401, which can appear spontaneously and without any other driver
activity - for example to repeatedly request DMA transfers in Spike2. The
desired effect is achieved by using an interrupt endpoint which can be
polled to see if it has data available, and writing the driver so that it
always maintains a pending read IRP from that endpoint which will read the
character data and terminate as soon as the 1401 makes data available. This
works very well, some care is taken with when you kick off this character
read IRP to avoid it being active when it is not wanted but generally it
is running all the time.
In the 2270, there are only three endpoints plus the control endpoint. In
addition to the transactions mentioned above, the control endpoint is used
to transfer character data to the 1401. The other endpoints are used as:
1 Characters from the 1401
2 Block data to the 1401
3 Block data to the host.
The type of interface available is specified by the interface subclass field
in the interface descriptor provided by the 1401. See the USB_INT_ constants
for the values that this field can hold.
****************************************************************************
Linux implementation
Although Linux Device Drivers (3rd Edition) was a major source of information,
it is very out of date. A lot of information was gleaned from the latest
usb_skeleton.c code (you need to download the kernel sources to get this).
To match the Windows version, everything is done using ioctl calls. All the
device state is held in the DEVICE_EXTENSION (named to match Windows use).
Block transfers are done by using get_user_pages() to pin down a list of
pages that we hold a pointer to in the device driver. We also allocate a
coherent transfer buffer of size STAGED_SZ (this must be a multiple of the
bulk endpoint size so that the 1401 does not realise that we break large
transfers down into smaller pieces). We use kmap_atomic() to get a kernel
va for each page, as it is required, for copying; see CopyUserSpace().
All character and data transfers are done using asynchronous IO. All Urbs are
tracked by anchoring them. Status and debug ioctls are implemented with the
synchronous non-Urb based transfers.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/usb.h>
#include <linux/mutex.h>
#include <linux/mm.h>
#include <linux/highmem.h>
#include <linux/version.h>
#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) )
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kref.h>
#include <linux/uaccess.h>
#endif
#include "usb1401.h"
/* Define these values to match your devices */
#define USB_CED_VENDOR_ID 0x0525
#define USB_CED_PRODUCT_ID 0xa0f0
/* table of devices that work with this driver */
static const struct usb_device_id ced_table[] =
{
{ USB_DEVICE(USB_CED_VENDOR_ID, USB_CED_PRODUCT_ID) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, ced_table);
/* Get a minor range for your devices from the usb maintainer */
#define USB_CED_MINOR_BASE 192
/* our private defines. if this grows any larger, use your own .h file */
#define MAX_TRANSFER (PAGE_SIZE - 512)
/* MAX_TRANSFER is chosen so that the VM is not stressed by
allocations > PAGE_SIZE and the number of packets in a page
is an integer 512 is the largest possible packet on EHCI */
#define WRITES_IN_FLIGHT 8
/* arbitrarily chosen */
/*
The cause for these errors is that the driver makes use of the functions usb_buffer_alloc() and usb_buffer_free() which got renamed in kernel 2.6.35. This is stated in the Changelog: USB: rename usb_buffer_alloc() and usb_buffer_free() users
For more clearance what the functions actually do,
usb_buffer_alloc() is renamed to usb_alloc_coherent()
usb_buffer_free() is renamed to usb_free_coherent()
This is needed on Debian 2.6.32-5-amd64
*/
#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) )
#define usb_alloc_coherent usb_buffer_alloc
#define usb_free_coherent usb_buffer_free
#define noop_llseek NULL
#endif
static struct usb_driver ced_driver;
static void ced_delete(struct kref *kref)
{
DEVICE_EXTENSION *pdx = to_DEVICE_EXTENSION(kref);
// Free up the output buffer, then free the output urb. Note that the interface member
// of pdx will probably be NULL, so cannot be used to get to dev.
usb_free_coherent(pdx->udev, OUTBUF_SZ, pdx->pCoherCharOut, pdx->pUrbCharOut->transfer_dma);
usb_free_urb(pdx->pUrbCharOut);
// Do the same for chan input
usb_free_coherent(pdx->udev, INBUF_SZ, pdx->pCoherCharIn, pdx->pUrbCharIn->transfer_dma);
usb_free_urb(pdx->pUrbCharIn);
// Do the same for the block transfers
usb_free_coherent(pdx->udev, STAGED_SZ, pdx->pCoherStagedIO, pdx->pStagedUrb->transfer_dma);
usb_free_urb(pdx->pStagedUrb);
usb_put_dev(pdx->udev);
kfree(pdx);
}
// This is the driver end of the open() call from user space.
static int ced_open(struct inode *inode, struct file *file)
{
DEVICE_EXTENSION *pdx;
int retval = 0;
int subminor = iminor(inode);
struct usb_interface* interface = usb_find_interface(&ced_driver, subminor);
if (!interface)
{
printk("%s (line %i) %s: %s - error, can't find device for minor %d", __FILE__,__LINE__,__func__, __func__,subminor);
retval = -ENODEV;
goto exit;
}
pdx = usb_get_intfdata(interface);
if (!pdx)
{
retval = -ENODEV;
goto exit;
}
dev_dbg(&interface->dev, "%s got pdx", __func__);
/* increment our usage count for the device */
kref_get(&pdx->kref);
/* lock the device to allow correctly handling errors
* in resumption */
mutex_lock(&pdx->io_mutex);
if (!pdx->open_count++)
{
retval = usb_autopm_get_interface(interface);
if (retval)
{
pdx->open_count--;
mutex_unlock(&pdx->io_mutex);
kref_put(&pdx->kref, ced_delete);
goto exit;
}
}
else
{ //uncomment this block if you want exclusive open
dev_err(&interface->dev, "%s fail: already open", __func__);
retval = -EBUSY;
pdx->open_count--;
mutex_unlock(&pdx->io_mutex);
kref_put(&pdx->kref, ced_delete);
goto exit;
}
/* prevent the device from being autosuspended */
/* save our object in the file's private structure */
file->private_data = pdx;
mutex_unlock(&pdx->io_mutex);
exit:
return retval;
}
static int ced_release(struct inode *inode, struct file *file)
{
DEVICE_EXTENSION *pdx = file->private_data;
if (pdx == NULL)
return -ENODEV;
dev_dbg(&pdx->interface->dev,"%s called", __func__);
mutex_lock(&pdx->io_mutex);
if (!--pdx->open_count && pdx->interface) // Allow autosuspend
usb_autopm_put_interface(pdx->interface);
mutex_unlock(&pdx->io_mutex);
kref_put(&pdx->kref, ced_delete); // decrement the count on our device
return 0;
}
static int ced_flush(struct file *file, fl_owner_t id)
{
int res;
DEVICE_EXTENSION *pdx = file->private_data;
if (pdx == NULL)
return -ENODEV;
dev_dbg(&pdx->interface->dev,"%s char in pend=%d", __func__, pdx->bReadCharsPending);
/* wait for io to stop */
mutex_lock(&pdx->io_mutex);
dev_dbg(&pdx->interface->dev,"%s got io_mutex", __func__);
ced_draw_down(pdx);
/* read out errors, leave subsequent opens a clean slate */
spin_lock_irq(&pdx->err_lock);
res = pdx->errors ? (pdx->errors == -EPIPE ? -EPIPE : -EIO) : 0;
pdx->errors = 0;
spin_unlock_irq(&pdx->err_lock);
mutex_unlock(&pdx->io_mutex);
dev_dbg(&pdx->interface->dev,"%s exit reached", __func__);
return res;
}
static ssize_t ced_read(struct file *file, char *buffer, size_t count,
loff_t *ppos)
{
DEVICE_EXTENSION *pdx = file->private_data;
dev_err(&pdx->interface->dev, "%s called: use ioctl for cedusb", __func__);
return 0; // as we do not do reads this way
}
static ssize_t ced_write(struct file *file, const char *user_buffer,
size_t count, loff_t *ppos)
{
DEVICE_EXTENSION *pdx = file->private_data;
dev_err(&pdx->interface->dev, "%s called: use ioctl for cedusb", __func__);
return 0;
}
/***************************************************************************
** CanAcceptIoRequests
** If the device is removed, interface is set NULL. We also clear our pointer
** from the interface, so we should make sure that pdx is not NULL. This will
** not help with a device extension held by a file.
** return true if can accept new io requests, else false
*/
static bool CanAcceptIoRequests(DEVICE_EXTENSION* pdx)
{
return pdx && pdx->interface; // Can we accept IO requests
}
/****************************************************************************
** Callback routine to complete writes. This may need to fire off another
** urb to complete the transfer.
****************************************************************************/
static void ced_writechar_callback(struct urb* pUrb)
{
DEVICE_EXTENSION *pdx = pUrb->context;
int nGot = pUrb->actual_length; // what we transferred
if (pUrb->status)
{ // sync/async unlink faults aren't errors
if (!(pUrb->status == -ENOENT || pUrb->status == -ECONNRESET || pUrb->status == -ESHUTDOWN))
{
dev_err(&pdx->interface->dev, "%s - nonzero write bulk status received: %d", __func__, pUrb->status);
}
spin_lock(&pdx->err_lock);
pdx->errors = pUrb->status;
spin_unlock(&pdx->err_lock);
nGot = 0; // and tidy up again if so
spin_lock(&pdx->charOutLock); // already at irq level
pdx->dwOutBuffGet = 0; // Reset the output buffer
pdx->dwOutBuffPut = 0;
pdx->dwNumOutput = 0; // Clear the char count
pdx->bPipeError[0] = 1; // Flag an error for later
pdx->bSendCharsPending = false; // Allow other threads again
spin_unlock(&pdx->charOutLock); // already at irq level
dev_dbg(&pdx->interface->dev, "%s - char out done, 0 chars sent", __func__);
}
else
{
dev_dbg(&pdx->interface->dev, "%s - char out done, %d chars sent", __func__, nGot);
spin_lock(&pdx->charOutLock); // already at irq level
pdx->dwNumOutput -= nGot; // Now adjust the char send buffer
pdx->dwOutBuffGet += nGot; // to match what we did
if (pdx->dwOutBuffGet >= OUTBUF_SZ) // Can't do this any earlier as data could be overwritten
pdx->dwOutBuffGet = 0;
if (pdx->dwNumOutput > 0) // if more to be done...
{
int nPipe = 0; // The pipe number to use
int iReturn;
char* pDat = &pdx->outputBuffer[pdx->dwOutBuffGet];
unsigned int dwCount = pdx->dwNumOutput; // maximum to send
if ((pdx->dwOutBuffGet+dwCount) > OUTBUF_SZ) // does it cross buffer end?
dwCount = OUTBUF_SZ - pdx->dwOutBuffGet;
spin_unlock(&pdx->charOutLock); // we are done with stuff that changes
memcpy(pdx->pCoherCharOut, pDat, dwCount); // copy output data to the buffer
usb_fill_bulk_urb(pdx->pUrbCharOut, pdx->udev,
usb_sndbulkpipe(pdx->udev, pdx->epAddr[0]),
pdx->pCoherCharOut, dwCount, ced_writechar_callback, pdx);
pdx->pUrbCharOut->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
usb_anchor_urb(pdx->pUrbCharOut, &pdx->submitted); // in case we need to kill it
iReturn = usb_submit_urb(pdx->pUrbCharOut, GFP_ATOMIC);
dev_dbg(&pdx->interface->dev, "%s n=%d>%s<", __func__, dwCount, pDat);
spin_lock(&pdx->charOutLock); // grab lock for errors
if (iReturn)
{
pdx->bPipeError[nPipe] = 1; // Flag an error to be handled later
pdx->bSendCharsPending = false; // Allow other threads again
usb_unanchor_urb(pdx->pUrbCharOut);
dev_err(&pdx->interface->dev, "%s usb_submit_urb() returned %d", __func__, iReturn);
}
}
else
pdx->bSendCharsPending = false; // Allow other threads again
spin_unlock(&pdx->charOutLock); // already at irq level
}
}
/****************************************************************************
** SendChars
** Transmit the characters in the output buffer to the 1401. This may need
** breaking down into multiple transfers.
****************************************************************************/
int SendChars(DEVICE_EXTENSION* pdx)
{
int iReturn = U14ERR_NOERROR;
spin_lock_irq(&pdx->charOutLock); // Protect ourselves
if ((!pdx->bSendCharsPending) && // Not currently sending
(pdx->dwNumOutput > 0) && // has characters to output
(CanAcceptIoRequests(pdx))) // and current activity is OK
{
unsigned int dwCount = pdx->dwNumOutput; // Get a copy of the character count
pdx->bSendCharsPending = true; // Set flag to lock out other threads
dev_dbg(&pdx->interface->dev, "Send %d chars to 1401, EP0 flag %d\n", dwCount, pdx->nPipes == 3);
// If we have only 3 end points we must send the characters to the 1401 using EP0.
if (pdx->nPipes == 3)
{
// For EP0 character transmissions to the 1401, we have to hang about until they
// are gone, as otherwise without more character IO activity they will never go.
unsigned int count = dwCount; // Local char counter
unsigned int index = 0; // The index into the char buffer
spin_unlock_irq(&pdx->charOutLock); // Free spinlock as we call USBD
while ((count > 0) && (iReturn == U14ERR_NOERROR))
{
// We have to break the transfer up into 64-byte chunks because of a 2270 problem
int n = count > 64 ? 64 : count; // Chars for this xfer, max of 64
int nSent = usb_control_msg(pdx->udev,
usb_sndctrlpipe(pdx->udev,0), // use end point 0
DB_CHARS, // bRequest
(H_TO_D|VENDOR|DEVREQ), // to the device, vendor request to the device
0,0, // value and index are both 0
&pdx->outputBuffer[index], // where to send from
n, // how much to send
1000); // timeout in jiffies
if (nSent <= 0)
{
iReturn = nSent ? nSent : -ETIMEDOUT; // if 0 chars says we timed out
dev_err(&pdx->interface->dev, "Send %d chars by EP0 failed: %d", n, iReturn);
}
else
{
dev_dbg(&pdx->interface->dev, "Sent %d chars by EP0", n);
count -= nSent;
index += nSent;
}
}
spin_lock_irq(&pdx->charOutLock); // Protect pdx changes, released by general code
pdx->dwOutBuffGet = 0; // so reset the output buffer
pdx->dwOutBuffPut = 0;
pdx->dwNumOutput = 0; // and clear the buffer count
pdx->bSendCharsPending = false; // Allow other threads again
}
else
{ // Here for sending chars normally - we hold the spin lock
int nPipe = 0; // The pipe number to use
char* pDat = &pdx->outputBuffer[pdx->dwOutBuffGet];
if ((pdx->dwOutBuffGet+dwCount) > OUTBUF_SZ) // does it cross buffer end?
dwCount = OUTBUF_SZ - pdx->dwOutBuffGet;
spin_unlock_irq(&pdx->charOutLock); // we are done with stuff that changes
memcpy(pdx->pCoherCharOut, pDat, dwCount); // copy output data to the buffer
usb_fill_bulk_urb(pdx->pUrbCharOut, pdx->udev,
usb_sndbulkpipe(pdx->udev, pdx->epAddr[0]),
pdx->pCoherCharOut, dwCount, ced_writechar_callback, pdx);
pdx->pUrbCharOut->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
usb_anchor_urb(pdx->pUrbCharOut, &pdx->submitted);
iReturn = usb_submit_urb(pdx->pUrbCharOut, GFP_KERNEL);
spin_lock_irq(&pdx->charOutLock); // grab lock for errors
if (iReturn)
{
pdx->bPipeError[nPipe] = 1; // Flag an error to be handled later
pdx->bSendCharsPending = false; // Allow other threads again
usb_unanchor_urb(pdx->pUrbCharOut); // remove from list of active urbs
}
}
}
else
if (pdx->bSendCharsPending && (pdx->dwNumOutput > 0))
dev_dbg(&pdx->interface->dev, "SendChars bSendCharsPending:true");
dev_dbg(&pdx->interface->dev, "SendChars exit code: %d", iReturn);
spin_unlock_irq(&pdx->charOutLock); // Now let go of the spinlock
return iReturn;
}
/***************************************************************************
** CopyUserSpace
** This moves memory between pinned down user space and the pCoherStagedIO
** memory buffer we use for transfers. Copy n bytes in the directions that
** is defined by pdx->StagedRead. The user space is determined by the area
** in pdx->StagedId and the offset in pdx->StagedDone. The user
** area may well not start on a page boundary, so allow for that.
**
** We have a table of physical pages that describe the area, so we can use
** this to get a virtual address that the kernel can use.
**
** pdx Is our device extension which holds all we know about the transfer.
** n The number of bytes to move one way or the other.
***************************************************************************/
static void CopyUserSpace(DEVICE_EXTENSION *pdx, int n)
{
unsigned int nArea = pdx->StagedId;
if (nArea < MAX_TRANSAREAS)
{
TRANSAREA *pArea = &pdx->rTransDef[nArea]; // area to be used
unsigned int dwOffset = pdx->StagedDone + pdx->StagedOffset + pArea->dwBaseOffset;
char* pCoherBuf = pdx->pCoherStagedIO; // coherent buffer
if (!pArea->bUsed)
{
dev_err(&pdx->interface->dev, "%s area %d unused", __func__, nArea);
return;
}
while (n)
{
int nPage = dwOffset >> PAGE_SHIFT; // page number in table
if (nPage < pArea->nPages)
{
char *pvAddress = (char*)kmap_atomic(pArea->pPages[nPage]);
if (pvAddress)
{
unsigned int uiPageOff = dwOffset & (PAGE_SIZE-1); // offset into the page
size_t uiXfer = PAGE_SIZE - uiPageOff; // max to transfer on this page
if (uiXfer > n) // limit byte count if too much
uiXfer = n; // for the page
if (pdx->StagedRead)
memcpy(pvAddress+uiPageOff, pCoherBuf, uiXfer);
else
memcpy(pCoherBuf, pvAddress+uiPageOff, uiXfer);
kunmap_atomic(pvAddress);
dwOffset += uiXfer;
pCoherBuf += uiXfer;
n -= uiXfer;
}
else
{
dev_err(&pdx->interface->dev, "%s did not map page %d", __func__, nPage);
return;
}
}
else
{
dev_err(&pdx->interface->dev, "%s exceeded pages %d", __func__, nPage);
return;
}
}
}
else
dev_err(&pdx->interface->dev, "%s bad area %d", __func__, nArea);
}
// Forward declarations for stuff used circularly
static int StageChunk(DEVICE_EXTENSION *pdx);
/***************************************************************************
** ReadWrite_Complete
**
** Completion routine for our staged read/write Irps
*/
static void staged_callback(struct urb* pUrb)
{
DEVICE_EXTENSION *pdx = pUrb->context;
unsigned int nGot = pUrb->actual_length; // what we transferred
bool bCancel = false;
bool bRestartCharInput; // used at the end
spin_lock(&pdx->stagedLock); // stop ReadWriteMem() action while this routine is running
pdx->bStagedUrbPending = false; // clear the flag for staged IRP pending
if (pUrb->status)
{ // sync/async unlink faults aren't errors
if (!(pUrb->status == -ENOENT || pUrb->status == -ECONNRESET || pUrb->status == -ESHUTDOWN))
{
dev_err(&pdx->interface->dev, "%s - nonzero write bulk status received: %d", __func__, pUrb->status);
}
else
dev_info(&pdx->interface->dev, "%s - staged xfer cancelled", __func__);
spin_lock(&pdx->err_lock);
pdx->errors = pUrb->status;
spin_unlock(&pdx->err_lock);
nGot = 0; // and tidy up again if so
bCancel = true;
}
else
{
dev_dbg(&pdx->interface->dev, "%s %d chars xferred", __func__, nGot);
if (pdx->StagedRead) // if reading, save to user space
CopyUserSpace(pdx, nGot); // copy from buffer to user
if (nGot == 0)
dev_dbg(&pdx->interface->dev, "%s ZLP", __func__);
}
// Update the transfer length based on the TransferBufferLength value in the URB
pdx->StagedDone += nGot;
dev_dbg(&pdx->interface->dev, "%s, done %d bytes of %d", __func__, pdx->StagedDone, pdx->StagedLength);
if ((pdx->StagedDone == pdx->StagedLength) || // If no more to do
(bCancel)) // or this IRP was cancelled
{
TRANSAREA* pArea = &pdx->rTransDef[pdx->StagedId]; // Transfer area info
dev_dbg(&pdx->interface->dev, "%s transfer done, bytes %d, cancel %d", __func__, pdx->StagedDone, bCancel);
// Here is where we sort out what to do with this transfer if using a circular buffer. We have
// a completed transfer that can be assumed to fit into the transfer area. We should be able to
// add this to the end of a growing block or to use it to start a new block unless the code
// that calculates the offset to use (in ReadWriteMem) is totally duff.
if ((pArea->bCircular) && (pArea->bCircToHost) && (!bCancel) && // Time to sort out circular buffer info?
(pdx->StagedRead)) // Only for tohost transfers for now
{
if (pArea->aBlocks[1].dwSize > 0) // If block 1 is in use we must append to it
{
if (pdx->StagedOffset == (pArea->aBlocks[1].dwOffset + pArea->aBlocks[1].dwSize))
{
pArea->aBlocks[1].dwSize += pdx->StagedLength;
dev_dbg(&pdx->interface->dev, "RWM_Complete, circ block 1 now %d bytes at %d",
pArea->aBlocks[1].dwSize, pArea->aBlocks[1].dwOffset);
}
else
{
// Here things have gone very, very, wrong, but I cannot see how this can actually be achieved
pArea->aBlocks[1].dwOffset = pdx->StagedOffset;
pArea->aBlocks[1].dwSize = pdx->StagedLength;
dev_err(&pdx->interface->dev, "%s ERROR, circ block 1 re-started %d bytes at %d",
__func__, pArea->aBlocks[1].dwSize, pArea->aBlocks[1].dwOffset);
}
}
else // If block 1 is not used, we try to add to block 0
{
if (pArea->aBlocks[0].dwSize > 0) // Got stored block 0 information?
{ // Must append onto the existing block 0
if (pdx->StagedOffset == (pArea->aBlocks[0].dwOffset + pArea->aBlocks[0].dwSize))
{
pArea->aBlocks[0].dwSize += pdx->StagedLength; // Just add this transfer in
dev_dbg(&pdx->interface->dev, "RWM_Complete, circ block 0 now %d bytes at %d",
pArea->aBlocks[0].dwSize, pArea->aBlocks[0].dwOffset);
}
else // If it doesn't append, put into new block 1
{
pArea->aBlocks[1].dwOffset = pdx->StagedOffset;
pArea->aBlocks[1].dwSize = pdx->StagedLength;
dev_dbg(&pdx->interface->dev, "RWM_Complete, circ block 1 started %d bytes at %d",
pArea->aBlocks[1].dwSize, pArea->aBlocks[1].dwOffset);
}
}
else // No info stored yet, just save in block 0
{
pArea->aBlocks[0].dwOffset = pdx->StagedOffset;
pArea->aBlocks[0].dwSize = pdx->StagedLength;
dev_dbg(&pdx->interface->dev, "RWM_Complete, circ block 0 started %d bytes at %d",
pArea->aBlocks[0].dwSize, pArea->aBlocks[0].dwOffset);
}
}
}
if (!bCancel) // Don't generate an event if cancelled
{
dev_dbg(&pdx->interface->dev, "RWM_Complete, bCircular %d, bToHost %d, eStart %d, eSize %d",
pArea->bCircular, pArea->bEventToHost, pArea->dwEventSt, pArea->dwEventSz);
if ((pArea->dwEventSz) && // Set a user-mode event...
(pdx->StagedRead == pArea->bEventToHost)) // ...on transfers in this direction?
{
int iWakeUp = 0; // assume
// If we have completed the right sort of DMA transfer then set the event to notify
// the user code to wake up anyone that is waiting.
if ((pArea->bCircular) && // Circular areas use a simpler test
(pArea->bCircToHost)) // only in supported direction
{ // Is total data waiting up to size limit?
unsigned int dwTotal = pArea->aBlocks[0].dwSize + pArea->aBlocks[1].dwSize;
iWakeUp = (dwTotal >= pArea->dwEventSz);
}
else
{
unsigned int transEnd = pdx->StagedOffset + pdx->StagedLength;
unsigned int eventEnd = pArea->dwEventSt + pArea->dwEventSz;
iWakeUp = (pdx->StagedOffset < eventEnd) && (transEnd > pArea->dwEventSt);
}
if (iWakeUp)
{
dev_dbg(&pdx->interface->dev, "About to set event to notify app");
wake_up_interruptible(&pArea->wqEvent); // wake up waiting processes
++pArea->iWakeUp; // increment wakeup count
}
}
}
pdx->dwDMAFlag = MODE_CHAR; // Switch back to char mode before ReadWriteMem call
if (!bCancel) // Don't look for waiting transfer if cancelled
{
// If we have a transfer waiting, kick it off
if (pdx->bXFerWaiting) // Got a block xfer waiting?
{
int iReturn;
dev_info(&pdx->interface->dev, "*** RWM_Complete *** pending transfer will now be set up!!!");
iReturn = ReadWriteMem(pdx, !pdx->rDMAInfo.bOutWard, pdx->rDMAInfo.wIdent, pdx->rDMAInfo.dwOffset, pdx->rDMAInfo.dwSize);
if (iReturn)
dev_err(&pdx->interface->dev, "RWM_Complete rw setup failed %d", iReturn);
}
}
}
else // Here for more to do
StageChunk(pdx); // fire off the next bit
// While we hold the stagedLock, see if we should reallow character input ints
// Don't allow if cancelled, or if a new block has started or if there is a waiting block.
// This feels wrong as we should ask which spin lock protects dwDMAFlag.
bRestartCharInput = !bCancel && (pdx->dwDMAFlag == MODE_CHAR) && !pdx->bXFerWaiting;
spin_unlock(&pdx->stagedLock); // Finally release the lock again
// This is not correct as dwDMAFlag is protected by the staged lock, but it is treated
// in Allowi as if it were protected by the char lock. In any case, most systems will
// not be upset by char input during DMA... sigh. Needs sorting out.
if (bRestartCharInput) // may be out of date, but...
Allowi(pdx, true); // ...Allowi tests a lock too.
dev_dbg(&pdx->interface->dev, "%s done", __func__);
}
/****************************************************************************
** StageChunk
**
** Generates the next chunk of data making up a staged transfer.
**
** The calling code must have acquired the staging spinlock before calling
** this function, and is responsible for releasing it. We are at callback level.
****************************************************************************/
static int StageChunk(DEVICE_EXTENSION *pdx)
{
int iReturn = U14ERR_NOERROR;
unsigned int ChunkSize;
int nPipe = pdx->StagedRead ? 3 : 2; // The pipe number to use for reads or writes
if (pdx->nPipes == 3) nPipe--; // Adjust for the 3-pipe case
if (nPipe < 0) // and trap case that should never happen
return U14ERR_FAIL;
if (!CanAcceptIoRequests(pdx)) // got sudden remove?
{
dev_info(&pdx->interface->dev, "%s sudden remove, giving up", __func__);
return U14ERR_FAIL; // could do with a better error
}
ChunkSize = (pdx->StagedLength - pdx->StagedDone); // transfer length remaining
if (ChunkSize > STAGED_SZ) // make sure to keep legal
ChunkSize = STAGED_SZ; // limit to max allowed
if (!pdx->StagedRead) // if writing...
CopyUserSpace(pdx, ChunkSize); // ...copy data into the buffer
usb_fill_bulk_urb(pdx->pStagedUrb, pdx->udev,
pdx->StagedRead ? usb_rcvbulkpipe(pdx->udev, pdx->epAddr[nPipe]):
usb_sndbulkpipe(pdx->udev, pdx->epAddr[nPipe]),
pdx->pCoherStagedIO, ChunkSize, staged_callback, pdx);
pdx->pStagedUrb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
usb_anchor_urb(pdx->pStagedUrb, &pdx->submitted); // in case we need to kill it
iReturn = usb_submit_urb(pdx->pStagedUrb, GFP_ATOMIC);
if (iReturn)
{
usb_unanchor_urb(pdx->pStagedUrb); // kill it
pdx->bPipeError[nPipe] = 1; // Flag an error to be handled later
dev_err(&pdx->interface->dev, "%s submit urb failed, code %d", __func__, iReturn);
}
else
pdx->bStagedUrbPending = true; // Set the flag for staged URB pending
dev_dbg(&pdx->interface->dev, "%s done so far:%d, this size:%d", __func__, pdx->StagedDone, ChunkSize);
return iReturn;
}
/***************************************************************************
** ReadWriteMem
**
** This routine is used generally for block read and write operations.
** Breaks up a read or write in to specified sized chunks, as specified by pipe
** information on maximum transfer size.
**
** Any code that calls this must be holding the stagedLock
**
** Arguments:
** DeviceObject - pointer to our FDO (Functional Device Object)
** Read - TRUE for read, FALSE for write. This is from POV of the driver
** wIdent - the transfer area number - defines memory area and more.
** dwOffs - the start offset within the transfer area of the start of this
** transfer.
** dwLen - the number of bytes to transfer.
*/
int ReadWriteMem(DEVICE_EXTENSION *pdx, bool Read, unsigned short wIdent,
unsigned int dwOffs, unsigned int dwLen)
{
TRANSAREA* pArea = &pdx->rTransDef[wIdent]; // Transfer area info
if (!CanAcceptIoRequests(pdx)) // Are we in a state to accept new requests?
{
dev_err(&pdx->interface->dev, "%s can't accept requests", __func__);
return U14ERR_FAIL;
}
dev_dbg(&pdx->interface->dev, "%s xfer %d bytes to %s, offset %d, area %d",
__func__, dwLen, Read ? "host" : "1401", dwOffs, wIdent);
// Amazingly, we can get an escape sequence back before the current staged Urb is done, so we
// have to check for this situation and, if so, wait until all is OK.
if (pdx->bStagedUrbPending)
{
pdx->bXFerWaiting = true; // Flag we are waiting
dev_info(&pdx->interface->dev, "%s xfer is waiting, as previous staged pending", __func__);
return U14ERR_NOERROR;
}
if (dwLen == 0) // allow 0-len read or write; just return success
{
dev_dbg(&pdx->interface->dev, "%s OK; zero-len read/write request", __func__);
return U14ERR_NOERROR;
}
if ((pArea->bCircular) && // Circular transfer?
(pArea->bCircToHost) && (Read)) // In a supported direction
{ // If so, we sort out offset ourself
bool bWait = false; // Flag for transfer having to wait
dev_dbg(&pdx->interface->dev, "Circular buffers are %d at %d and %d at %d",
pArea->aBlocks[0].dwSize, pArea->aBlocks[0].dwOffset, pArea->aBlocks[1].dwSize, pArea->aBlocks[1].dwOffset);
if (pArea->aBlocks[1].dwSize > 0) // Using the second block already?
{
dwOffs = pArea->aBlocks[1].dwOffset + pArea->aBlocks[1].dwSize; // take offset from that
bWait = (dwOffs + dwLen) > pArea->aBlocks[0].dwOffset; // Wait if will overwrite block 0?
bWait |= (dwOffs + dwLen) > pArea->dwLength; // or if it overflows the buffer
}
else // Area 1 not in use, try to use area 0
{
if (pArea->aBlocks[0].dwSize == 0) // Reset block 0 if not in use
pArea->aBlocks[0].dwOffset = 0;
dwOffs = pArea->aBlocks[0].dwOffset + pArea->aBlocks[0].dwSize;
if ((dwOffs+dwLen) > pArea->dwLength) // Off the end of the buffer?
{
pArea->aBlocks[1].dwOffset = 0; // Set up to use second block
dwOffs = 0;
bWait = (dwOffs + dwLen) > pArea->aBlocks[0].dwOffset; // Wait if will overwrite block 0?
bWait |= (dwOffs + dwLen) > pArea->dwLength; // or if it overflows the buffer
}
}
if (bWait) // This transfer will have to wait?
{
pdx->bXFerWaiting = true; // Flag we are waiting
dev_dbg(&pdx->interface->dev, "%s xfer waiting for circular buffer space", __func__);
return U14ERR_NOERROR;
}
dev_dbg(&pdx->interface->dev, "%s circular xfer, %d bytes starting at %d", __func__, dwLen, dwOffs);
}
// Save the parameters for the read\write transfer
pdx->StagedRead = Read; // Save the parameters for this read
pdx->StagedId = wIdent; // ID allows us to get transfer area info
pdx->StagedOffset = dwOffs; // The area within the transfer area
pdx->StagedLength = dwLen;
pdx->StagedDone = 0; // Initialise the byte count
pdx->dwDMAFlag = MODE_LINEAR; // Set DMA mode flag at this point
pdx->bXFerWaiting = false; // Clearly not a transfer waiting now
// KeClearEvent(&pdx->StagingDoneEvent); // Clear the transfer done event
StageChunk(pdx); // fire off the first chunk
return U14ERR_NOERROR;
}
/****************************************************************************
**
** ReadChar
**
** Reads a character a buffer. If there is no more
** data we return FALSE. Used as part of decoding a DMA request.
**
****************************************************************************/
static bool ReadChar(unsigned char* pChar, char* pBuf, unsigned int* pdDone, unsigned int dGot)
{
bool bRead = false;
unsigned int dDone = *pdDone;
if (dDone < dGot) // If there is more data
{
*pChar = (unsigned char)pBuf[dDone];// Extract the next char
dDone++; // Increment the done count
*pdDone = dDone;
bRead = true; // and flag success
}
return bRead;
}
#ifdef NOTUSED
/****************************************************************************
**
** ReadWord
**
** Reads a word from the 1401, just uses ReadChar twice; passes on any error
**
*****************************************************************************/
static bool ReadWord(unsigned short* pWord, char* pBuf, unsigned int* pdDone, unsigned int dGot)
{
if (ReadChar((unsigned char*)pWord, pBuf, pdDone, dGot))
return ReadChar(((unsigned char*)pWord)+1, pBuf, pdDone, dGot);
else
return false;
}
#endif
/****************************************************************************
** ReadHuff
**
** Reads a coded number in and returns it, Code is:
** If data is in range 0..127 we recieve 1 byte. If data in range 128-16383
** we recieve two bytes, top bit of first indicates another on its way. If
** data in range 16383-4194303 we get three bytes, top two bits of first set
** to indicate three byte total.
**
*****************************************************************************/
static bool ReadHuff(volatile unsigned int* pDWord, char* pBuf, unsigned int* pdDone, unsigned int dGot)
{
unsigned char ucData; /* for each read to ReadChar */
bool bReturn = true; /* assume we will succeed */
unsigned int dwData = 0; /* Accumulator for the data */
if (ReadChar(&ucData, pBuf, pdDone, dGot))
{
dwData = ucData; /* copy the data */
if ((dwData & 0x00000080) != 0) /* Bit set for more data ? */
{
dwData &= 0x0000007F; /* Clear the relevant bit */
if (ReadChar(&ucData, pBuf, pdDone, dGot))
{
dwData = (dwData << 8) | ucData;
if ((dwData & 0x00004000) != 0) /* three byte sequence ? */
{
dwData &= 0x00003FFF; /* Clear the relevant bit */
if (ReadChar(&ucData, pBuf, pdDone, dGot))
dwData = (dwData << 8) | ucData;
else
bReturn = false;
}
}
else
bReturn = false; /* couldn't read data */
}
}
else
bReturn = false;
*pDWord = dwData; /* return the data */
return bReturn;
}
/***************************************************************************
**
** ReadDMAInfo
**
** Tries to read info about the dma request from the 1401 and decode it into
** the dma descriptor block. We have at this point had the escape character
** from the 1401 and now we must read in the rest of the information about
** the transfer request. Returns FALSE if 1401 fails to respond or obselete
** code from 1401 or bad parameters.
**
** The pBuf char pointer does not include the initial escape character, so
** we start handling the data at offset zero.
**
*****************************************************************************/
static bool ReadDMAInfo(volatile DMADESC* pDmaDesc, DEVICE_EXTENSION *pdx,
char* pBuf, unsigned int dwCount)
{
bool bResult = false; // assume we won't succeed
unsigned char ucData;
unsigned int dDone = 0; // We haven't parsed anything so far
dev_dbg(&pdx->interface->dev, "%s", __func__);
if (ReadChar(&ucData, pBuf, &dDone, dwCount))
{
unsigned char ucTransCode = (ucData & 0x0F); // get code for transfer type
unsigned short wIdent = ((ucData >> 4) & 0x07); // and area identifier
// fill in the structure we were given
pDmaDesc->wTransType = ucTransCode; // type of transfer
pDmaDesc->wIdent = wIdent; // area to use
pDmaDesc->dwSize = 0; // initialise other bits
pDmaDesc->dwOffset = 0;
dev_dbg(&pdx->interface->dev, "%s type: %d ident: %d", __func__, pDmaDesc->wTransType, pDmaDesc->wIdent);
pDmaDesc->bOutWard = (ucTransCode != TM_EXTTOHOST); // set transfer direction
switch (ucTransCode)
{
case TM_EXTTOHOST: // Extended linear transfer modes (the only ones!)
case TM_EXTTO1401:
{
bResult = ReadHuff(&(pDmaDesc->dwOffset), pBuf, &dDone, dwCount) &&
ReadHuff(&(pDmaDesc->dwSize), pBuf, &dDone, dwCount);
if (bResult)
{
dev_dbg(&pdx->interface->dev, "%s xfer offset & size %d %d",
__func__, pDmaDesc->dwOffset, pDmaDesc->dwSize);
if ((wIdent >= MAX_TRANSAREAS) || // Illegal area number, or...
(!pdx->rTransDef[wIdent].bUsed) || // area not set up, or...
(pDmaDesc->dwOffset > pdx->rTransDef[wIdent].dwLength) || // range/size
((pDmaDesc->dwOffset + pDmaDesc->dwSize) > (pdx->rTransDef[wIdent].dwLength)))
{
bResult = false; // bad parameter(s)
dev_dbg(&pdx->interface->dev, "%s bad param - id %d, bUsed %d, offset %d, size %d, area length %d",
__func__, wIdent, pdx->rTransDef[wIdent].bUsed, pDmaDesc->dwOffset, pDmaDesc->dwSize,
pdx->rTransDef[wIdent].dwLength);
}
}
break;
}
default:
break;
}
}
else