aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/gadget/function/f_ncm.c
blob: e93b0eb74c08ca1e35989bd1abb1a435fc04400c (plain)
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
/* $XFree86$ */
/* $XdotOrg$ */
/*
 * Data and prototypes for init.c
 *
 * Copyright (C) 2001-2005 by Thomas Winischhofer, Vienna, Austria
 *
 * If distributed as part of the Linux kernel, the following license terms
 * apply:
 *
 * * 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 named License,
 * * or 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
 *
 * Otherwise, the following license terms apply:
 *
 * * Redistribution and use in source and binary forms, with or without
 * * modification, are permitted provided that the following conditions
 * * are met:
 * * 1) Redistributions of source code must retain the above copyright
 * *    notice, this list of conditions and the following disclaimer.
 * * 2) Redistributions in binary form must reproduce the above copyright
 * *    notice, this list of conditions and the following disclaimer in the
 * *    documentation and/or other materials provided with the distribution.
 * * 3) The name of the author may not be used to endorse or promote products
 * *    derived from this software without specific prior written permission.
 * *
 * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Author: 	Thomas Winischhofer <thomas@winischhofer.net>
 *
 */

#ifndef _INIT_H_
#define _INIT_H_

#include "osdef.h"
#include "initdef.h"

#ifdef SIS_XORG_XF86
#include "sis.h"
#define SIS_NEED_inSISREG
#define SIS_NEED_inSISREGW
#define SIS_NEED_inSISREGL
#define SIS_NEED_outSISREG
#define SIS_NEED_outSISREGW
#define SIS_NEED_outSISREGL
#include "sis_regs.h"
#endif

#ifdef SIS_LINUX_KERNEL
#include "vgatypes.h"
#include "vstruct.h"
#ifdef SIS_CP
#undef SIS_CP
#endif
#include <linux/types.h>
#include <asm/io.h>
#include <linux/fb.h>
#include "sis.h"
#include <video/sisfb.h>
#endif

/* Mode numbers */
static const unsigned short ModeIndex_320x200[]      = {0x59, 0x41, 0x00, 0x4f};
static const unsigned short ModeIndex_320x240[]      = {0x50, 0x56, 0x00, 0x53};
static const unsigned short ModeIndex_320x240_FSTN[] = {0x5a, 0x5b, 0x00, 0x00};  /* FSTN */
static const unsigned short ModeIndex_400x300[]      = {0x51, 0x57, 0x00, 0x54};
static const unsigned short ModeIndex_512x384[]      = {0x52, 0x58, 0x00, 0x5c};
static const unsigned short ModeIndex_640x400[]      = {0x2f, 0x5d, 0x00, 0x5e};
static const unsigned short ModeIndex_640x480[]      = {0x2e, 0x44, 0x00, 0x62};
static const unsigned short ModeIndex_720x480[]      = {0x31, 0x33, 0x00, 0x35};
static const unsigned short ModeIndex_720x576[]      = {0x32, 0x34, 0x00, 0x36};
static const unsigned short ModeIndex_768x576[]      = {0x5f, 0x60, 0x00, 0x61};
static const unsigned short ModeIndex_800x480[]      = {0x70, 0x7a, 0x00, 0x76};
static const unsigned short ModeIndex_800x600[]      = {0x30, 0x47, 0x00, 0x63};
static const unsigned short ModeIndex_848x480[]      = {0x39, 0x3b, 0x00, 0x3e};
static const unsigned short ModeIndex_856x480[]      = {0x3f, 0x42, 0x00, 0x45};
static const unsigned short ModeIndex_960x540[]      = {0x1d, 0x1e, 0x00, 0x1f};  /* 315 series only */
static const unsigned short ModeIndex_960x600[]      = {0x20, 0x21, 0x00, 0x22};  /* 315 series only */
static const unsigned short ModeIndex_1024x768[]     = {0x38, 0x4a, 0x00, 0x64};
static const unsigned short ModeIndex_1024x576[]     = {0x71, 0x74, 0x00, 0x77};
static const unsigned short ModeIndex_1024x600[]     = {0x20, 0x21, 0x00, 0x22};  /* 300 series only */
static const unsigned short ModeIndex_1280x1024[]    = {0x3a, 0x4d, 0x00, 0x65};
static const unsigned short ModeIndex_1280x960[]     = {0x7c, 0x7d, 0x00, 0x7e};
static const unsigned short ModeIndex_1152x768[]     = {0x23, 0x24, 0x00, 0x25};  /* 300 series only */
static const unsigned short ModeIndex_1152x864[]     = {0x29, 0x2a, 0x00, 0x2b};
static const unsigned short ModeIndex_300_1280x768[] = {0x55, 0x5a, 0x00, 0x5b};
static const unsigned short ModeIndex_310_1280x768[] = {0x23, 0x24, 0x00, 0x25};
static const unsigned short ModeIndex_1280x720[]     = {0x79, 0x75, 0x00, 0x78};
static const unsigned short ModeIndex_1280x800[]     = {0x14, 0x15, 0x00, 0x16};
static const unsigned short ModeIndex_1280x854[]     = {0x1a, 0x1b, 0x00, 0x1c};
static const unsigned short ModeIndex_1360x768[]     = {0x48, 0x4b, 0x00, 0x4e};
static const unsigned short ModeIndex_300_1360x1024[]= {0x67, 0x6f, 0x00, 0x72};  /* 300 series, BARCO only */
static const unsigned short ModeIndex_1400x1050[]    = {0x26, 0x27, 0x00, 0x28};  /* 315 series only */
static const unsigned short ModeIndex_1680x1050[]    = {0x17, 0x18, 0x00, 0x19};  /* 315 series only */
static const unsigned short ModeIndex_1600x1200[]    = {0x3c, 0x3d, 0x00, 0x66};
static const unsigned short ModeIndex_1920x1080[]    = {0x2c, 0x2d, 0x00, 0x73};  /* 315 series only */
static const unsigned short ModeIndex_1920x1440[]    = {0x68, 0x69, 0x00, 0x6b};
static const unsigned short ModeIndex_300_2048x1536[]= {0x6c, 0x6d, 0x00, 0x00};
static const unsigned short ModeIndex_310_2048x1536[]= {0x6c, 0x6d, 0x00, 0x6e};

static const unsigned short SiS_DRAMType[17][5]={
	{0x0C,0x0A,0x02,0x40,0x39},
	{0x0D,0x0A,0x01,0x40,0x48},
	{0x0C,0x09,0x02,0x20,0x35},
	{0x0D,0x09,0x01,0x20,0x44},
	{0x0C,0x08,0x02,0x10,0x31},
	{0x0D,0x08,0x01,0x10,0x40},
	{0x0C,0x0A,0x01,0x20,0x34},
	{0x0C,0x09,0x01,0x08,0x32},
	{0x0B,0x08,0x02,0x08,0x21},
	{0x0C,0x08,0x01,0x08,0x30},
	{0x0A,0x08,0x02,0x04,0x11},
	{0x0B,0x0A,0x01,0x10,0x28},
	{0x09,0x08,0x02,0x02,0x01},
	{0x0B,0x09,0x01,0x08,0x24},
	{0x0B,0x08,0x01,0x04,0x20},
	{0x0A,0x08,0x01,0x02,0x10},
	{0x09,0x08,0x01,0x01,0x00}
};

static const unsigned short SiS_SDRDRAM_TYPE[13][5] =
{
	{ 2,12, 9,64,0x35},
	{ 1,13, 9,64,0x44},
	{ 2,12, 8,32,0x31},
	{ 2,11, 9,32,0x25},
	{ 1,12, 9,32,0x34},
	{ 1,13, 8,32,0x40},
	{ 2,11, 8,16,0x21},
	{ 1,12, 8,16,0x30},
	{ 1,11, 9,16,0x24},
	{ 1,11, 8, 8,0x20},
	{ 2, 9, 8, 4,0x01},
	{ 1,10, 8, 4,0x10},
	{ 1, 9, 8, 2,0x00}
};

static const unsigned short SiS_DDRDRAM_TYPE[4][5] =
{
	{ 2,12, 9,64,0x35},
	{ 2,12, 8,32,0x31},
	{ 2,11, 8,16,0x21},
	{ 2, 9, 8, 4,0x01}
};

static const unsigned char SiS_MDA_DAC[] =
{
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
        0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15,
        0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15,
        0x3F,0x3F,0x3F,0x3F,0x3F,
@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #f92672 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #f92672 } /* Operator.Word */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}
@media (prefers-color-scheme: light) {
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
}
// SPDX-License-Identifier: GPL-2.0+
/*
 * f_ncm.c -- USB CDC Network (NCM) link function driver
 *
 * Copyright (C) 2010 Nokia Corporation
 * Contact: Yauheni Kaliuta <yauheni.kaliuta@nokia.com>
 *
 * The driver borrows from f_ecm.c which is:
 *
 * Copyright (C) 2003-2005,2008 David Brownell
 * Copyright (C) 2008 Nokia Corporation
 */

#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/etherdevice.h>
#include <linux/crc32.h>

#include <linux/usb/cdc.h>

#include "u_ether.h"
#include "u_ether_configfs.h"
#include "u_ncm.h"
#include "configfs.h"

/*
 * This function is a "CDC Network Control Model" (CDC NCM) Ethernet link.
 * NCM is intended to be used with high-speed network attachments.
 *
 * Note that NCM requires the use of "alternate settings" for its data
 * interface.  This means that the set_alt() method has real work to do,
 * and also means that a get_alt() method is required.
 */

/* to trigger crc/non-crc ndp signature */

#define NCM_NDP_HDR_CRC		0x01000000

enum ncm_notify_state {
	NCM_NOTIFY_NONE,		/* don't notify */
	NCM_NOTIFY_CONNECT,		/* issue CONNECT next */
	NCM_NOTIFY_SPEED,		/* issue SPEED_CHANGE next */
};

struct f_ncm {
	struct gether			port;
	u8				ctrl_id, data_id;

	char				ethaddr[14];

	struct usb_ep			*notify;
	struct usb_request		*notify_req;
	u8				notify_state;
	atomic_t			notify_count;
	bool				is_open;

	const struct ndp_parser_opts	*parser_opts;
	bool				is_crc;
	u32				ndp_sign;

	/*
	 * for notification, it is accessed from both
	 * callback and ethernet open/close
	 */
	spinlock_t			lock;

	struct net_device		*netdev;

	/* For multi-frame NDP TX */
	struct sk_buff			*skb_tx_data;
	struct sk_buff			*skb_tx_ndp;
	u16				ndp_dgram_count;
	struct hrtimer			task_timer;
};

static inline struct f_ncm *func_to_ncm(struct usb_function *f)
{
	return container_of(f, struct f_ncm, port.func);
}

/* peak (theoretical) bulk transfer rate in bits-per-second */
static inline unsigned ncm_bitrate(struct usb_gadget *g)
{
	if (!g)
		return 0;
	else if (gadget_is_superspeed(g) && g->speed >= USB_SPEED_SUPER_PLUS)
		return 4250000000U;
	else if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
		return 3750000000U;
	else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
		return 13 * 512 * 8 * 1000 * 8;
	else
		return 19 *  64 * 1 * 1000 * 8;
}

/*-------------------------------------------------------------------------*/

/*
 * We cannot group frames so use just the minimal size which ok to put
 * one max-size ethernet frame.
 * If the host can group frames, allow it to do that, 16K is selected,
 * because it's used by default by the current linux host driver
 */
#define NTB_DEFAULT_IN_SIZE	16384
#define NTB_OUT_SIZE		16384

/* Allocation for storing the NDP, 32 should suffice for a
 * 16k packet. This allows a maximum of 32 * 507 Byte packets to
 * be transmitted in a single 16kB skb, though when sending full size
 * packets this limit will be plenty.
 * Smaller packets are not likely to be trying to maximize the
 * throughput and will be mstly sending smaller infrequent frames.
 */
#define TX_MAX_NUM_DPE		32

/* Delay for the transmit to wait before sending an unfilled NTB frame. */
#define TX_TIMEOUT_NSECS	300000

#define FORMATS_SUPPORTED	(USB_CDC_NCM_NTB16_SUPPORTED |	\
				 USB_CDC_NCM_NTB32_SUPPORTED)

static struct usb_cdc_ncm_ntb_parameters ntb_parameters = {
	.wLength = cpu_to_le16(sizeof(ntb_parameters)),
	.bmNtbFormatsSupported = cpu_to_le16(FORMATS_SUPPORTED),
	.dwNtbInMaxSize = cpu_to_le32(NTB_DEFAULT_IN_SIZE),
	.wNdpInDivisor = cpu_to_le16(4),
	.wNdpInPayloadRemainder = cpu_to_le16(0),
	.wNdpInAlignment = cpu_to_le16(4),

	.dwNtbOutMaxSize = cpu_to_le32(NTB_OUT_SIZE),
	.wNdpOutDivisor = cpu_to_le16(4),
	.wNdpOutPayloadRemainder = cpu_to_le16(0),
	.wNdpOutAlignment = cpu_to_le16(4),
};

/*
 * Use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one
 * packet, to simplify cancellation; and a big transfer interval, to
 * waste less bandwidth.
 */

#define NCM_STATUS_INTERVAL_MS		32
#define NCM_STATUS_BYTECOUNT		16	/* 8 byte header + data */

static struct usb_interface_assoc_descriptor ncm_iad_desc = {
	.bLength =		sizeof ncm_iad_desc,
	.bDescriptorType =	USB_DT_INTERFACE_ASSOCIATION,

	/* .bFirstInterface =	DYNAMIC, */
	.bInterfaceCount =	2,	/* control + data */
	.bFunctionClass =	USB_CLASS_COMM,
	.bFunctionSubClass =	USB_CDC_SUBCLASS_NCM,
	.bFunctionProtocol =	USB_CDC_PROTO_NONE,
	/* .iFunction =		DYNAMIC */
};

/* interface descriptor: */

static struct usb_interface_descriptor ncm_control_intf = {
	.bLength =		sizeof ncm_control_intf,
	.bDescriptorType =	USB_DT_INTERFACE,

	/* .bInterfaceNumber = DYNAMIC */
	.bNumEndpoints =	1,
	.bInterfaceClass =	USB_CLASS_COMM,
	.bInterfaceSubClass =	USB_CDC_SUBCLASS_NCM,
	.bInterfaceProtocol =	USB_CDC_PROTO_NONE,
	/* .iInterface = DYNAMIC */
};

static struct usb_cdc_header_desc ncm_header_desc = {
	.bLength =		sizeof ncm_header_desc,
	.bDescriptorType =	USB_DT_CS_INTERFACE,
	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,

	.bcdCDC =		cpu_to_le16(0x0110),
};

static struct usb_cdc_union_desc ncm_union_desc = {
	.bLength =		sizeof(ncm_union_desc),
	.bDescriptorType =	USB_DT_CS_INTERFACE,
	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
	/* .bMasterInterface0 =	DYNAMIC */
	/* .bSlaveInterface0 =	DYNAMIC */
};

static struct usb_cdc_ether_desc ecm_desc = {
	.bLength =		sizeof ecm_desc,
	.bDescriptorType =	USB_DT_CS_INTERFACE,
	.bDescriptorSubType =	USB_CDC_ETHERNET_TYPE,

	/* this descriptor actually adds value, surprise! */
	/* .iMACAddress = DYNAMIC */
	.bmEthernetStatistics =	cpu_to_le32(0), /* no statistics */
	.wMaxSegmentSize =	cpu_to_le16(ETH_FRAME_LEN),
	.wNumberMCFilters =	cpu_to_le16(0),
	.bNumberPowerFilters =	0,
};

#define NCAPS	(USB_CDC_NCM_NCAP_ETH_FILTER | USB_CDC_NCM_NCAP_CRC_MODE)

static struct usb_cdc_ncm_desc ncm_desc = {
	.bLength =		sizeof ncm_desc,
	.bDescriptorType =	USB_DT_CS_INTERFACE,
	.bDescriptorSubType =	USB_CDC_NCM_TYPE,

	.bcdNcmVersion =	cpu_to_le16(0x0100),
	/* can process SetEthernetPacketFilter */
	.bmNetworkCapabilities = NCAPS,
};

/* the default data interface has no endpoints ... */

static struct usb_interface_descriptor ncm_data_nop_intf = {
	.bLength =		sizeof ncm_data_nop_intf,
	.bDescriptorType =	USB_DT_INTERFACE,

	.bInterfaceNumber =	1,
	.bAlternateSetting =	0,
	.bNumEndpoints =	0,
	.bInterfaceClass =	USB_CLASS_CDC_DATA,
	.bInterfaceSubClass =	0,
	.bInterfaceProtocol =	USB_CDC_NCM_PROTO_NTB,
	/* .iInterface = DYNAMIC */
};

/* ... but the "real" data interface has two bulk endpoints */

static struct usb_interface_descriptor ncm_data_intf = {
	.bLength =		sizeof ncm_data_intf,
	.bDescriptorType =	USB_DT_INTERFACE,

	.bInterfaceNumber =	1,
	.bAlternateSetting =	1,
	.bNumEndpoints =	2,
	.bInterfaceClass =	USB_CLASS_CDC_DATA,
	.bInterfaceSubClass =	0,
	.bInterfaceProtocol =	USB_CDC_NCM_PROTO_NTB,
	/* .iInterface = DYNAMIC */
};

/* full speed support: */

static struct usb_endpoint_descriptor fs_ncm_notify_desc = {
	.bLength =		USB_DT_ENDPOINT_SIZE,
	.bDescriptorType =	USB_DT_ENDPOINT,

	.bEndpointAddress =	USB_DIR_IN,
	.bmAttributes =		USB_ENDPOINT_XFER_INT,
	.wMaxPacketSize =	cpu_to_le16(NCM_STATUS_BYTECOUNT),
	.bInterval =		NCM_STATUS_INTERVAL_MS,
};

static struct usb_endpoint_descriptor fs_ncm_in_desc = {
	.bLength =		USB_DT_ENDPOINT_SIZE,
	.bDescriptorType =	USB_DT_ENDPOINT,

	.bEndpointAddress =	USB_DIR_IN,
	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
};

static struct usb_endpoint_descriptor fs_ncm_out_desc = {
	.bLength =		USB_DT_ENDPOINT_SIZE,
	.bDescriptorType =	USB_DT_ENDPOINT,

	.bEndpointAddress =	USB_DIR_OUT,
	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
};

static struct usb_descriptor_header *ncm_fs_function[] = {
	(struct usb_descriptor_header *) &ncm_iad_desc,
	/* CDC NCM control descriptors */
	(struct usb_descriptor_header *) &ncm_control_intf,
	(struct usb_descriptor_header *) &ncm_header_desc,
	(struct usb_descriptor_header *) &ncm_union_desc,
	(struct usb_descriptor_header *) &ecm_desc,
	(struct usb_descriptor_header *) &ncm_desc,
	(struct usb_descriptor_header *) &fs_ncm_notify_desc,
	/* data interface, altsettings 0 and 1 */
	(struct usb_descriptor_header *) &ncm_data_nop_intf,
	(struct usb_descriptor_header *) &ncm_data_intf,
	(struct usb_descriptor_header *) &fs_ncm_in_desc,
	(struct usb_descriptor_header *) &fs_ncm_out_desc,
	NULL,
};

/* high speed support: */

static struct usb_endpoint_descriptor hs_ncm_notify_desc = {
	.bLength =		USB_DT_ENDPOINT_SIZE,
	.bDescriptorType =	USB_DT_ENDPOINT,

	.bEndpointAddress =	USB_DIR_IN,
	.bmAttributes =		USB_ENDPOINT_XFER_INT,
	.wMaxPacketSize =	cpu_to_le16(NCM_STATUS_BYTECOUNT),
	.bInterval =		USB_MS_TO_HS_INTERVAL(NCM_STATUS_INTERVAL_MS),
};
static struct usb_endpoint_descriptor hs_ncm_in_desc = {
	.bLength =		USB_DT_ENDPOINT_SIZE,
	.bDescriptorType =	USB_DT_ENDPOINT,

	.bEndpointAddress =	USB_DIR_IN,
	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
	.wMaxPacketSize =	cpu_to_le16(512),
};

static struct usb_endpoint_descriptor hs_ncm_out_desc = {
	.bLength =		USB_DT_ENDPOINT_SIZE,
	.bDescriptorType =	USB_DT_ENDPOINT,

	.bEndpointAddress =	USB_DIR_OUT,
	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
	.wMaxPacketSize =	cpu_to_le16(512),
};

static struct usb_descriptor_header *ncm_hs_function[] = {
	(struct usb_descriptor_header *) &ncm_iad_desc,
	/* CDC NCM control descriptors */
	(struct usb_descriptor_header *) &ncm_control_intf,
	(struct usb_descriptor_header *) &ncm_header_desc,
	(struct usb_descriptor_header *) &ncm_union_desc,
	(struct usb_descriptor_header *) &ecm_desc,
	(struct usb_descriptor_header *) &ncm_desc,
	(struct usb_descriptor_header *) &hs_ncm_notify_desc,
	/* data interface, altsettings 0 and 1 */
	(struct usb_descriptor_header *) &ncm_data_nop_intf,
	(struct usb_descriptor_header *) &ncm_data_intf,
	(struct usb_descriptor_header *) &hs_ncm_in_desc,
	(struct usb_descriptor_header *) &hs_ncm_out_desc,
	NULL,
};


/* super speed support: */

static struct usb_endpoint_descriptor ss_ncm_notify_desc = {
	.bLength =		USB_DT_ENDPOINT_SIZE,
	.bDescriptorType =	USB_DT_ENDPOINT,

	.bEndpointAddress =	USB_DIR_IN,
	.bmAttributes =		USB_ENDPOINT_XFER_INT,
	.wMaxPacketSize =	cpu_to_le16(NCM_STATUS_BYTECOUNT),
	.bInterval =		USB_MS_TO_HS_INTERVAL(NCM_STATUS_INTERVAL_MS)
};

static struct usb_ss_ep_comp_descriptor ss_ncm_notify_comp_desc = {
	.bLength =		sizeof(ss_ncm_notify_comp_desc),
	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,

	/* the following 3 values can be tweaked if necessary */
	/* .bMaxBurst =		0, */
	/* .bmAttributes =	0, */
	.wBytesPerInterval =	cpu_to_le16(NCM_STATUS_BYTECOUNT),
};

static struct usb_endpoint_descriptor ss_ncm_in_desc = {
	.bLength =		USB_DT_ENDPOINT_SIZE,
	.bDescriptorType =	USB_DT_ENDPOINT,

	.bEndpointAddress =	USB_DIR_IN,
	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
	.wMaxPacketSize =	cpu_to_le16(1024),
};

static struct usb_endpoint_descriptor ss_ncm_out_desc = {
	.bLength =		USB_DT_ENDPOINT_SIZE,
	.bDescriptorType =	USB_DT_ENDPOINT,

	.bEndpointAddress =	USB_DIR_OUT,
	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
	.wMaxPacketSize =	cpu_to_le16(1024),
};

static struct usb_ss_ep_comp_descriptor ss_ncm_bulk_comp_desc = {
	.bLength =		sizeof(ss_ncm_bulk_comp_desc),
	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,

	/* the following 2 values can be tweaked if necessary */
	.bMaxBurst =		15,
	/* .bmAttributes =	0, */
};

static struct usb_descriptor_header *ncm_ss_function[] = {
	(struct usb_descriptor_header *) &ncm_iad_desc,
	/* CDC NCM control descriptors */
	(struct usb_descriptor_header *) &ncm_control_intf,
	(struct usb_descriptor_header *) &ncm_header_desc,
	(struct usb_descriptor_header *) &ncm_union_desc,
	(struct usb_descriptor_header *) &ecm_desc,
	(struct usb_descriptor_header *) &ncm_desc,
	(struct usb_descriptor_header *) &ss_ncm_notify_desc,
	(struct usb_descriptor_header *) &ss_ncm_notify_comp_desc,
	/* data interface, altsettings 0 and 1 */
	(struct usb_descriptor_header *) &ncm_data_nop_intf,
	(struct usb_descriptor_header *) &ncm_data_intf,
	(struct usb_descriptor_header *) &ss_ncm_in_desc,
	(struct usb_descriptor_header *) &ss_ncm_bulk_comp_desc,
	(struct usb_descriptor_header *) &ss_ncm_out_desc,
	(struct usb_descriptor_header *) &ss_ncm_bulk_comp_desc,
	NULL,
};

/* string descriptors: */

#define STRING_CTRL_IDX	0
#define STRING_MAC_IDX	1
#define STRING_DATA_IDX	2
#define STRING_IAD_IDX	3

static struct usb_string ncm_string_defs[] = {
	[STRING_CTRL_IDX].s = "CDC Network Control Model (NCM)",
	[STRING_MAC_IDX].s = "",
	[STRING_DATA_IDX].s = "CDC Network Data",
	[STRING_IAD_IDX].s = "CDC NCM",
	{  } /* end of list */
};

static struct usb_gadget_strings ncm_string_table = {
	.language =		0x0409,	/* en-us */
	.strings =		ncm_string_defs,
};

static struct usb_gadget_strings *ncm_strings[] = {
	&ncm_string_table,
	NULL,
};

/*
 * Here are options for NCM Datagram Pointer table (NDP) parser.
 * There are 2 different formats: NDP16 and NDP32 in the spec (ch. 3),
 * in NDP16 offsets and sizes fields are 1 16bit word wide,
 * in NDP32 -- 2 16bit words wide. Also signatures are different.
 * To make the parser code the same, put the differences in the structure,
 * and switch pointers to the structures when the format is changed.
 */

struct ndp_parser_opts {
	u32		nth_sign;
	u32		ndp_sign;
	unsigned	nth_size;
	unsigned	ndp_size;
	unsigned	dpe_size;
	unsigned	ndplen_align;
	/* sizes in u16 units */
	unsigned	dgram_item_len; /* index or length */
	unsigned	block_length;
	unsigned	ndp_index;
	unsigned	reserved1;
	unsigned	reserved2;
	unsigned	next_ndp_index;
};

#define INIT_NDP16_OPTS {					\
		.nth_sign = USB_CDC_NCM_NTH16_SIGN,		\
		.ndp_sign = USB_CDC_NCM_NDP16_NOCRC_SIGN,	\
		.nth_size = sizeof(struct usb_cdc_ncm_nth16),	\
		.ndp_size = sizeof(struct usb_cdc_ncm_ndp16),	\
		.dpe_size = sizeof(struct usb_cdc_ncm_dpe16),	\
		.ndplen_align = 4,				\
		.dgram_item_len = 1,				\
		.block_length = 1,				\
		.ndp_index = 1,					\
		.reserved1 = 0,					\
		.reserved2 = 0,					\
		.next_ndp_index = 1,				\
	}


#define INIT_NDP32_OPTS {					\
		.nth_sign = USB_CDC_NCM_NTH32_SIGN,		\
		.ndp_sign = USB_CDC_NCM_NDP32_NOCRC_SIGN,	\
		.nth_size = sizeof(struct usb_cdc_ncm_nth32),	\
		.ndp_size = sizeof(struct usb_cdc_ncm_ndp32),	\
		.dpe_size = sizeof(struct usb_cdc_ncm_dpe32),	\
		.ndplen_align = 8,				\
		.dgram_item_len = 2,				\
		.block_length = 2,				\
		.ndp_index = 2,					\
		.reserved1 = 1,					\
		.reserved2 = 2,					\
		.next_ndp_index = 2,				\
	}

static const struct ndp_parser_opts ndp16_opts = INIT_NDP16_OPTS;
static const struct ndp_parser_opts ndp32_opts = INIT_NDP32_OPTS;

static inline void put_ncm(__le16 **p, unsigned size, unsigned val)
{
	switch (size) {
	case 1:
		put_unaligned_le16((u16)val, *p);
		break;
	case 2:
		put_unaligned_le32((u32)val, *p);

		break;
	default:
		BUG();
	}

	*p += size;
}

static inline unsigned get_ncm(__le16 **p, unsigned size)
{
	unsigned tmp;

	switch (size) {
	case 1:
		tmp = get_unaligned_le16(*p);
		break;
	case 2:
		tmp = get_unaligned_le32(*p);
		break;
	default:
		BUG();
	}

	*p += size;
	return tmp;
}

/*-------------------------------------------------------------------------*/

static inline void ncm_reset_values(struct f_ncm *ncm)
{
	ncm->parser_opts = &ndp16_opts;
	ncm->is_crc = false;
	ncm->ndp_sign = ncm->parser_opts->ndp_sign;
	ncm->port.cdc_filter = DEFAULT_FILTER;

	/* doesn't make sense for ncm, fixed size used */
	ncm->port.header_len = 0;

	ncm->port.fixed_out_len = le32_to_cpu(ntb_parameters.dwNtbOutMaxSize);
	ncm->port.fixed_in_len = NTB_DEFAULT_IN_SIZE;
}

/*
 * Context: ncm->lock held
 */
static void ncm_do_notify(struct f_ncm *ncm)
{
	struct usb_request		*req = ncm->notify_req;
	struct usb_cdc_notification	*event;
	struct usb_composite_dev	*cdev = ncm->port.func.config->cdev;
	__le32				*data;
	int				status;

	/* notification already in flight? */
	if (atomic_read(&ncm->notify_count))
		return;

	event = req->buf;
	switch (ncm->notify_state) {
	case NCM_NOTIFY_NONE:
		return;

	case NCM_NOTIFY_CONNECT:
		event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
		if (ncm->is_open)
			event->wValue = cpu_to_le16(1);
		else
			event->wValue = cpu_to_le16(0);
		event->wLength = 0;
		req->length = sizeof *event;

		DBG(cdev, "notify connect %s\n",
				ncm->is_open ? "true" : "false");
		ncm->notify_state = NCM_NOTIFY_NONE;
		break;

	case NCM_NOTIFY_SPEED:
		event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE;
		event->wValue = cpu_to_le16(0);
		event->wLength = cpu_to_le16(8);
		req->length = NCM_STATUS_BYTECOUNT;

		/* SPEED_CHANGE data is up/down speeds in bits/sec */
		data = req->buf + sizeof *event;
		data[0] = cpu_to_le32(ncm_bitrate(cdev->gadget));
		data[1] = data[0];

		DBG(cdev, "notify speed %u\n", ncm_bitrate(cdev->gadget));
		ncm->notify_state = NCM_NOTIFY_CONNECT;
		break;
	}
	event->bmRequestType = 0xA1;
	event->wIndex = cpu_to_le16(ncm->ctrl_id);

	atomic_inc(&ncm->notify_count);

	/*
	 * In double buffering if there is a space in FIFO,
	 * completion callback can be called right after the call,
	 * so unlocking
	 */
	spin_unlock(&ncm->lock);
	status = usb_ep_queue(ncm->notify, req, GFP_ATOMIC);
	spin_lock(&ncm->lock);
	if (status < 0) {
		atomic_dec(&ncm->notify_count);
		DBG(cdev, "notify --> %d\n", status);
	}
}

/*
 * Context: ncm->lock held
 */
static void ncm_notify(struct f_ncm *ncm)
{
	/*
	 * NOTE on most versions of Linux, host side cdc-ethernet
	 * won't listen for notifications until its netdevice opens.
	 * The first notification then sits in the FIFO for a long
	 * time, and the second one is queued.
	 *
	 * If ncm_notify() is called before the second (CONNECT)
	 * notification is sent, then it will reset to send the SPEED
	 * notificaion again (and again, and again), but it's not a problem
	 */
	ncm->notify_state = NCM_NOTIFY_SPEED;
	ncm_do_notify(ncm);
}

static void ncm_notify_complete(struct usb_ep *ep, struct usb_request *req)
{
	struct f_ncm			*ncm = req->context;
	struct usb_composite_dev	*cdev = ncm->port.func.config->cdev;
	struct usb_cdc_notification	*event = req->buf;

	spin_lock(&ncm->lock);
	switch (req->status) {
	case 0:
		VDBG(cdev, "Notification %02x sent\n",
		     event->bNotificationType);
		atomic_dec(&ncm->notify_count);
		break;
	case -ECONNRESET:
	case -ESHUTDOWN:
		atomic_set(&ncm->notify_count, 0);
		ncm->notify_state = NCM_NOTIFY_NONE;
		break;
	default:
		DBG(cdev, "event %02x --> %d\n",
			event->bNotificationType, req->status);
		atomic_dec(&ncm->notify_count);
		break;
	}
	ncm_do_notify(ncm);
	spin_unlock(&ncm->lock);
}

static void ncm_ep0out_complete(struct usb_ep *ep, struct usb_request *req)
{
	/* now for SET_NTB_INPUT_SIZE only */
	unsigned		in_size;
	struct usb_function	*f = req->context;
	struct f_ncm		*ncm = func_to_ncm(f);
	struct usb_composite_dev *cdev = f->config->cdev;

	req->context = NULL;
	if (req->status || req->actual != req->length) {
		DBG(cdev, "Bad control-OUT transfer\n");
		goto invalid;
	}

	in_size = get_unaligned_le32(req->buf);
	if (in_size < USB_CDC_NCM_NTB_MIN_IN_SIZE ||
	    in_size > le32_to_cpu(ntb_parameters.dwNtbInMaxSize)) {
		DBG(cdev, "Got wrong INPUT SIZE (%d) from host\n", in_size);
		goto invalid;
	}

	ncm->port.fixed_in_len = in_size;
	VDBG(cdev, "Set NTB INPUT SIZE %d\n", in_size);
	return;

invalid:
	usb_ep_set_halt(ep);
	return;
}

static int ncm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
{
	struct f_ncm		*ncm = func_to_ncm(f);
	struct usb_composite_dev *cdev = f->config->cdev;
	struct usb_request	*req = cdev->req;
	int			value = -EOPNOTSUPP;
	u16			w_index = le16_to_cpu(ctrl->wIndex);
	u16			w_value = le16_to_cpu(ctrl->wValue);
	u16			w_length = le16_to_cpu(ctrl->wLength);

	/*
	 * composite driver infrastructure handles everything except
	 * CDC class messages; interface activation uses set_alt().
	 */
	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
			| USB_CDC_SET_ETHERNET_PACKET_FILTER:
		/*
		 * see 6.2.30: no data, wIndex = interface,
		 * wValue = packet filter bitmap
		 */
		if (w_length != 0 || w_index != ncm->ctrl_id)
			goto invalid;
		DBG(cdev, "packet filter %02x\n", w_value);
		/*
		 * REVISIT locking of cdc_filter.  This assumes the UDC
		 * driver won't have a concurrent packet TX irq running on
		 * another CPU; or that if it does, this write is atomic...
		 */
		ncm->port.cdc_filter = w_value;
		value = 0;
		break;
	/*
	 * and optionally:
	 * case USB_CDC_SEND_ENCAPSULATED_COMMAND:
	 * case USB_CDC_GET_ENCAPSULATED_RESPONSE:
	 * case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS:
	 * case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER:
	 * case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER:
	 * case USB_CDC_GET_ETHERNET_STATISTIC:
	 */

	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
		| USB_CDC_GET_NTB_PARAMETERS:

		if (w_length == 0 || w_value != 0 || w_index != ncm->ctrl_id)
			goto invalid;
		value = w_length > sizeof ntb_parameters ?
			sizeof ntb_parameters : w_length;
		memcpy(req->buf, &ntb_parameters, value);
		VDBG(cdev, "Host asked NTB parameters\n");
		break;

	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
		| USB_CDC_GET_NTB_INPUT_SIZE:

		if (w_length < 4 || w_value != 0 || w_index != ncm->ctrl_id)
			goto invalid;
		put_unaligned_le32(ncm->port.fixed_in_len, req->buf);
		value = 4;
		VDBG(cdev, "Host asked INPUT SIZE, sending %d\n",
		     ncm->port.fixed_in_len);
		break;

	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
		| USB_CDC_SET_NTB_INPUT_SIZE:
	{
		if (w_length != 4 || w_value != 0 || w_index != ncm->ctrl_id)
			goto invalid;
		req->complete = ncm_ep0out_complete;
		req->length = w_length;
		req->context = f;

		value = req->length;
		break;
	}

	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
		| USB_CDC_GET_NTB_FORMAT:
	{
		uint16_t format;

		if (w_length < 2 || w_value != 0 || w_index != ncm->ctrl_id)
			goto invalid;
		format = (ncm->parser_opts == &ndp16_opts) ? 0x0000 : 0x0001;
		put_unaligned_le16(format, req->buf);
		value = 2;
		VDBG(cdev, "Host asked NTB FORMAT, sending %d\n", format);
		break;
	}

	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
		| USB_CDC_SET_NTB_FORMAT:
	{
		if (w_length != 0 || w_index != ncm->ctrl_id)
			goto invalid;
		switch (w_value) {
		case 0x0000:
			ncm->parser_opts = &ndp16_opts;
			DBG(cdev, "NCM16 selected\n");
			break;
		case 0x0001:
			ncm->parser_opts = &ndp32_opts;
			DBG(cdev, "NCM32 selected\n");
			break;
		default:
			goto invalid;
		}
		value = 0;
		break;
	}
	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
		| USB_CDC_GET_CRC_MODE:
	{
		uint16_t is_crc;

		if (w_length < 2 || w_value != 0 || w_index != ncm->ctrl_id)
			goto invalid;
		is_crc = ncm->is_crc ? 0x0001 : 0x0000;
		put_unaligned_le16(is_crc, req->buf);
		value = 2;
		VDBG(cdev, "Host asked CRC MODE, sending %d\n", is_crc);
		break;
	}

	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
		| USB_CDC_SET_CRC_MODE:
	{
		if (w_length != 0 || w_index != ncm->ctrl_id)
			goto invalid;
		switch (w_value) {
		case 0x0000:
			ncm->is_crc = false;
			DBG(cdev, "non-CRC mode selected\n");
			break;
		case 0x0001:
			ncm->is_crc = true;
			DBG(cdev, "CRC mode selected\n");
			break;
		default:
			goto invalid;
		}
		value = 0;
		break;
	}

	/* and disabled in ncm descriptor: */
	/* case USB_CDC_GET_NET_ADDRESS: */
	/* case USB_CDC_SET_NET_ADDRESS: */
	/* case USB_CDC_GET_MAX_DATAGRAM_SIZE: */
	/* case USB_CDC_SET_MAX_DATAGRAM_SIZE: */

	default:
invalid:
		DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
			ctrl->bRequestType, ctrl->bRequest,
			w_value, w_index, w_length);
	}
	ncm->ndp_sign = ncm->parser_opts->ndp_sign |
		(ncm->is_crc ? NCM_NDP_HDR_CRC : 0);

	/* respond with data transfer or status phase? */
	if (value >= 0) {
		DBG(cdev, "ncm req%02x.%02x v%04x i%04x l%d\n",
			ctrl->bRequestType, ctrl->bRequest,
			w_value, w_index, w_length);
		req->zero = 0;
		req->length = value;
		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
		if (value < 0)
			ERROR(cdev, "ncm req %02x.%02x response err %d\n",
					ctrl->bRequestType, ctrl->bRequest,
					value);
	}

	/* device either stalls (value < 0) or reports success */
	return value;
}


static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
	struct f_ncm		*ncm = func_to_ncm(f);
	struct usb_composite_dev *cdev = f->config->cdev;

	/* Control interface has only altsetting 0 */
	if (intf == ncm->ctrl_id) {
		if (alt != 0)
			goto fail;

		DBG(cdev, "reset ncm control %d\n", intf);
		usb_ep_disable(ncm->notify);

		if (!(ncm->notify->desc)) {
			DBG(cdev, "init ncm ctrl %d\n", intf);
			if (config_ep_by_speed(cdev->gadget, f, ncm->notify))
				goto fail;
		}
		usb_ep_enable(ncm->notify);

	/* Data interface has two altsettings, 0 and 1 */
	} else if (intf == ncm->data_id) {
		if (alt > 1)
			goto fail;

		if (ncm->port.in_ep->enabled) {
			DBG(cdev, "reset ncm\n");
			ncm->netdev = NULL;
			gether_disconnect(&ncm->port);
			ncm_reset_values(ncm);
		}

		/*
		 * CDC Network only sends data in non-default altsettings.
		 * Changing altsettings resets filters, statistics, etc.
		 */
		if (alt == 1) {
			struct net_device	*net;

			if (!ncm->port.in_ep->desc ||
			    !ncm->port.out_ep->desc) {
				DBG(cdev, "init ncm\n");
				if (config_ep_by_speed(cdev->gadget, f,
						       ncm->port.in_ep) ||
				    config_ep_by_speed(cdev->gadget, f,
						       ncm->port.out_ep)) {
					ncm->port.in_ep->desc = NULL;
					ncm->port.out_ep->desc = NULL;
					goto fail;
				}
			}

			/* TODO */
			/* Enable zlps by default for NCM conformance;
			 * override for musb_hdrc (avoids txdma ovhead)
			 */
			ncm->port.is_zlp_ok =
				gadget_is_zlp_supported(cdev->gadget);
			ncm->port.cdc_filter = DEFAULT_FILTER;
			DBG(cdev, "activate ncm\n");
			net = gether_connect(&ncm->port);
			if (IS_ERR(net))
				return PTR_ERR(net);
			ncm->netdev = net;
		}

		spin_lock(&ncm->lock);
		ncm_notify(ncm);
		spin_unlock(&ncm->lock);
	} else
		goto fail;

	return 0;
fail:
	return -EINVAL;
}

/*
 * Because the data interface supports multiple altsettings,
 * this NCM function *MUST* implement a get_alt() method.
 */
static int ncm_get_alt(struct usb_function *f, unsigned intf)
{
	struct f_ncm		*ncm = func_to_ncm(f);

	if (intf == ncm->ctrl_id)
		return 0;
	return ncm->port.in_ep->enabled ? 1 : 0;
}

static struct sk_buff *package_for_tx(struct f_ncm *ncm)
{
	__le16		*ntb_iter;
	struct sk_buff	*skb2 = NULL;
	unsigned	ndp_pad;
	unsigned	ndp_index;
	unsigned	new_len;

	const struct ndp_parser_opts *opts = ncm->parser_opts;
	const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment);
	const int dgram_idx_len = 2 * 2 * opts->dgram_item_len;

	/* Stop the timer */
	hrtimer_try_to_cancel(&ncm->task_timer);

	ndp_pad = ALIGN(ncm->skb_tx_data->len, ndp_align) -
			ncm->skb_tx_data->len;
	ndp_index = ncm->skb_tx_data->len + ndp_pad;
	new_len = ndp_index + dgram_idx_len + ncm->skb_tx_ndp->len;

	/* Set the final BlockLength and wNdpIndex */
	ntb_iter = (void *) ncm->skb_tx_data->data;
	/* Increment pointer to BlockLength */
	ntb_iter += 2 + 1 + 1;
	put_ncm(&ntb_iter, opts->block_length, new_len);
	put_ncm(&ntb_iter, opts->ndp_index, ndp_index);

	/* Set the final NDP wLength */
	new_len = opts->ndp_size +
			(ncm->ndp_dgram_count * dgram_idx_len);
	ncm->ndp_dgram_count = 0;
	/* Increment from start to wLength */
	ntb_iter = (void *) ncm->skb_tx_ndp->data;
	ntb_iter += 2;
	put_unaligned_le16(new_len, ntb_iter);

	/* Merge the skbs */
	swap(skb2, ncm->skb_tx_data);
	if (ncm->skb_tx_data) {
		dev_consume_skb_any(ncm->skb_tx_data);
		ncm->skb_tx_data = NULL;
	}

	/* Insert NDP alignment. */
	skb_put_zero(skb2, ndp_pad);

	/* Copy NTB across. */
	skb_put_data(skb2, ncm->skb_tx_ndp->data, ncm->skb_tx_ndp->len);
	dev_consume_skb_any(ncm->skb_tx_ndp);
	ncm->skb_tx_ndp = NULL;

	/* Insert zero'd datagram. */
	skb_put_zero(skb2, dgram_idx_len);

	return skb2;
}

static struct sk_buff *ncm_wrap_ntb(struct gether *port,
				    struct sk_buff *skb)
{
	struct f_ncm	*ncm = func_to_ncm(&port->func);
	struct sk_buff	*skb2 = NULL;

	if (skb) {
		int		ncb_len = 0;
		__le16		*ntb_data;
		__le16		*ntb_ndp;
		int		dgram_pad;

		unsigned	max_size = ncm->port.fixed_in_len;
		const struct ndp_parser_opts *opts = ncm->parser_opts;
		const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment);
		const int div = le16_to_cpu(ntb_parameters.wNdpInDivisor);
		const int rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder);
		const int dgram_idx_len = 2 * 2 * opts->dgram_item_len;

		/* Add the CRC if required up front */
		if (ncm->is_crc) {
			uint32_t	crc;
			__le16		*crc_pos;

			crc = ~crc32_le(~0,
					skb->data,
					skb->len);
			crc_pos = skb_put(skb, sizeof(uint32_t));
			put_unaligned_le32(crc, crc_pos);
		}

		/* If the new skb is too big for the current NCM NTB then
		 * set the current stored skb to be sent now and clear it
		 * ready for new data.
		 * NOTE: Assume maximum align for speed of calculation.
		 */
		if (ncm->skb_tx_data
		    && (ncm->ndp_dgram_count >= TX_MAX_NUM_DPE
		    || (ncm->skb_tx_data->len +
		    div + rem + skb->len +
		    ncm->skb_tx_ndp->len + ndp_align + (2 * dgram_idx_len))
		    > max_size)) {
			skb2 = package_for_tx(ncm);
			if (!skb2)
				goto err;
		}

		if (!ncm->skb_tx_data) {
			ncb_len = opts->nth_size;
			dgram_pad = ALIGN(ncb_len, div) + rem - ncb_len;
			ncb_len += dgram_pad;

			/* Create a new skb for the NTH and datagrams. */
			ncm->skb_tx_data = alloc_skb(max_size, GFP_ATOMIC);
			if (!ncm->skb_tx_data)
				goto err;

			ncm->skb_tx_data->dev = ncm->netdev;
			ntb_data = skb_put_zero(ncm->skb_tx_data, ncb_len);
			/* dwSignature */
			put_unaligned_le32(opts->nth_sign, ntb_data);
			ntb_data += 2;
			/* wHeaderLength */
			put_unaligned_le16(opts->nth_size, ntb_data++);

			/* Allocate an skb for storing the NDP,
			 * TX_MAX_NUM_DPE should easily suffice for a
			 * 16k packet.
			 */
			ncm->skb_tx_ndp = alloc_skb((int)(opts->ndp_size
						    + opts->dpe_size
						    * TX_MAX_NUM_DPE),
						    GFP_ATOMIC);
			if (!ncm->skb_tx_ndp)
				goto err;

			ncm->skb_tx_ndp->dev = ncm->netdev;
			ntb_ndp = skb_put(ncm->skb_tx_ndp, opts->ndp_size);
			memset(ntb_ndp, 0, ncb_len);
			/* dwSignature */
			put_unaligned_le32(ncm->ndp_sign, ntb_ndp);
			ntb_ndp += 2;

			/* There is always a zeroed entry */
			ncm->ndp_dgram_count = 1;

			/* Note: we skip opts->next_ndp_index */

			/* Start the timer. */
			hrtimer_start(&ncm->task_timer, TX_TIMEOUT_NSECS,
				      HRTIMER_MODE_REL_SOFT);
		}

		/* Add the datagram position entries */
		ntb_ndp = skb_put_zero(ncm->skb_tx_ndp, dgram_idx_len);

		ncb_len = ncm->skb_tx_data->len;
		dgram_pad = ALIGN(ncb_len, div) + rem - ncb_len;
		ncb_len += dgram_pad;

		/* (d)wDatagramIndex */
		put_ncm(&ntb_ndp, opts->dgram_item_len, ncb_len);
		/* (d)wDatagramLength */
		put_ncm(&ntb_ndp, opts->dgram_item_len, skb->len);
		ncm->ndp_dgram_count++;

		/* Add the new data to the skb */
		skb_put_zero(ncm->skb_tx_data, dgram_pad);
		skb_put_data(ncm->skb_tx_data, skb->data, skb->len);
		dev_consume_skb_any(skb);
		skb = NULL;

	} else if (ncm->skb_tx_data) {
		/* If we get here ncm_wrap_ntb() was called with NULL skb,
		 * because eth_start_xmit() was called with NULL skb by
		 * ncm_tx_timeout() - hence, this is our signal to flush/send.
		 */
		skb2 = package_for_tx(ncm);
		if (!skb2)
			goto err;
	}

	return skb2;

err:
	ncm->netdev->stats.tx_dropped++;

	if (skb)
		dev_kfree_skb_any(skb);
	if (ncm->skb_tx_data)
		dev_kfree_skb_any(ncm->skb_tx_data);
	if (ncm->skb_tx_ndp)
		dev_kfree_skb_any(ncm->skb_tx_ndp);

	return NULL;
}

/*
 * The transmit should only be run if no skb data has been sent
 * for a certain duration.
 */
static enum hrtimer_restart ncm_tx_timeout(struct hrtimer *data)
{
	struct f_ncm *ncm = container_of(data, struct f_ncm, task_timer);
	struct net_device *netdev = READ_ONCE(ncm->netdev);

	if (netdev) {
		/* XXX This allowance of a NULL skb argument to ndo_start_xmit
		 * XXX is not sane.  The gadget layer should be redesigned so
		 * XXX that the dev->wrap() invocations to build SKBs is transparent
		 * XXX and performed in some way outside of the ndo_start_xmit
		 * XXX interface.
		 *
		 * This will call directly into u_ether's eth_start_xmit()
		 */
		netdev->netdev_ops->ndo_start_xmit(NULL, netdev);
	}
	return HRTIMER_NORESTART;
}

static int ncm_unwrap_ntb(struct gether *port,
			  struct sk_buff *skb,
			  struct sk_buff_head *list)
{
	struct f_ncm	*ncm = func_to_ncm(&port->func);
	unsigned char	*ntb_ptr = skb->data;
	__le16		*tmp;
	unsigned	index, index2;
	int		ndp_index;
	unsigned	dg_len, dg_len2;
	unsigned	ndp_len;
	unsigned	block_len;
	struct sk_buff	*skb2;
	int		ret = -EINVAL;
	unsigned	ntb_max = le32_to_cpu(ntb_parameters.dwNtbOutMaxSize);
	unsigned	frame_max = le16_to_cpu(ecm_desc.wMaxSegmentSize);
	const struct ndp_parser_opts *opts = ncm->parser_opts;
	unsigned	crc_len = ncm->is_crc ? sizeof(uint32_t) : 0;
	int		dgram_counter;
	int		to_process = skb->len;

parse_ntb:
	tmp = (__le16 *)ntb_ptr;

	/* dwSignature */
	if (get_unaligned_le32(tmp) != opts->nth_sign) {
		INFO(port->func.config->cdev, "Wrong NTH SIGN, skblen %d\n",
			skb->len);
		print_hex_dump(KERN_INFO, "HEAD:", DUMP_PREFIX_ADDRESS, 32, 1,
			       skb->data, 32, false);

		goto err;
	}
	tmp += 2;
	/* wHeaderLength */
	if (get_unaligned_le16(tmp++) != opts->nth_size) {
		INFO(port->func.config->cdev, "Wrong NTB headersize\n");
		goto err;
	}
	tmp++; /* skip wSequence */

	block_len = get_ncm(&tmp, opts->block_length);
	/* (d)wBlockLength */
	if (block_len > ntb_max) {
		INFO(port->func.config->cdev, "OUT size exceeded\n");
		goto err;
	}

	ndp_index = get_ncm(&tmp, opts->ndp_index);

	/* Run through all the NDP's in the NTB */
	do {
		/*
		 * NCM 3.2
		 * dwNdpIndex
		 */
		if (((ndp_index % 4) != 0) ||
				(ndp_index < opts->nth_size) ||
				(ndp_index > (block_len -
					      opts->ndp_size))) {
			INFO(port->func.config->cdev, "Bad index: %#X\n",
			     ndp_index);
			goto err;
		}

		/*
		 * walk through NDP
		 * dwSignature
		 */
		tmp = (__le16 *)(ntb_ptr + ndp_index);
		if (get_unaligned_le32(tmp) != ncm->ndp_sign) {
			INFO(port->func.config->cdev, "Wrong NDP SIGN\n");
			goto err;
		}
		tmp += 2;

		ndp_len = get_unaligned_le16(tmp++);
		/*
		 * NCM 3.3.1
		 * wLength
		 * entry is 2 items
		 * item size is 16/32 bits, opts->dgram_item_len * 2 bytes
		 * minimal: struct usb_cdc_ncm_ndpX + normal entry + zero entry
		 * Each entry is a dgram index and a dgram length.
		 */
		if ((ndp_len < opts->ndp_size
				+ 2 * 2 * (opts->dgram_item_len * 2)) ||
				(ndp_len % opts->ndplen_align != 0)) {
			INFO(port->func.config->cdev, "Bad NDP length: %#X\n",
			     ndp_len);
			goto err;
		}
		tmp += opts->reserved1;
		/* Check for another NDP (d)wNextNdpIndex */
		ndp_index = get_ncm(&tmp, opts->next_ndp_index);
		tmp += opts->reserved2;

		ndp_len -= opts->ndp_size;
		index2 = get_ncm(&tmp, opts->dgram_item_len);
		dg_len2 = get_ncm(&tmp, opts->dgram_item_len);
		dgram_counter = 0;

		do {
			index = index2;
			/* wDatagramIndex[0] */
			if ((index < opts->nth_size) ||
					(index > block_len - opts->dpe_size)) {
				INFO(port->func.config->cdev,
				     "Bad index: %#X\n", index);
				goto err;
			}

			dg_len = dg_len2;
			/*
			 * wDatagramLength[0]
			 * ethernet hdr + crc or larger than max frame size
			 */
			if ((dg_len < 14 + crc_len) ||
					(dg_len > frame_max)) {
				INFO(port->func.config->cdev,
				     "Bad dgram length: %#X\n", dg_len);
				goto err;
			}
			if (ncm->is_crc) {
				uint32_t crc, crc2;

				crc = get_unaligned_le32(ntb_ptr +
							 index + dg_len -
							 crc_len);
				crc2 = ~crc32_le(~0,
						 ntb_ptr + index,
						 dg_len - crc_len);
				if (crc != crc2) {
					INFO(port->func.config->cdev,
					     "Bad CRC\n");
					goto err;
				}
			}

			index2 = get_ncm(&tmp, opts->dgram_item_len);
			dg_len2 = get_ncm(&tmp, opts->dgram_item_len);

			/* wDatagramIndex[1] */
			if (index2 > block_len - opts->dpe_size) {
				INFO(port->func.config->cdev,
				     "Bad index: %#X\n", index2);
				goto err;
			}

			/*
			 * Copy the data into a new skb.
			 * This ensures the truesize is correct
			 */
			skb2 = netdev_alloc_skb_ip_align(ncm->netdev,
							 dg_len - crc_len);
			if (skb2 == NULL)
				goto err;
			skb_put_data(skb2, ntb_ptr + index,
				     dg_len - crc_len);

			skb_queue_tail(list, skb2);

			ndp_len -= 2 * (opts->dgram_item_len * 2);

			dgram_counter++;
			if (index2 == 0 || dg_len2 == 0)
				break;
		} while (ndp_len > 2 * (opts->dgram_item_len * 2));
	} while (ndp_index);

	VDBG(port->func.config->cdev,
	     "Parsed NTB with %d frames\n", dgram_counter);

	to_process -= block_len;

	/*
	 * Windows NCM driver avoids USB ZLPs by adding a 1-byte
	 * zero pad as needed.
	 */
	if (to_process == 1 &&
	    (*(unsigned char *)(ntb_ptr + block_len) == 0x00)) {
		to_process--;
	} else if ((to_process > 0) && (block_len != 0)) {
		ntb_ptr = (unsigned char *)(ntb_ptr + block_len);
		goto parse_ntb;
	}

	dev_consume_skb_any(skb);

	return 0;
err:
	skb_queue_purge(list);
	dev_kfree_skb_any(skb);
	return ret;
}

static void ncm_disable(struct usb_function *f)
{
	struct f_ncm		*ncm = func_to_ncm(f);
	struct usb_composite_dev *cdev = f->config->cdev;

	DBG(cdev, "ncm deactivated\n");

	if (ncm->port.in_ep->enabled) {
		ncm->netdev = NULL;
		gether_disconnect(&ncm->port);
	}

	if (ncm->notify->enabled) {
		usb_ep_disable(ncm->notify);
		ncm->notify->desc = NULL;
	}
}

/*-------------------------------------------------------------------------*/

/*
 * Callbacks let us notify the host about connect/disconnect when the
 * net device is opened or closed.
 *
 * For testing, note that link states on this side include both opened
 * and closed variants of:
 *
 *   - disconnected/unconfigured
 *   - configured but inactive (data alt 0)
 *   - configured and active (data alt 1)
 *
 * Each needs to be tested with unplug, rmmod, SET_CONFIGURATION, and
 * SET_INTERFACE (altsetting).  Remember also that "configured" doesn't
 * imply the host is actually polling the notification endpoint, and
 * likewise that "active" doesn't imply it's actually using the data
 * endpoints for traffic.
 */

static void ncm_open(struct gether *geth)
{
	struct f_ncm		*ncm = func_to_ncm(&geth->func);

	DBG(ncm->port.func.config->cdev, "%s\n", __func__);

	spin_lock(&ncm->lock);
	ncm->is_open = true;
	ncm_notify(ncm);
	spin_unlock(&ncm->lock);
}

static void ncm_close(struct gether *geth)
{
	struct f_ncm		*ncm = func_to_ncm(&geth->func);

	DBG(ncm->port.func.config->cdev, "%s\n", __func__);

	spin_lock(&ncm->lock);
	ncm->is_open = false;
	ncm_notify(ncm);
	spin_unlock(&ncm->lock);
}

/*-------------------------------------------------------------------------*/

/* ethernet function driver setup/binding */

static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
{
	struct usb_composite_dev *cdev = c->cdev;
	struct f_ncm		*ncm = func_to_ncm(f);
	struct usb_string	*us;
	int			status = 0;
	struct usb_ep		*ep;
	struct f_ncm_opts	*ncm_opts;

	if (!can_support_ecm(cdev->gadget))
		return -EINVAL;

	ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst);

	if (cdev->use_os_string) {
		f->os_desc_table = kzalloc(sizeof(*f->os_desc_table),
					   GFP_KERNEL);
		if (!f->os_desc_table)
			return -ENOMEM;
		f->os_desc_n = 1;
		f->os_desc_table[0].os_desc = &ncm_opts->ncm_os_desc;
	}

	mutex_lock(&ncm_opts->lock);
	gether_set_gadget(ncm_opts->net, cdev->gadget);
	if (!ncm_opts->bound)
		status = gether_register_netdev(ncm_opts->net);
	mutex_unlock(&ncm_opts->lock);

	if (status)
		goto fail;

	ncm_opts->bound = true;

	us = usb_gstrings_attach(cdev, ncm_strings,
				 ARRAY_SIZE(ncm_string_defs));
	if (IS_ERR(us)) {
		status = PTR_ERR(us);
		goto fail;
	}
	ncm_control_intf.iInterface = us[STRING_CTRL_IDX].id;
	ncm_data_nop_intf.iInterface = us[STRING_DATA_IDX].id;
	ncm_data_intf.iInterface = us[STRING_DATA_IDX].id;
	ecm_desc.iMACAddress = us[STRING_MAC_IDX].id;
	ncm_iad_desc.iFunction = us[STRING_IAD_IDX].id;

	/* allocate instance-specific interface IDs */
	status = usb_interface_id(c, f);
	if (status < 0)
		goto fail;
	ncm->ctrl_id = status;
	ncm_iad_desc.bFirstInterface = status;

	ncm_control_intf.bInterfaceNumber = status;
	ncm_union_desc.bMasterInterface0 = status;

	if (cdev->use_os_string)
		f->os_desc_table[0].if_id =
			ncm_iad_desc.bFirstInterface;

	status = usb_interface_id(c, f);
	if (status < 0)
		goto fail;
	ncm->data_id = status;

	ncm_data_nop_intf.bInterfaceNumber = status;
	ncm_data_intf.bInterfaceNumber = status;
	ncm_union_desc.bSlaveInterface0 = status;

	status = -ENODEV;

	/* allocate instance-specific endpoints */
	ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_in_desc);
	if (!ep)
		goto fail;
	ncm->port.in_ep = ep;

	ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_out_desc);
	if (!ep)
		goto fail;
	ncm->port.out_ep = ep;

	ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_notify_desc);
	if (!ep)
		goto fail;
	ncm->notify = ep;

	status = -ENOMEM;

	/* allocate notification request and buffer */
	ncm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
	if (!ncm->notify_req)
		goto fail;
	ncm->notify_req->buf = kmalloc(NCM_STATUS_BYTECOUNT, GFP_KERNEL);
	if (!ncm->notify_req->buf)
		goto fail;
	ncm->notify_req->context = ncm;
	ncm->notify_req->complete = ncm_notify_complete;

	/*
	 * support all relevant hardware speeds... we expect that when
	 * hardware is dual speed, all bulk-capable endpoints work at
	 * both speeds
	 */
	hs_ncm_in_desc.bEndpointAddress = fs_ncm_in_desc.bEndpointAddress;
	hs_ncm_out_desc.bEndpointAddress = fs_ncm_out_desc.bEndpointAddress;
	hs_ncm_notify_desc.bEndpointAddress =
		fs_ncm_notify_desc.bEndpointAddress;

	ss_ncm_in_desc.bEndpointAddress = fs_ncm_in_desc.bEndpointAddress;
	ss_ncm_out_desc.bEndpointAddress = fs_ncm_out_desc.bEndpointAddress;
	ss_ncm_notify_desc.bEndpointAddress =
		fs_ncm_notify_desc.bEndpointAddress;

	status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function,
			ncm_ss_function, ncm_ss_function);
	if (status)
		goto fail;

	/*
	 * NOTE:  all that is done without knowing or caring about
	 * the network link ... which is unavailable to this code
	 * until we're activated via set_alt().
	 */

	ncm->port.open = ncm_open;
	ncm->port.close = ncm_close;

	hrtimer_init(&ncm->task_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
	ncm->task_timer.function = ncm_tx_timeout;

	DBG(cdev, "CDC Network: %s speed IN/%s OUT/%s NOTIFY/%s\n",
			gadget_is_superspeed(c->cdev->gadget) ? "super" :
			gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
			ncm->port.in_ep->name, ncm->port.out_ep->name,
			ncm->notify->name);
	return 0;

fail:
	kfree(f->os_desc_table);
	f->os_desc_n = 0;

	if (ncm->notify_req) {
		kfree(ncm->notify_req->buf);
		usb_ep_free_request(ncm->notify, ncm->notify_req);
	}

	ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);

	return status;
}

static inline struct f_ncm_opts *to_f_ncm_opts(struct config_item *item)
{
	return container_of(to_config_group(item), struct f_ncm_opts,
			    func_inst.group);
}

/* f_ncm_item_ops */
USB_ETHERNET_CONFIGFS_ITEM(ncm);

/* f_ncm_opts_dev_addr */
USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ncm);

/* f_ncm_opts_host_addr */
USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ncm);

/* f_ncm_opts_qmult */
USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ncm);

/* f_ncm_opts_ifname */
USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ncm);

static struct configfs_attribute *ncm_attrs[] = {
	&ncm_opts_attr_dev_addr,
	&ncm_opts_attr_host_addr,
	&ncm_opts_attr_qmult,
	&ncm_opts_attr_ifname,
	NULL,
};

static const struct config_item_type ncm_func_type = {
	.ct_item_ops	= &ncm_item_ops,
	.ct_attrs	= ncm_attrs,
	.ct_owner	= THIS_MODULE,
};

static void ncm_free_inst(struct usb_function_instance *f)
{
	struct f_ncm_opts *opts;

	opts = container_of(f, struct f_ncm_opts, func_inst);
	if (opts->bound)
		gether_cleanup(netdev_priv(opts->net));
	else
		free_netdev(opts->net);
	kfree(opts->ncm_interf_group);
	kfree(opts);
}

static struct usb_function_instance *ncm_alloc_inst(void)
{
	struct f_ncm_opts *opts;
	struct usb_os_desc *descs[1];
	char *names[1];
	struct config_group *ncm_interf_group;

	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
	if (!opts)
		return ERR_PTR(-ENOMEM);
	opts->ncm_os_desc.ext_compat_id = opts->ncm_ext_compat_id;

	mutex_init(&opts->lock);
	opts->func_inst.free_func_inst = ncm_free_inst;
	opts->net = gether_setup_default();
	if (IS_ERR(opts->net)) {
		struct net_device *net = opts->net;
		kfree(opts);
		return ERR_CAST(net);
	}
	INIT_LIST_HEAD(&opts->ncm_os_desc.ext_prop);

	descs[0] = &opts->ncm_os_desc;
	names[0] = "ncm";

	config_group_init_type_name(&opts->func_inst.group, "", &ncm_func_type);
	ncm_interf_group =
		usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs,
					       names, THIS_MODULE);
	if (IS_ERR(ncm_interf_group)) {
		ncm_free_inst(&opts->func_inst);
		return ERR_CAST(ncm_interf_group);
	}
	opts->ncm_interf_group = ncm_interf_group;

	return &opts->func_inst;
}

static void ncm_free(struct usb_function *f)
{
	struct f_ncm *ncm;
	struct f_ncm_opts *opts;

	ncm = func_to_ncm(f);
	opts = container_of(f->fi, struct f_ncm_opts, func_inst);
	kfree(ncm);
	mutex_lock(&opts->lock);
	opts->refcnt--;
	mutex_unlock(&opts->lock);
}

static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
{
	struct f_ncm *ncm = func_to_ncm(f);

	DBG(c->cdev, "ncm unbind\n");

	hrtimer_cancel(&ncm->task_timer);

	kfree(f->os_desc_table);
	f->os_desc_n = 0;

	ncm_string_defs[0].id = 0;
	usb_free_all_descriptors(f);

	if (atomic_read(&ncm->notify_count)) {
		usb_ep_dequeue(ncm->notify, ncm->notify_req);
		atomic_set(&ncm->notify_count, 0);
	}

	kfree(ncm->notify_req->buf);
	usb_ep_free_request(ncm->notify, ncm->notify_req);
}

static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
{
	struct f_ncm		*ncm;
	struct f_ncm_opts	*opts;
	int status;

	/* allocate and initialize one new instance */
	ncm = kzalloc(sizeof(*ncm), GFP_KERNEL);
	if (!ncm)
		return ERR_PTR(-ENOMEM);

	opts = container_of(fi, struct f_ncm_opts, func_inst);
	mutex_lock(&opts->lock);
	opts->refcnt++;

	/* export host's Ethernet address in CDC format */
	status = gether_get_host_addr_cdc(opts->net, ncm->ethaddr,
				      sizeof(ncm->ethaddr));
	if (status < 12) { /* strlen("01234567890a") */
		kfree(ncm);
		mutex_unlock(&opts->lock);
		return ERR_PTR(-EINVAL);
	}
	ncm_string_defs[STRING_MAC_IDX].s = ncm->ethaddr;

	spin_lock_init(&ncm->lock);
	ncm_reset_values(ncm);
	ncm->port.ioport = netdev_priv(opts->net);
	mutex_unlock(&opts->lock);
	ncm->port.is_fixed = true;
	ncm->port.supports_multi_frame = true;

	ncm->port.func.name = "cdc_network";
	/* descriptors are per-instance copies */
	ncm->port.func.bind = ncm_bind;
	ncm->port.func.unbind = ncm_unbind;
	ncm->port.func.set_alt = ncm_set_alt;
	ncm->port.func.get_alt = ncm_get_alt;
	ncm->port.func.setup = ncm_setup;
	ncm->port.func.disable = ncm_disable;
	ncm->port.func.free_func = ncm_free;

	ncm->port.wrap = ncm_wrap_ntb;
	ncm->port.unwrap = ncm_unwrap_ntb;

	return &ncm->port.func;
}

DECLARE_USB_FUNCTION_INIT(ncm, ncm_alloc_inst, ncm_alloc);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yauheni Kaliuta");
, 1056, 655, 1408, 806 }, /* 800x600 */ { 0, 0, 0, 0, 0, 0 }, /* 1024x768 */ { 0, 0, 0, 0, 0, 0 }, /* 1280x1024 */ { 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0 }, { 1, 1, 1408, 806, 1408, 806 } /* 1280x720 */ }; /* About 1280x768: For TMDS, Panel_1280x768 will only be set if * the panel is a Fujitsu 7911 (VL-17WDX8) (with clock 81, 1688x802) * Other TMDS panels of this resolution will be treated as custom. * For LVDS, we know another type (_2). * (Note: 1280x768_3 is now special for SiS301/NetVista */ static const struct SiS_LCDData SiS_StLCD1280x768_2Data[] = /* 2.03.00 */ { { 64, 21, 858, 434, 1408, 806 }, /* 640x400 */ { 32, 9, 858, 372, 1408, 806 }, { 64, 21, 858, 434, 1408, 806 }, { 32, 9, 858, 372, 1408, 806 }, { 143, 68, 1024, 527, 1408, 806 }, /* 640x480 */ { 64, 51, 1364, 663, 1408, 806 }, /* 800x600 */ { 88, 81, 1296, 806, 1408, 806 }, /* 1024x768 */ { 0, 0, 0, 0, 0, 0 }, { 1, 1, 1408, 806, 1408, 806 }, /* 1280x768 */ { 0, 0, 0, 0, 0, 0 }, { 16, 15, 1600, 750, 1600, 806 } /* 1280x720 - from Ext */ }; static const struct SiS_LCDData SiS_ExtLCD1280x768_2Data[] = /* 2.03.00 */ { { 16, 5, 960, 410, 1600, 806 }, /* 640x400 */ { 64, 21, 1152, 364, 1600, 806 }, { 16, 5, 960, 410, 1600, 806 }, { 64, 21, 1152, 364, 1600, 806 }, { 32, 13, 1040, 493, 1600, 806 }, /* 640x480 */ { 16, 9, 1152, 618, 1600, 806 }, /* 800x600 */ { 25, 21, 1344, 796, 1600, 806 }, /* 1024x768 */ { 0, 0, 0, 0, 0, 0 }, { 1, 1, 1600, 806, 1600, 806 }, /* 1280x768 */ { 0, 0, 0, 0, 0, 0 }, { 16, 15, 1600, 750, 1600, 806 } /* 1280x720 */ }; #if 0 /* Not used; _3 now reserved for NetVista (SiS301) */ static const struct SiS_LCDData SiS_LCD1280x768_3Data[] = { { 64, 25, 1056, 422, 1664, 798 }, /* 640x400 */ { 128, 39, 884, 396, 1408, 806 }, /* ,640 */ { 64, 25, 1056, 422, 1664, 798 }, /* 640x400 */ { 128, 39, 884, 396, 1408, 806 }, /* ,640 */ { 32, 15, 1056, 513, 1408, 806 }, /* ,664 */ /* 640x480 */ { 176, 125, 1280, 640, 1408, 806 }, /* ,768 */ /* 800x600 */ { 64, 61, 1342, 806, 1408, 806 }, /* 1024x768 */ { 0, 0, 0, 0, 0, 0 }, { 1, 1, 1408, 806, 1408, 806 }, /* 1280x768 */ { 0, 0, 0, 0, 0, 0 }, { 16, 15, 1600, 750, 1600, 806 } /* 1280x720 from above */ }; #endif static const struct SiS_LCDData SiS_LCD1280x800Data[] = /* 0.93.12a (TMDS) */ { { 128, 51, 1122, 412, 1408, 816 }, /* 640x400 */ { 128, 49, 1232, 361, 1408, 816 }, { 128, 51, 1122, 412, 1408, 816 }, { 128, 49, 1232, 361, 1408, 816 }, { 8, 3, 880, 491, 1408, 816 }, /* 640x480 */ { 11, 6, 1024, 612, 1408, 816 }, /* 800x600 */ { 22, 21, 1400, 784, 1408, 816 }, /* 1024x768 */ { 0, 0, 0, 0, 0, 0 }, /* 1280x1024 */ { 1, 1, 1408, 816, 1408, 816 }, /* 1280x800 */ { 0, 0, 0, 0, 0, 0 }, /* 1280x768 (patch index) */ { 0, 0, 0, 0, 0, 0 } /* 1280x720 */ }; static const struct SiS_LCDData SiS_LCD1280x800_2Data[] = /* 2.03.00 (LVDS) */ { { 97, 42, 1344, 409, 1552, 812 }, /* 640x400 */ { 97, 35, 1280, 358, 1552, 812 }, { 97, 42, 1344, 409, 1552, 812 }, { 97, 35, 1280, 358, 1552, 812 }, { 97, 39, 1040, 488, 1552, 812 }, /* 640x480 */ { 194, 105, 1120, 608, 1552, 812 }, /* 800x600 */ { 97, 84, 1400, 780, 1552, 812 }, /* 1024x768 */ { 0, 0, 0, 0, 0, 0 }, /* 1280x1024 */ { 1, 1, 1552, 812, 1552, 812 }, /* 1280x800 */ { 97, 96, 1600, 780, 1552, 812 }, /* 1280x768 - patch index */ { 97, 90, 1600, 730, 1552, 812 } /* 1280x720 */ }; #if 0 static const struct SiS_LCDData SiS_LCD1280x800_3Data[] = /* 2.02.05a (LVDS); m250 */ { { 128, 51, 1122, 412, 1408, 816 }, /* 640x400 */ { 128, 49, 1232, 361, 1408, 816 }, { 128, 51, 1122, 412, 1408, 816 }, { 128, 49, 1232, 361, 1408, 816 }, { 8, 3, 880, 491, 1408, 816 }, /* 640x480 */ { 11, 6, 1024, 612, 1408, 816 }, /* 800x600 */ { 22, 21, 1400, 784, 1408, 816 }, /* 1024x768 */ { 0, 0, 0, 0, 0, 0 }, /* 1280x1024 */ { 1, 1, 1408, 816, 1408, 816 }, /* 1280x800 */ { 0, 0, 0, 0, 0, 0 }, /* 1280x768 - patch index */ { 0, 0, 0, 0, 0, 0 } /* 1280x720 */ }; #endif static const struct SiS_LCDData SiS_LCD1280x854Data[] = /* 2.21.00CS (LVDS) */ { { 56, 15, 936, 410, 1664, 861 }, /* 640x400 */ { 64, 25, 1586, 355, 1664, 861 }, { 56, 15, 936, 410, 1664, 861 }, { 64, 25, 1586, 355, 1664, 861 }, { 91, 45, 1464, 485, 1664, 861 }, /* 640x480 */ { 182, 75, 976, 605, 1664, 861 }, /* 800x600 */ { 91, 66, 1342, 774, 1664, 861 }, /* 1024x768 */ { 0, 0, 0, 0, 0, 0 }, /* 1280x1024 */ { 26, 25, 1708, 807, 1664, 861 }, /* 1280x800 */ { 13, 12, 1708, 774, 1664, 861 }, /* 1280x768 - patch index */ { 52, 45, 1708, 725, 1664, 861 }, /* 1280x720 */ { 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0 }, { 1, 1, 1664, 861, 1664, 861 } /* 1280x854 */ }; static const struct SiS_LCDData SiS_LCD1280x960Data[] = { { 9, 2, 800, 500, 1800, 1000 }, { 9, 2, 800, 500, 1800, 1000 }, { 4, 1, 900, 500, 1800, 1000 }, { 4, 1, 900, 500, 1800, 1000 }, { 9, 2, 800, 500, 1800, 1000 }, { 30, 11, 1056, 625, 1800, 1000 }, { 5, 3, 1350, 800, 1800, 1000 }, { 1, 1, 1576, 1050, 1576, 1050 }, { 1, 1, 1800, 1000, 1800, 1000 }, { 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0 } }; static const struct SiS_LCDData SiS_StLCD1400x1050Data[] = { { 211, 100, 2100, 408, 1688, 1066 }, { 211, 64, 1536, 358, 1688, 1066 }, { 211, 100, 2100, 408, 1688, 1066 }, { 211, 64, 1536, 358, 1688, 1066 }, { 211, 48, 840, 488, 1688, 1066 }, { 211, 72, 1008, 609, 1688, 1066 }, { 211, 128, 1400, 776, 1688, 1066 }, { 211, 205, 1680, 1041, 1688, 1066 }, { 1, 1, 1688, 1066, 1688, 1066 }, { 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0 } }; static const struct SiS_LCDData SiS_ExtLCD1400x1050Data[] = { /* { 211, 60, 1260, 410, 1688, 1066 }, 640x400 (6330) */ { 211, 100, 2100, 408, 1688, 1066 }, /* 640x400 (6325) WORKS */ { 211, 64, 1536, 358, 1688, 1066 }, { 211, 100, 2100, 408, 1688, 1066 }, { 211, 64, 1536, 358, 1688, 1066 }, /* { 211, 80, 1400, 490, 1688, 1066 }, 640x480 (6330) */ { 211, 48, 840, 488, 1688, 1066 }, /* 640x480 (6325) WORKS */ /* { 211, 117, 1638, 613, 1688, 1066 }, 800x600 (6330) */ { 211, 72, 1008, 609, 1688, 1066 }, /* 800x600 (6325) WORKS */ { 211, 128, 1400, 776, 1688, 1066 }, /* 1024x768 */ { 211, 205, 1680, 1041, 1688, 1066 }, /* 1280x1024 - not used (always unscaled) */ { 1, 1, 1688, 1066, 1688, 1066 }, /* 1400x1050 */ { 0, 0, 0, 0, 0, 0 }, /* kludge */ { 211, 120, 1400, 730, 1688, 1066 }, /* 1280x720 */ { 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0 } }; static const struct SiS_LCDData SiS_LCD1680x1050Data[] = { { 95, 24, 1260, 410, 1900, 1066 }, /* 0 640x400 */ { 10, 3, 1710, 362, 1900, 1066 }, { 95, 24, 1260, 410, 1900, 1066 }, { 10, 3, 1710, 362, 1900, 1066 }, { 95, 32, 1400, 490, 1900, 1066 }, /* 4 640x480 */ { 95, 42, 1470, 610, 1900, 1066 }, /* 5 800x600 */ { 95, 64, 1750, 784, 1900, 1066 }, /* 6 1024x768 */ { 95, 94, 1900, 1055, 1900, 1066 }, /* 7 1280x1024 */ { 41, 31, 1900, 806, 1900, 1066 }, /* 8 1280x768 */ { 95, 69, 1800, 817, 1900, 1066 }, /* 9 1280x800 patch index */ { 13, 9, 1900, 739, 1900, 1066 }, /* 10 1280x720 */ { 95, 94, 1880, 1066, 1900, 1066 }, /* 11 1400x1050 patch index */ { 1, 1, 1900, 1066, 1900, 1066 }, /* 12 1680x1050 */ { 0, 0, 0, 0, 0, 0 } }; static const struct SiS_LCDData SiS_StLCD1600x1200Data[] = { {27, 4, 800, 500, 2160, 1250 }, {27, 4, 800, 500, 2160, 1250 }, { 6, 1, 900, 500, 2160, 1250 }, { 6, 1, 900, 500, 2160, 1250 }, {27, 1, 800, 500, 2160, 1250 }, { 4, 1,1080, 625, 2160, 1250 }, { 5, 2,1350, 800, 2160, 1250 }, {135,88,1600,1100, 2160, 1250 }, {72, 49,1680,1092, 2160, 1250 }, { 1, 1,2160,1250, 2160, 1250 }, { 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0 } }; static const struct SiS_LCDData SiS_ExtLCD1600x1200Data[] = { {72,11, 990, 422, 2160, 1250 }, /* 640x400 (6330) WORKS */ /* {27, 4, 800, 500, 2160, 1250 }, 640x400 (6235) */ {27, 4, 800, 500, 2160, 1250 }, { 6, 1, 900, 500, 2160, 1250 }, { 6, 1, 900, 500, 2160, 1250 }, {45, 8, 960, 505, 2160, 1250 }, /* 640x480 (6330) WORKS */ /* {27, 1, 800, 500, 2160, 1250 }, 640x480 (6325) */ { 4, 1,1080, 625, 2160, 1250 }, { 5, 2,1350, 800, 2160, 1250 }, {27,16,1500,1064, 2160, 1250 }, /* 1280x1024 */ {72,49,1680,1092, 2160, 1250 }, /* 1400x1050 (6330, was not supported on 6325) */ { 1, 1,2160,1250, 2160, 1250 }, { 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0 } }; static const struct SiS_LCDData SiS_NoScaleData[] = { { 1, 1, 800, 449, 800, 449 }, /* 0x00: 320x200, 640x400 */ { 1, 1, 800, 449, 800, 449 }, { 1, 1, 900, 449, 900, 449 }, { 1, 1, 900, 449, 900, 449 }, { 1, 1, 800, 525, 800, 525 }, /* 0x04: 320x240, 640x480 */ { 1, 1,1056, 628,1056, 628 }, /* 0x05: 400x300, 800x600 */ { 1, 1,1344, 806,1344, 806 }, /* 0x06: 512x384, 1024x768 */ { 1, 1,1688,1066,1688,1066 }, /* 0x07: 1280x1024 */ { 1, 1,1688, 802,1688, 802 }, /* 0x08: 1280x768: Fujitsu, TMDS only */ { 1, 1,2160,1250,2160,1250 }, /* 0x09: 1600x1200 */ { 1, 1,1800,1000,1800,1000 }, /* 0x0a: 1280x960 */ { 1, 1,1688,1066,1688,1066 }, /* 0x0b: 1400x1050 */ { 1, 1,1650, 750,1650, 750 }, /* 0x0c: 1280x720 (TMDS, projector) */ { 1, 1,1552, 812,1552, 812 }, /* 0x0d: 1280x800_2 (LVDS) (was: 1408,816/ 1656,841) */ { 1, 1,1900,1066,1900,1066 }, /* 0x0e: 1680x1050 (LVDS) */ { 1, 1,1660, 806,1660, 806 }, /* 0x0f: 1280x768_2 (LVDS) */ { 1, 1,1664, 798,1664, 798 }, /* 0x10: 1280x768_3 (NetVista SiS 301) - TODO */ { 1, 1,1688, 802,1688, 802 }, /* 0x11: 1280x768 (TMDS Fujitsu) */ { 1, 1,1408, 806,1408, 806 }, /* 0x12: 1280x720 (LVDS) */ { 1, 1, 896, 497, 896, 497 }, /* 0x13: 720x480 */ { 1, 1, 912, 597, 912, 597 }, /* 0x14: 720x576 */ { 1, 1, 912, 597, 912, 597 }, /* 0x15: 768x576 */ { 1, 1,1056, 497,1056, 497 }, /* 0x16: 848x480 */ { 1, 1,1064, 497,1064, 497 }, /* 0x17: 856x480 */ { 1, 1,1056, 497,1056, 497 }, /* 0x18: 800x480 */ { 1, 1,1328, 739,1328, 739 }, /* 0x19: 1024x576 */ { 1, 1,1680, 892,1680, 892 }, /* 0x1a: 1152x864 */ { 1, 1,1808, 808,1808, 808 }, /* 0x1b: 1360x768 */ { 1, 1,1104, 563,1104, 563 }, /* 0x1c: 960x540 */ { 1, 1,1120, 618,1120, 618 }, /* 0x1d: 960x600 */ { 1, 1,1408, 816,1408, 816 }, /* 0x1f: 1280x800 (TMDS special) */ { 1, 1,1760,1235,1760,1235 }, /* 0x20: 1600x1200 for LCDA */ { 1, 1,2048,1320,2048,1320 }, /* 0x21: 1600x1200 for non-SiS LVDS */ { 1, 1,1664, 861,1664, 861 } /* 0x22: 1280x854 */ }; /**************************************************************/ /* LVDS ----------------------------------------------------- */ /**************************************************************/ /* FSTN/DSTN 320x240, 2 variants */ static const struct SiS_LVDSData SiS_LVDS320x240Data_1[]= { { 848, 433, 400, 525}, { 848, 389, 400, 525}, { 848, 433, 400, 525}, { 848, 389, 400, 525}, { 848, 518, 400, 525}, {1056, 628, 400, 525}, { 400, 525, 400, 525} /* xSTN */ }; static const struct SiS_LVDSData SiS_LVDS320x240Data_2[]= { { 800, 445, 800, 525}, { 800, 395, 800, 525}, { 800, 445, 800, 525}, { 800, 395, 800, 525}, { 800, 525, 800, 525}, {1056, 628,1056, 628}, { 480, 525, 480, 525} /* xSTN */ }; static const struct SiS_LVDSData SiS_LVDS640x480Data_1[]= { { 800, 445, 800, 525}, /* 800, 449, 800, 449 */ { 800, 395, 800, 525}, { 800, 445, 800, 525}, { 800, 395, 800, 525}, { 800, 525, 800, 525} }; static const struct SiS_LVDSData SiS_LVDS800x600Data_1[]= { { 848, 433,1060, 629}, { 848, 389,1060, 629}, { 848, 433,1060, 629}, { 848, 389,1060, 629}, { 848, 518,1060, 629}, {1056, 628,1056, 628} }; static const struct SiS_LVDSData SiS_LVDS1024x600Data_1[] = { { 840, 604,1344, 800}, { 840, 560,1344, 800}, { 840, 604,1344, 800}, { 840, 560,1344, 800}, { 840, 689,1344, 800}, {1050, 800,1344, 800}, {1344, 800,1344, 800} }; static const struct SiS_LVDSData SiS_LVDS1024x768Data_1[]= { { 840, 438,1344, 806}, { 840, 409,1344, 806}, { 840, 438,1344, 806}, { 840, 409,1344, 806}, { 840, 518,1344, 806}, /* 640x480 */ {1050, 638,1344, 806}, /* 800x600 */ {1344, 806,1344, 806}, /* 1024x768 */ }; static const struct SiS_LVDSData SiS_CHTVUNTSCData[]= { { 840, 600, 840, 600}, { 840, 600, 840, 600}, { 840, 600, 840, 600}, { 840, 600, 840, 600}, { 784, 600, 784, 600}, {1064, 750,1064, 750}, {1160, 945,1160, 945} }; static const struct SiS_LVDSData SiS_CHTVONTSCData[]= { { 840, 525, 840, 525}, { 840, 525, 840, 525}, { 840, 525, 840, 525}, { 840, 525, 840, 525}, { 784, 525, 784, 525}, {1040, 700,1040, 700}, {1160, 840,1160, 840} }; /* CRT1 CRTC data for slave modes */ static const struct SiS_LVDSCRT1Data SiS_LVDSCRT1320x240_1[] = { {{0x65,0x4f,0x89,0x56,0x83,0xaa,0x1f, 0x90,0x85,0x8f,0xab,0x30,0x00,0x05, 0x00 }}, {{0x65,0x4f,0x89,0x56,0x83,0x83,0x1f, 0x5e,0x83,0x5d,0x79,0x10,0x00,0x05, 0x00 }}, {{0x65,0x4f,0x89,0x54,0x9f,0xc4,0x1f, 0x92,0x89,0x8f,0xb5,0x30,0x00,0x01, 0x00 }}, {{0x65,0x4f,0x89,0x56,0x83,0x83,0x1f, 0x5e,0x83,0x5d,0x79,0x10,0x00,0x05, 0x00 }}, {{0x65,0x4f,0x89,0x56,0x83,0x04,0x3e, 0xe0,0x85,0xdf,0xfb,0x10,0x00,0x05, 0x00 }}, {{0x7f,0x63,0x83,0x6c,0x1c,0x72,0xf0, 0x58,0x8c,0x57,0x73,0x20,0x00,0x06, 0x01 }}, {{0x2d,0x27,0x90,0x2c,0x80,0x0b,0x3e, 0xe9,0x8b,0xe7,0x04,0x00,0x00,0x00, 0x00 }} }; static const struct SiS_LVDSCRT1Data SiS_LVDSCRT1320x240_2[] = { {{0x5f,0x4f,0x82,0x55,0x81,0x0b,0x3e, 0xe9,0x8b,0xdf,0x04,0x30,0x00,0x05, 0x00}}, {{0x5f,0x4f,0x82,0x55,0x81,0x0b,0x3e, 0xe9,0x8b,0xdf,0x04,0x30,0x00,0x05, 0x00}}, {{0x5f,0x4f,0x82,0x55,0x81,0x0b,0x3e, 0xe9,0x8b,0xdf,0x04,0x30,0x00,0x05, 0x00}}, {{0x5f,0x4f,0x82,0x55,0x81,0x0b,0x3e, 0xe9,0x8b,0xdf,0x04,0x30,0x00,0x05, 0x00}}, {{0x5f,0x4f,0x82,0x55,0x81,0x0b,0x3e, 0xe9,0x8b,0xdf,0x04,0x30,0x00,0x05, 0x00}}, {{0x7f,0x63,0x83,0x6c,0x1c,0x72,0xf0, 0x58,0x8c,0x57,0x73,0x20,0x00,0x06, 0x01}}, #if 0 {{0x2d,0x27,0x90,0x2c,0x80,0x0b,0x3e, 0xe9,0x8b,0xe7,0x04,0x00,0x00,0x00, 0x00}} #endif {{0x5f,0x4f,0x83,0x55,0x81,0x0b,0x3e, 0xe9,0x8b,0xe8,0x0c,0x00,0x00,0x05, 0x00}}, }; static const struct SiS_LVDSCRT1Data SiS_LVDSCRT1320x240_2_H[] = { {{0x65,0x4f,0x89,0x56,0x83,0xaa,0x1f, 0x90,0x85,0x8f,0xab,0x30,0x00,0x05, 0x00}}, {{0x65,0x4f,0x89,0x56,0x83,0x83,0x1f, 0x5e,0x83,0x5d,0x79,0x10,0x00,0x05, 0x00}}, {{0x65,0x4f,0x89,0x54,0x9f,0xc4,0x1f, 0x92,0x89,0x8f,0xb5,0x30,0x00,0x01, 0x00}}, {{0x65,0x4f,0x89,0x56,0x83,0x83,0x1f, 0x5e,0x83,0x5d,0x79,0x10,0x00,0x05, 0x00}}, {{0x65,0x4f,0x89,0x56,0x83,0x04,0x3e, 0xe0,0x85,0xdf,0xfb,0x10,0x00,0x05, 0x00}}, {{0x7f,0x63,0x83,0x6c,0x1c,0x72,0xf0, 0x58,0x8c,0x57,0x73,0x20,0x00,0x06, 0x01}}, {{0x2d,0x27,0x90,0x2c,0x80,0x0b,0x3e, 0xe9,0x8b,0xe7,0x04,0x00,0x00,0x00, 0x00}} }; static const struct SiS_LVDSCRT1Data SiS_LVDSCRT1320x240_3[] = { {{0x5f,0x4f,0x82,0x55,0x81,0x0b,0x3e, 0xe9,0x8b,0xdf,0x04,0x00,0x00,0x05, 0x00}}, {{0x5f,0x4f,0x82,0x55,0x81,0x0b,0x3e, 0xe9,0x8b,0xdf,0x04,0x00,0x00,0x05, 0x00}}, {{0x5f,0x4f,0x82,0x55,0x81,0x0b,0x3e, 0xe9,0x8b,0xdf,0x04,0x00,0x00,0x05, 0x00}}, {{0x5f,0x4f,0x82,0x55,0x81,0x0b,0x3e, 0xe9,0x8b,0xdf,0x04,0x00,0x00,0x05, 0x00}}, {{0x5f,0x4f,0x82,0x55,0x81,0x0b,0x3e, 0xe9,0x8b,0xdf,0x04,0x00,0x00,0x05, 0x00}}, {{0x7f,0x63,0x83,0x6c,0x1c,0x72,0xf0, 0x58,0x8c,0x57,0x73,0x20,0x00,0x06, 0x01}}, {{0x2d,0x27,0x90,0x2c,0x80,0x0b,0x3e, 0xe9,0x8b,0xe7,0x04,0x00,0x00,0x00, 0x00}} }; static const struct SiS_LVDSCRT1Data SiS_LVDSCRT1320x240_3_H[] = { {{0x65,0x4f,0x89,0x56,0x83,0xaa,0x1f, 0x90,0x85,0x8f,0xab,0x30,0x00,0x05, 0x00}}, {{0x65,0x4f,0x89,0x56,0x83,0x83,0x1f, 0x5e,0x83,0x5d,0x79,0x10,0x00,0x05, 0x00}}, {{0x65,0x4f,0x89,0x54,0x9f,0xc4,0x1f, 0x92,0x89,0x8f,0xb5,0x30,0x00,0x01, 0x00}}, {{0x65,0x4f,0x89,0x56,0x83,0x83,0x1f, 0x5e,0x83,0x5d,0x79,0x10,0x00,0x05, 0x00}}, {{0x65,0x4f,0x89,0x56,0x83,0x04,0x3e, 0xe0,0x85,0xdf,0xfb,0x10,0x00,0x05, 0x00}}, {{0x7f,0x63,0x83,0x6c,0x1c,0x72,0xf0, 0x58,0x8c,0x57,0x73,0x20,0x00,0x06, 0x01}}, {{0x2d,0x27,0x90,0x2c,0x80,0x0b,0x3e, 0xe9,0x8b,0xe7,0x04,0x00,0x00,0x00, 0x00}} }; static const struct SiS_LVDSCRT1Data SiS_LVDSCRT1640x480_1[] = { {{0x5f,0x4f,0x82,0x55,0x81,0x0b,0x3e, 0xe9,0x8b,0xdf,0x04,0x30,0x00,0x05, 0x00}}, {{0x5f,0x4f,0x82,0x55,0x81,0x0b,0x3e, 0xe9,0x8b,0xdf,0x04,0x30,0x00,0x05, 0x00}}, {{0x5f,0x4f,0x82,0x55,0x81,0x0b,0x3e, 0xe9,0x8b,0xdf,0x04,0x30,0x00,0x05, 0x00}}, {{0x5f,0x4f,0x82,0x55,0x81,0x0b,0x3e, 0xe9,0x8b,0xdf,0x04,0x30,0x00,0x05, 0x00}}, {{0x5f,0x4f,0x82,0x55,0x81,0x0b,0x3e, 0xe9,0x8b,0xdf,0x04,0x30,0x00,0x05, 0x00}}, {{0x7f,0x63,0x83,0x6c,0x1c,0x72,0xf0, 0x58,0x8c,0x57,0x73,0x20,0x00,0x06, 0x01}} }; static const struct SiS_LVDSCRT1Data SiS_LVDSCRT1640x480_1_H[] = { {{0x2d,0x28,0x90,0x2b,0xa0,0xbf,0x1f, 0x9c,0x8e,0x96,0xb9,0x00,0x00,0x00, 0x00}}, {{0x2d,0x28,0x90,0x2b,0xa0,0xbf,0x1f, 0x83,0x85,0x63,0xba,0x00,0x00,0x00, 0x00}}, {{0x2d,0x28,0x90,0x2b,0xa0,0xbf,0x1f, 0x9c,0x8e,0x96,0xb9,0x00,0x00,0x00, 0x00}}, {{0x2d,0x28,0x90,0x2b,0xa0,0xbf,0x1f, 0x83,0x85,0x63,0xba,0x00,0x00,0x00, 0x00}}, {{0x2d,0x28,0x90,0x2c,0x80,0x0b,0x3e, 0xe9,0x8b,0xe7,0x04,0x00,0x00,0x00, 0x00}} }; bool SiSInitPtr(struct SiS_Private *SiS_Pr); #ifdef SIS_XORG_XF86 unsigned short SiS_GetModeID(int VGAEngine, unsigned int VBFlags, int HDisplay, int VDisplay, int Depth, bool FSTN, int LCDwith, int LCDheight); #endif unsigned short SiS_GetModeID_LCD(int VGAEngine, unsigned int VBFlags, int HDisplay, int VDisplay, int Depth, bool FSTN, unsigned short CustomT, int LCDwith, int LCDheight, unsigned int VBFlags2); unsigned short SiS_GetModeID_TV(int VGAEngine, unsigned int VBFlags, int HDisplay, int VDisplay, int Depth, unsigned int VBFlags2); unsigned short SiS_GetModeID_VGA2(int VGAEngine, unsigned int VBFlags, int HDisplay, int VDisplay, int Depth, unsigned int VBFlags2); void SiS_SetReg(SISIOADDRESS port, unsigned short index, unsigned short data); void SiS_SetRegByte(SISIOADDRESS port, unsigned short data); void SiS_SetRegShort(SISIOADDRESS port, unsigned short data); void SiS_SetRegLong(SISIOADDRESS port, unsigned int data); unsigned char SiS_GetReg(SISIOADDRESS port, unsigned short index); unsigned char SiS_GetRegByte(SISIOADDRESS port); unsigned short SiS_GetRegShort(SISIOADDRESS port); unsigned int SiS_GetRegLong(SISIOADDRESS port); void SiS_SetRegANDOR(SISIOADDRESS Port, unsigned short Index, unsigned short DataAND, unsigned short DataOR); void SiS_SetRegAND(SISIOADDRESS Port,unsigned short Index, unsigned short DataAND); void SiS_SetRegOR(SISIOADDRESS Port,unsigned short Index, unsigned short DataOR); void SiS_DisplayOn(struct SiS_Private *SiS_Pr); void SiS_DisplayOff(struct SiS_Private *SiS_Pr); void SiSRegInit(struct SiS_Private *SiS_Pr, SISIOADDRESS BaseAddr); #ifndef SIS_LINUX_KERNEL void SiSSetLVDSetc(struct SiS_Private *SiS_Pr); #endif void SiS_SetEnableDstn(struct SiS_Private *SiS_Pr, int enable); void SiS_SetEnableFstn(struct SiS_Private *SiS_Pr, int enable); unsigned short SiS_GetModeFlag(struct SiS_Private *SiS_Pr, unsigned short ModeNo, unsigned short ModeIdIndex); bool SiSDetermineROMLayout661(struct SiS_Private *SiS_Pr); #ifndef SIS_LINUX_KERNEL void SiS_GetVBType(struct SiS_Private *SiS_Pr); #endif bool SiS_SearchModeID(struct SiS_Private *SiS_Pr, unsigned short *ModeNo, unsigned short *ModeIdIndex); unsigned short SiS_GetModePtr(struct SiS_Private *SiS_Pr, unsigned short ModeNo, unsigned short ModeIdIndex); unsigned short SiS_GetRefCRTVCLK(struct SiS_Private *SiS_Pr, unsigned short Index, int UseWide); unsigned short SiS_GetRefCRT1CRTC(struct SiS_Private *SiS_Pr, unsigned short Index, int UseWide); unsigned short SiS_GetColorDepth(struct SiS_Private *SiS_Pr, unsigned short ModeNo, unsigned short ModeIdIndex); unsigned short SiS_GetOffset(struct SiS_Private *SiS_Pr,unsigned short ModeNo, unsigned short ModeIdIndex, unsigned short RRTI); #ifdef SIS300 void SiS_GetFIFOThresholdIndex300(struct SiS_Private *SiS_Pr, unsigned short *idx1, unsigned short *idx2); unsigned short SiS_GetFIFOThresholdB300(unsigned short idx1, unsigned short idx2); unsigned short SiS_GetLatencyFactor630(struct SiS_Private *SiS_Pr, unsigned short index); #endif void SiS_LoadDAC(struct SiS_Private *SiS_Pr, unsigned short ModeNo, unsigned short ModeIdIndex); #ifdef SIS_XORG_XF86 bool SiSSetMode(struct SiS_Private *SiS_Pr, ScrnInfoPtr pScrn, unsigned short ModeNo, bool dosetpitch); bool SiSBIOSSetMode(struct SiS_Private *SiS_Pr, ScrnInfoPtr pScrn, DisplayModePtr mode, bool IsCustom); bool SiSBIOSSetModeCRT2(struct SiS_Private *SiS_Pr, ScrnInfoPtr pScrn, DisplayModePtr mode, bool IsCustom); bool SiSBIOSSetModeCRT1(struct SiS_Private *SiS_Pr, ScrnInfoPtr pScrn, DisplayModePtr mode, bool IsCustom); #endif #ifdef SIS_LINUX_KERNEL bool SiSSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo); #endif void SiS_CalcCRRegisters(struct SiS_Private *SiS_Pr, int depth); void SiS_CalcLCDACRT1Timing(struct SiS_Private *SiS_Pr, unsigned short ModeNo, unsigned short ModeIdIndex); #ifdef SIS_XORG_XF86 void SiS_Generic_ConvertCRData(struct SiS_Private *SiS_Pr, unsigned char *crdata, int xres, int yres, DisplayModePtr current); #endif #ifdef SIS_LINUX_KERNEL void SiS_Generic_ConvertCRData(struct SiS_Private *SiS_Pr, unsigned char *crdata, int xres, int yres, struct fb_var_screeninfo *var, bool writeres); #endif /* From init301.c: */ extern void SiS_GetVBInfo(struct SiS_Private *SiS_Pr, unsigned short ModeNo, unsigned short ModeIdIndex, int chkcrt2mode); extern void SiS_GetLCDResInfo(struct SiS_Private *SiS_Pr, unsigned short ModeNo, unsigned short ModeIdIndex); extern void SiS_SetYPbPr(struct SiS_Private *SiS_Pr); extern void SiS_SetTVMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo, unsigned short ModeIdIndex); extern void SiS_UnLockCRT2(struct SiS_Private *SiS_Pr); extern void SiS_DisableBridge(struct SiS_Private *); extern bool SiS_SetCRT2Group(struct SiS_Private *, unsigned short); extern unsigned short SiS_GetRatePtr(struct SiS_Private *SiS_Pr, unsigned short ModeNo, unsigned short ModeIdIndex); extern void SiS_WaitRetrace1(struct SiS_Private *SiS_Pr); extern unsigned short SiS_GetResInfo(struct SiS_Private *SiS_Pr, unsigned short ModeNo, unsigned short ModeIdIndex); extern unsigned short SiS_GetCH700x(struct SiS_Private *SiS_Pr, unsigned short tempax); extern unsigned short SiS_GetVCLK2Ptr(struct SiS_Private *SiS_Pr, unsigned short ModeNo, unsigned short ModeIdIndex, unsigned short RRTI); extern bool SiS_IsVAMode(struct SiS_Private *); extern bool SiS_IsDualEdge(struct SiS_Private *); #ifdef SIS_XORG_XF86 /* From other modules: */ extern unsigned short SiS_CheckBuildCustomMode(ScrnInfoPtr pScrn, DisplayModePtr mode, unsigned int VBFlags); extern unsigned char SiS_GetSetBIOSScratch(ScrnInfoPtr pScrn, unsigned short offset, unsigned char value); extern unsigned char SiS_GetSetModeID(ScrnInfoPtr pScrn, unsigned char id); extern unsigned short SiS_GetModeNumber(ScrnInfoPtr pScrn, DisplayModePtr mode, unsigned int VBFlags); #endif #ifdef SIS_LINUX_KERNEL #ifdef SIS300 extern unsigned int sisfb_read_nbridge_pci_dword(struct SiS_Private *SiS_Pr, int reg); extern void sisfb_write_nbridge_pci_dword(struct SiS_Private *SiS_Pr, int reg, unsigned int val); #endif #ifdef SIS315H extern void sisfb_write_nbridge_pci_byte(struct SiS_Private *SiS_Pr, int reg, unsigned char val); extern unsigned int sisfb_read_mio_pci_word(struct SiS_Private *SiS_Pr, int reg); #endif #endif #endif