S: United Kingdom
N: Ron Holt
-E: ron@sovereign.org
+E: ron@holt.org
+E: rholt@netcom.com
W: http://www.holt.org/
W: http://www.ronholt.com/
D: Kernel development
FIFO. See Documentation/parport.txt to find out how to specify
which IRQ/DMA to use.
+SuperIO chipset support (EXPERIMENTAL)
+CONFIG_PARPORT_PC_SUPERIO
+ Saying Y here enables some probes for Super-IO chipsets in order to
+ find out things like base addresses, IRQ lines and DMA channels. It
+ is safe to say N.
+
Support for PCMCIA management for PC-style ports
CONFIG_PARPORT_PC_PCMCIA
Say Y here if you need PCMCIA support for your PC-style parallel
module, say M here and read Documentation/modules.txt and
Documentation/scsi.txt .
-Extra SCSI Tapes
-CONFIG_ST_EXTRA_DEVS
- This controls the amount of additional space allocated in tables for
- drivers that are loaded as modules after the kernel is booted. In the
- event that the SCSI core itself was loaded as a module, this this value
- is the number of additional tape devices that can be loaded after the
- first host driver is loaded.
-
- Admittedly this isn't pretty, but there are tons of race conditions
- involved with resizing the internal arrays on the fly. Someday this
- flag will go away, and everything will work automatically.
-
SCSI CDROM support
CONFIG_BLK_DEV_SR
If you want to use a SCSI CDROM under Linux, say Y and read the
clean:
rm -f core *~
- rm -r$BOOKS
+ rm -r $(BOOKS)
include $(TOPDIR)/Rules.make
--- /dev/null
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter
+100.00
+Single
+-2
+1200 2
+6 1425 4350 5175 5475
+6 3450 5100 4425 5475
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 4425 5475 4425 5100 3450 5100 3450 5475 4425 5475
+4 0 0 50 0 0 12 0.0000 4 135 510 3600 5400 Printer\001
+-6
+6 3375 4350 5175 4725
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 5175 4725 5175 4350 3375 4350 3375 4725 5175 4725
+4 0 0 50 0 0 12 0.0000 4 180 870 3825 4650 Multiplexor\001
+-6
+6 1425 4650 2775 5475
+6 1425 4650 2775 5475
+2 4 0 1 0 7 50 0 -1 0.000 0 0 6 0 0 5
+ 2757 5475 2757 4650 1425 4650 1425 5475 2757 5475
+4 0 0 50 0 0 12 0.0000 4 180 735 1725 5100 Computer\001
+-6
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 2775 4875 2700 4875 2700 5025 2775 5025 2775 4875
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 2775 5175 2700 5175 2700 5325 2775 5325 2775 5175
+-6
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+ 2775 4950 3600 4725
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+ 2775 5250 3450 5325
+-6
+6 3150 2625 4125 3525
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 4125 3075 4125 2625 3150 2625 3150 3075 4125 3075
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+ 3675 3075 3675 3525
+4 0 0 50 0 0 12 0.0000 4 135 510 3300 2925 Printer\001
+-6
+6 4275 3450 5250 4350
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 5250 3900 5250 3450 4275 3450 4275 3900 5250 3900
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+ 4800 3900 4800 4350
+4 0 0 50 0 0 12 0.0000 4 135 510 4425 3750 Printer\001
+-6
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 3900 4050 3900 3525 3375 3525 3375 4050 3900 4050
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+ 3675 4050 3675 4350
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 3600 4350 3750 4350 3750 4425 3600 4425 3600 4350
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 4725 4350 4875 4350 4875 4425 4725 4425 4725 4350
+4 0 0 50 0 0 12 0.0000 4 135 285 3450 3900 ZIP\001
--- /dev/null
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter
+100.00
+Single
+-2
+1200 2
+0 32 #8e8e8e
+0 33 #8e8e8e
+0 34 #aeaaae
+0 35 #515551
+0 36 #414141
+0 37 #868286
+0 38 #8e8e8e
+0 39 #414141
+0 40 #868286
+0 41 #c7c3c7
+0 42 #e7e3e7
+0 43 #414141
+0 44 #868286
+0 45 #c7c3c7
+0 46 #e7e3e7
+0 47 #868286
+0 48 #c7c3c7
+0 49 #e7e3e7
+6 1200 3000 2250 4950
+6 1275 3150 2175 3675
+6 1312 3487 1837 3637
+2 1 0 1 40 -1 19 0 -1 0.000 2 0 -1 0 0 5
+ 1312 3562 1312 3524 1474 3524 1474 3487 1675 3487
+2 1 0 1 40 -1 19 0 -1 0.000 2 0 -1 0 0 3
+ 1474 3637 1474 3562 1675 3562
+2 1 0 1 40 -1 19 0 -1 0.000 2 0 -1 0 0 2
+ 1675 3524 1837 3524
+2 1 0 1 7 -1 19 0 -1 0.000 2 0 -1 0 0 2
+ 1675 3487 1675 3524
+2 1 0 1 7 -1 19 0 -1 0.000 2 0 -1 0 0 2
+ 1312 3562 1474 3562
+2 1 0 1 7 -1 19 0 -1 0.000 2 0 -1 0 0 5
+ 1474 3637 1675 3637 1675 3562 1837 3562 1837 3524
+2 1 0 1 40 -1 19 0 -1 0.000 2 0 -1 0 0 3
+ 1716 3637 1797 3637 1797 3600
+2 1 0 1 7 -1 19 0 -1 0.000 2 0 -1 0 0 3
+ 1716 3637 1716 3600 1797 3600
+-6
+6 1413 3345 2070 3397
+6 1994 3352 2070 3390
+2 1 0 1 7 40 19 0 -1 0.000 2 0 -1 0 0 3
+ 1994 3390 1994 3352 2070 3352
+2 1 0 1 40 -1 19 0 -1 0.000 2 0 -1 0 0 3
+ 1994 3390 2070 3390 2070 3352
+-6
+6 1531 3353 1643 3389
+2 2 0 0 40 41 19 0 20 0.000 2 0 -1 0 0 5
+ 1568 3353 1606 3353 1606 3389 1568 3389 1568 3353
+2 2 0 0 40 39 19 0 20 0.000 2 0 -1 0 0 5
+ 1606 3353 1643 3353 1643 3389 1606 3389 1606 3353
+2 2 0 0 40 41 19 0 20 0.000 2 0 -1 0 0 5
+ 1568 3353 1531 3353 1531 3389 1568 3389 1568 3353
+-6
+6 1413 3345 1465 3397
+1 3 0 0 0 39 18 0 20 0.000 1 0.0000 1439 3371 26 26 1439 3371 1439 3397
+1 3 0 0 40 41 18 0 20 0.000 1 0.0000 1439 3371 15 15 1439 3371 1443 3385
+-6
+2 2 0 0 40 7 19 0 20 0.000 2 0 -1 0 0 3
+ 1950 3371 1875 3371 1950 3371
+2 2 0 0 40 41 19 0 20 0.000 2 0 -1 0 0 5
+ 1945 3384 1896 3384 1896 3357 1945 3357 1945 3384
+-6
+6 1350 3183 2100 3300
+2 1 0 1 7 40 19 0 -1 0.000 2 0 -1 0 0 3
+ 1350 3300 1350 3183 2100 3183
+2 1 0 1 40 -1 19 0 -1 0.000 2 0 -1 0 0 3
+ 1350 3300 2100 3300 2100 3183
+-6
+2 1 0 1 7 7 19 0 -1 0.000 2 0 -1 0 0 5
+ 1275 3675 1875 3675 1875 3450 2175 3450 2175 3150
+2 1 0 1 40 7 19 0 -1 0.000 2 0 -1 0 0 3
+ 1275 3675 1275 3150 2175 3150
+-6
+6 1950 3750 2175 3975
+5 1 0 1 7 7 19 0 -1 0.000 0 0 0 0 2038.000 3900.000 1985 3953 1985 3847 2091 3847
+5 1 0 1 40 7 19 0 -1 0.000 0 1 0 0 2038.000 3900.000 1985 3953 2091 3953 2091 3847
+-6
+6 1200 4050 1800 4800
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+ 1275 4125 1725 4125
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+ 1275 4200 1725 4200
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+ 1275 4275 1725 4275
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+ 1275 4350 1725 4350
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+ 1275 4425 1725 4425
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+ 1275 4500 1725 4500
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+ 1275 4575 1725 4575
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+ 1275 4650 1725 4650
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+ 1275 4725 1725 4725
+-6
+2 2 0 1 0 39 20 0 20 0.000 2 0 -1 0 0 5
+ 1200 4950 1425 4950 1425 4911 1200 4911 1200 4950
+2 2 0 1 0 39 20 0 20 0.000 2 0 -1 0 0 5
+ 2025 4950 2250 4950 2250 4911 2025 4911 2025 4950
+2 2 0 1 0 42 20 0 20 0.000 2 0 -1 0 0 5
+ 1200 4907 2250 4907 2250 3000 1200 3000 1200 4907
+-6
+6 2374 3225 3375 4050
+3 2 0 1 0 37 50 0 -1 0.000 0 0 0 3
+ 2374 3402 3139 3402 3257 4050
+ 0.000 -1.000 0.000
+3 2 0 1 0 37 50 0 -1 0.000 0 0 0 3
+ 2374 3461 3096 3437 3198 4050
+ 0.000 -1.000 0.000
+-6
+2 2 0 1 0 1 50 0 20 0.000 0 0 -1 0 0 5
+ 2925 4575 4050 4575 4050 4875 2925 4875 2925 4575
+2 3 0 1 0 32 50 0 20 0.000 0 0 -1 0 0 5
+ 1200 3000 1575 2475 2400 2475 2250 3000 1200 3000
+2 3 0 1 0 8 50 0 20 0.000 0 0 -1 0 0 5
+ 2925 4575 3000 4200 4050 4200 4050 4575 2925 4575
+2 2 0 1 0 0 50 0 20 0.000 0 0 -1 0 0 5
+ 3075 4725 3900 4725 3900 4800 3075 4800 3075 4725
+2 2 0 1 0 46 50 0 20 0.000 0 0 -1 0 0 5
+ 4800 3975 6450 3975 6450 4875 4800 4875 4800 3975
+2 2 0 1 0 36 50 0 20 0.000 0 0 -1 0 0 5
+ 5025 4575 6225 4575 6225 4725 5025 4725 5025 4575
+2 2 0 1 0 36 50 0 20 0.000 0 0 -1 0 0 5
+ 5025 3975 6225 3975 6225 3300 5025 3300 5025 3975
+2 3 0 1 0 37 50 0 20 0.000 0 0 -1 0 0 5
+ 4800 3975 4800 3825 5025 3825 5025 3975 4800 3975
+2 3 0 1 0 37 50 0 20 0.000 0 0 -1 0 0 5
+ 6225 3825 6375 3825 6450 3975 6225 3975 6225 3825
+2 3 0 1 0 32 50 0 20 0.000 0 0 -1 0 0 5
+ 2400 2475 2250 3000 2250 4875 2400 4350 2400 2475
+2 3 0 1 0 37 50 0 -1 0.000 0 0 -1 0 0 6
+ 3075 4200 3075 4050 3300 4050 3375 4050 3375 4200 3075 4200
+2 3 0 1 0 37 50 0 -1 0.000 0 0 -1 0 0 6
+ 3900 4200 3900 4050 3675 4050 3600 4050 3600 4200 3900 4200
+3 2 0 1 0 37 50 0 -1 0.000 0 0 0 5
+ 3705 4050 3825 3675 4185 3390 4590 3615 4800 4035
+ 0.000 -1.000 -1.000 -1.000 0.000
+3 2 0 1 0 37 50 0 -1 0.000 0 0 0 5
+ 3765 4050 3874 3708 4202 3449 4571 3654 4800 4185
+ 0.000 -1.000 -1.000 -1.000 0.000
+4 0 0 50 0 0 12 0.0000 4 180 735 1350 5400 Computer\001
+4 0 0 50 0 0 12 0.0000 4 180 675 3150 5400 Zip drive\001
+4 0 0 50 0 0 12 0.0000 4 135 510 5325 5400 Printer\001
--- /dev/null
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter
+100.00
+Single
+-2
+1200 2
+0 32 #414541
+0 33 #8e8e8e
+0 34 #414541
+0 35 #8e8e8e
+0 36 #414541
+0 37 #8e8e8e
+0 38 #414541
+0 39 #8e8e8e
+0 40 #414541
+0 41 #8e8e8e
+0 42 #414541
+0 43 #8e8e8e
+0 44 #414141
+0 45 #868286
+0 46 #c7c3c7
+0 47 #8e8e8e
+0 48 #414141
+0 49 #868286
+0 50 #c7c3c7
+0 51 #e7e3e7
+6 2025 1800 3075 2250
+2 4 0 1 0 7 50 0 -1 0.000 0 0 3 0 0 5
+ 3045 2250 3045 1800 2025 1800 2025 2250 3045 2250
+4 0 0 50 0 14 12 0.0000 4 180 210 2400 2100 lp\001
+-6
+6 4125 1800 5175 2250
+2 4 0 1 0 7 50 0 -1 0.000 0 0 3 0 0 5
+ 5145 2250 5145 1800 4125 1800 4125 2250 5145 2250
+4 0 0 50 0 14 12 0.0000 4 135 315 4425 2100 ppa\001
+-6
+6 3225 3075 4275 3525
+6 3375 3225 4125 3450
+4 0 0 50 0 14 12 0.0000 4 165 735 3375 3375 parport\001
+-6
+2 4 0 1 0 7 50 0 -1 0.000 0 0 3 0 0 5
+ 4245 3525 4245 3075 3225 3075 3225 3525 4245 3525
+-6
+6 3000 4350 4500 4800
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 4500 4800 4500 4350 3000 4350 3000 4800 4500 4800
+4 0 0 50 0 14 12 0.0000 4 165 1050 3225 4650 parport_pc\001
+-6
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 2550 2250 3600 3075
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 4650 2250 3825 3075
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 3750 3525 3750 4350
--- /dev/null
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN"[]>
+
+<book id="ParportGuide">
+ <bookinfo>
+ <title>The Parallel Port Subsystem</title>
+
+ <authorgroup>
+ <author>
+ <firstname>Tim</firstname>
+ <surname>Waugh</surname>
+ <affiliation>
+ <address>
+ <email>twaugh@redhat.com</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+
+ <copyright>
+ <year>1999-2000</year>
+ <holder>Tim Waugh</holder>
+ </copyright>
+
+ <legalnotice>
+ <para>
+ This documentation 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.
+ </para>
+
+ <para>
+ 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.
+ </para>
+
+ <para>
+ 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
+ </para>
+
+ <para>
+ For more details see the file COPYING in the source
+ distribution of Linux.
+ </para>
+ </legalnotice>
+ </bookinfo>
+
+<toc></toc>
+
+<chapter id="design">
+<title>Design goals</title>
+
+<sect1>
+<title>The problems</title>
+
+<!-- Short-comings -->
+<!-- How they are addressed -->
+
+<!-- Short-comings
+ - simplistic lp driver
+ - platform differences
+ - no support for Zip drive pass-through
+ - no support for readback? When did Carsten add it?
+ - more parallel port devices. Figures?
+ - IEEE 1284 transfer modes: no advanced modes
+ -->
+
+<para>The first parallel port support for Linux came with the line
+printer driver, <filename>lp</filename>. The printer driver is a
+character special device, and (in Linux 2.0) had support for writing,
+via <function>write</function>, and configuration and statistics
+reporting via <function>ioctl</function>.</para>
+
+<para>The printer driver could be used on any computer that had an IBM
+PC-compatible parallel port. Because some architectures have parallel
+ports that aren't really the same as PC-style ports, other variants of
+the printer driver were written in order to support Amiga and Atari
+parallel ports.</para>
+
+<para>When the Iomega Zip drive was released, and a driver written for
+it, a problem became apparent. The Zip drive is a parallel port
+device that provides a parallel port of its own---it is designed to
+sit between a computer and an attached printer, with the printer
+plugged into the Zip drive, and the Zip drive plugged into the
+computer.</para>
+
+<para>The problem was that, although printers and Zip drives were both
+supported, for any given port only one could be used at a time. Only
+one of the two drivers could be present in the kernel at once. This
+was because of the fact that both drivers wanted to drive the same
+hardware---the parallel port. When the printer driver initialised, it
+would call the <function>check_region</function> function to make sure
+that the IO region associated with the parallel port was free, and
+then it would call <function>request_region</function> to allocate it.
+The Zip drive used the same mechanism. Whichever driver initialised
+first would gain exclusive control of the parallel port.</para>
+
+<para>The only way around this problem at the time was to make sure
+that both drivers were available as loadable kernel modules. To use
+the printer, load the printer driver module; then for the Zip drive,
+unload the printer driver module and load the Zip driver
+module.</para>
+
+<para>The net effect was that printing a document that was stored on a Zip
+drive was a bit of an ordeal, at least if the Zip drive and printer
+shared a parallel port. A better solution was needed.</para>
+
+<para>Zip drives are not the only devices that presented problems for
+Linux. There are other devices with pass-through ports, for example
+parallel port CD-ROM drives. There are also printers that report
+their status textually rather than using simple error pins: sending a
+command to the printer can cause it to report the number of pages that
+it has ever printed, or how much free memory it has, or whether it is
+running out of toner, and so on. The printer driver didn't originally
+offer any facility for reading back this information (although Carsten
+Gross added nibble mode readback support for kernel 2.2).</para>
+
+<!-- IEEE 1284 transfer modes: no advanced modes -->
+
+<para>The IEEE has issued a standards document called IEEE 1284, which
+documents existing practice for parallel port communications in a
+variety of modes. Those modes are: <quote>compatibility</quote>,
+reverse nibble, reverse byte, ECP and EPP. Newer devices often use
+the more advanced modes of transfer (ECP and EPP). In Linux 2.0, the
+printer driver only supported <quote>compatibility mode</quote>
+(i.e. normal printer protocol) and reverse nibble mode.</para>
+
+</sect1>
+
+<sect1>
+<title>The solutions</title>
+
+<!-- How they are addressed
+ - sharing model
+ - overview of structure (i.e. port drivers) in 2.2 and 2.3.
+ - IEEE 1284 stuff
+ - whether or not 'platform independence' goal was met
+ -->
+
+<para>The <filename>parport</filename> code in Linux 2.2 was designed
+to meet these problems of architectural differences in parallel ports,
+of port-sharing between devices with pass-through ports, and of lack
+of support for IEEE 1284 transfer modes.</para>
+
+<!-- platform differences -->
+
+<para>There are two layers to the
+<filename>parport</filename> subsystem, only one of which deals
+directly with the hardware. The other layer deals with sharing and
+IEEE 1284 transfer modes. In this way, parallel support for a
+particular architecture comes in the form of a module which registers
+itself with the generic sharing layer.</para>
+
+<!-- sharing model -->
+
+<para>The sharing model provided by the <filename>parport</filename>
+subsystem is one of exclusive access. A device driver, such as the
+printer driver, must ask the <filename>parport</filename> layer for
+access to the port, and can only use the port once access has been
+granted. When it has finished a <quote>transaction</quote>, it can
+tell the <filename>parport</filename> layer that it may release the
+port for other device drivers to use.</para>
+
+<!-- talk a bit about how drivers can share devices on the same port -->
+
+<para>Devices with pass-through ports all manage to share a parallel
+port with other devices in generally the same way. The device has a
+latch for each of the pins on its pass-through port. The normal state
+of affairs is pass-through mode, with the device copying the signal
+lines between its host port and its pass-through port. When the
+device sees a special signal from the host port, it latches the
+pass-through port so that devices further downstream don't get
+confused by the pass-through device's conversation with the host
+parallel port: the device connected to the pass-through port (and any
+devices connected in turn to it) are effectively cut off from the
+computer. When the pass-through device has completed its transaction
+with the computer, it enables the pass-through port again.</para>
+
+<mediaobject>
+<imageobject>
+<imagedata Align=center scalefit=1 fileref="parport-share.eps">
+</imageobject>
+</mediaobject>
+
+<para>This technique relies on certain <quote>special signals</quote>
+being invisible to devices that aren't watching for them. This tends
+to mean only changing the data signals and leaving the control signals
+alone. IEEE 1284.3 documents a standard protocol for daisy-chaining
+devices together with parallel ports.</para>
+
+<!-- transfer modes -->
+
+<para>Support for standard transfer modes are provided as operations
+that can be performed on a port, along with operations for setting the
+data lines, or the control lines, or reading the status lines. These
+operations appear to the device driver as function pointers; more
+later.</para>
+
+</sect1>
+
+</chapter>
+
+<chapter id="transfermodes">
+<title>Standard transfer modes</title>
+
+<!-- Defined by IEEE, but in common use (even though there are widely -->
+<!-- varying implementations). -->
+
+<para>The <quote>standard</quote> transfer modes in use over the
+parallel port are <quote>defined</quote> by a document called IEEE
+1284. It really just codifies existing practice and documents
+protocols (and variations on protocols) that have been in common use
+for quite some time.</para>
+
+<para>The original definitions of which pin did what were set out by
+Centronics Data Computer Corporation, but only the printer-side
+interface signals were specified.</para>
+
+<para>By the early 1980s, IBM's host-side implementation had become
+the most widely used. New printers emerged that claimed Centronics
+compatibility, but although compatible with Centronics they differed
+from one another in a number of ways.</para>
+
+<para>As a result of this, when IEEE 1284 was published in 1994, all
+that it could really do was document the various protocols that are
+used for printers (there are about six variations on a theme).</para>
+
+<para>In addition to the protocol used to talk to
+Centronics-compatible printers, IEEE 1284 defined other protocols that
+are used for unidirectional peripheral-to-host transfers (reverse
+nibble and reverse byte) and for fast bidirectional transfers (ECP and
+EPP).</para>
+
+</chapter>
+
+<chapter id="structure">
+<title>Structure</title>
+
+<!-- Main structure
+ - sharing core
+ - parports and their IEEE 1284 overrides
+ - IEEE 1284 transfer modes for generic ports
+ - maybe mention muxes here
+ - pardevices
+ - IEEE 1284.3 API
+ -->
+
+<!-- Diagram -->
+
+<mediaobject>
+<imageobject>
+<imagedata Align=Center ScaleFit=1 fileref="parport-structure.eps">
+</imageobject>
+</mediaobject>
+
+<sect1>
+<title>Sharing core</title>
+
+<!-- sharing core -->
+
+<para>At the core of the <filename>parport</filename> subsystem is the
+sharing mechanism (see <filename>drivers/parport/share.c</filename>).
+This module, <filename>parport</filename>, is responsible for
+keeping track of which ports there are in the system, which device
+drivers might be interested in new ports, and whether or not each port
+is available for use (or if not, which driver is currently using
+it).</para>
+
+</sect1>
+
+<sect1>
+<title>Parports and their overrides</title>
+<!-- parports and their overrides -->
+
+<para>The generic <filename>parport</filename> sharing code doesn't
+directly handle the parallel port hardware. That is done instead by
+<quote>low-level</quote> <filename>parport</filename> drivers. The
+function of a low-level <filename>parport</filename> driver is to
+detect parallel ports, register them with the sharing code, and
+provide a list of access functions for each port.</para>
+
+<para>The most basic access functions that must be provided are ones
+for examining the status lines, for setting the control lines, and for
+setting the data lines. There are also access functions for setting
+the direction of the data lines; normally they are in the
+<quote>forward</quote> direction (that is, the computer drives them),
+but some ports allow switching to <quote>reverse</quote> mode (driven
+by the peripheral). There is an access function for examining the
+data lines once in reverse mode.</para>
+
+</sect1>
+
+<sect1>
+<title>IEEE 1284 transfer modes</title>
+<!-- IEEE 1284 transfer modes -->
+
+<para>Stacked on top of the sharing mechanism, but still in the
+<filename>parport</filename> module, are functions for transferring
+data. They are provided for the device drivers to use, and are very
+much like library routines. Since these transfer functions are
+provided by the generic <filename>parport</filename> core they must
+use the <quote>lowest common denominator</quote> set of access
+functions: they can set the control lines, examine the status lines,
+and use the data lines. With some parallel ports the data lines can
+only be set and not examined, and with other ports accessing the data
+register causes control line activity; with these types of situations,
+the IEEE 1284 transfer functions make a best effort attempt to do the
+right thing. In some cases, it is not physically possible to use
+particular IEEE 1284 transfer modes.</para>
+
+<para>The low-level <filename>parport</filename> drivers also provide
+IEEE 1284 transfer functions, as names in the access function list.
+The low-level driver can just name the generic IEEE 1284 transfer
+functions for this. Some parallel ports can do IEEE 1284 transfers in
+hardware; for those ports, the low-level driver can provide functions
+to utilise that feature.</para>
+
+</sect1>
+
+<!-- muxes? -->
+
+<!-- pardevices and pardrivers -->
+
+<sect1>
+<title>Pardevices and parport_drivers</title>
+
+<para>When a parallel port device driver (such as
+<filename>lp</filename>) initialises it tells the sharing layer about
+itself using <function>parport_register_driver</function>. The
+information is put into a <structname>struct
+parport_driver</structname>, which is put into a linked list. The
+information in a <structname>struct parport_driver</structname> really
+just amounts to some function pointers to callbacks in the parallel
+port device driver.</para>
+
+<para>During its initialisation, a low-level port driver tells the
+sharing layer about all the ports that it has found (using
+<function>parport_register_port</function>), and the sharing layer
+creates a <structname>struct parport</structname> for each of them.
+Each <structname>struct parport</structname> contains (among other
+things) a pointer to a <structname>struct
+parport_operations</structname>, which is a list of function pointers
+for the various operations that can be performed on a port. You can
+think of a <structname>struct parport</structname> as a parallel port
+<quote>object</quote>, if <quote>object-orientated</quote> programming
+is your thing. The <structname>parport</structname> structures are
+chained in a linked list, whose head is <varname>portlist</varname>
+(in <filename>drivers/parport/share.c</filename>).</para>
+
+<para>Once the port has been registered, the low-level port driver
+announces it. The <function>parport_announce_port</function> function
+walks down the list of parallel port device drivers
+(<structname>struct parport_driver</structname>s) calling the
+<function>attach</function> function of each.</para>
+
+<para>Similarly, a low-level port driver can undo the effect of
+registering a port with the
+<function>parport_unregister_port</function> function, and device
+drivers are notified using the <function>detach</function>
+callback.</para>
+
+<para>Device drivers can undo the effect of registering themselves
+with the <function>parport_unregister_driver</function>
+function.</para>
+
+</sect1>
+
+<!-- IEEE 1284.3 API -->
+
+<sect1>
+<title>The IEEE 1284.3 API</title>
+
+<para>The ability to daisy-chain devices is very useful, but if every
+device does it in a different way it could lead to lots of
+complications for device driver writers. Fortunately, the IEEE are
+standardising it in IEEE 1284.3, which covers daisy-chain devices and
+port multiplexors.</para>
+
+<para>At the time of writing, IEEE 1284.3 has not been published, but
+the draft specifies the on-the-wire protocol for daisy-chaining and
+multiplexing, and also suggests a programming interface for using it.
+That interface (or most of it) has been implemented in the
+<filename>parport</filename> code in Linux.</para>
+
+<para>At initialisation of the parallel port <quote>bus</quote>, daisy-chained
+devices are assigned addresses starting from zero. There can only be
+four devices with daisy-chain addresses, plus one device on the end
+that doesn't know about daisy-chaining and thinks it's connected
+directly to a computer.</para>
+
+<para>Another way of connecting more parallel port devices is to use a
+multiplexor. The idea is to have a device that is connected directly
+to a parallel port on a computer, but has a number of parallel ports
+on the other side for other peripherals to connect to (two or four
+ports are allowed). The multiplexor switches control to different
+ports under software control---it is, in effect, a programmable
+printer switch.</para>
+
+<para>Combining the ability of daisy-chaining five devices together
+with the ability to multiplex one parallel port between four gives the
+potential to have twenty peripherals connected to the same parallel
+port!</para>
+
+<para>In addition, of course, a single computer can have multiple
+parallel ports. So, each parallel port peripheral in the system can
+be identified with three numbers, or co-ordinates: the parallel port,
+the multiplexed port, and the daisy-chain address.</para>
+
+<mediaobject>
+<imageobject>
+<imagedata align=center scalefit=1 fileref="parport-multi.eps">
+</imageobject>
+</mediaobject>
+
+<!-- x parport_open -->
+<!-- x parport_close -->
+<!-- x parport_device_id -->
+<!-- x parport_device_num -->
+<!-- x parport_device_coords -->
+<!-- x parport_find_device -->
+<!-- x parport_find_class -->
+
+<para>Each device in the system is numbered at initialisation (by
+<function>parport_daisy_init</function>). You can convert between
+this device number and its co-ordinates with
+<function>parport_device_num</function> and
+<function>parport_device_coords</function>.</para>
+
+<funcsynopsis><funcprototype>
+ <funcdef>int <function>parport_device_num</function></funcdef>
+ <paramdef>int <parameter>parport</parameter></paramdef>
+ <paramdef>int <parameter>mux</parameter></paramdef>
+ <paramdef>int <parameter>daisy</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+ <funcdef>int <function>parport_device_coords</function></funcdef>
+ <paramdef>int <parameter>devnum</parameter></paramdef>
+ <paramdef>int *<parameter>parport</parameter></paramdef>
+ <paramdef>int *<parameter>mux</parameter></paramdef>
+ <paramdef>int *<parameter>daisy</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>Any parallel port peripheral will be connected directly or
+indirectly to a parallel port on the system, but it won't have a
+daisy-chain address if it does not know about daisy-chaining, and it
+won't be connected through a multiplexor port if there is no
+multiplexor. The special co-ordinate value <constant>-1</constant> is
+used to indicate these cases.</para>
+
+<para>Two functions are provided for finding devices based on their
+IEEE 1284 Device ID: <function>parport_find_device</function> and
+<function>parport_find_class</function>.</para>
+
+<funcsynopsis><funcprototype>
+ <funcdef>int <function>parport_find_device</function></funcdef>
+ <paramdef>const char *<parameter>mfg</parameter></paramdef>
+ <paramdef>const char *<parameter>mdl</parameter></paramdef>
+ <paramdef>int <parameter>from</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+ <funcdef>int <function>parport_find_class</function></funcdef>
+ <paramdef>parport_device_class <parameter>cls</parameter></paramdef>
+ <paramdef>int <parameter>from</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>These functions take a device number (in addition to some other
+things), and return another device number. They walk through the list
+of detected devices until they find one that matches the requirements,
+and then return that device number (or <constant>-1</constant> if
+there are no more such devices). They start their search at the
+device after the one in the list with the number given (at
+<parameter>from</parameter>+1, in other words).</para>
+
+</sect1>
+
+</chapter>
+
+<chapter id="drivers">
+<title>Device driver's view</title>
+
+<!-- Cover:
+ - sharing interface, preemption, interrupts, wakeups...
+ - IEEE 1284.3 interface
+ - port operations
+ - why can read data but ctr is faked, etc.
+ -->
+
+<!-- I should take a look at the kernel hackers' guide bit I wrote, -->
+<!-- as that deals with a lot of this. The main complaint with it -->
+<!-- was that there weren't enough examples, but 'The printer -->
+<!-- driver' should deal with that later; might be worth mentioning -->
+<!-- in the text. -->
+
+<para>This section is written from the point of view of the device
+driver programmer, who might be writing a driver for a printer or a
+scanner or else anything that plugs into the parallel port. It
+explains how to use the <filename>parport</filename> interface to find
+parallel ports, use them, and share them with other device
+drivers.</para>
+
+<para>We'll start out with a description of the various functions that
+can be called, and then look at a reasonably simple example of their
+use: the printer driver.</para>
+
+<para>The interactions between the device driver and the
+<filename>parport</filename> layer are as follows. First, the device
+driver registers its existence with <filename>parport</filename>, in
+order to get told about any parallel ports that have been (or will be)
+detected. When it gets told about a parallel port, it then tells
+<filename>parport</filename> that it wants to drive a device on that
+port. Thereafter it can claim exclusive access to the port in order
+to talk to its device.</para>
+
+<para>So, the first thing for the device driver to do is tell
+<filename>parport</filename> that it wants to know what parallel ports
+are on the system. To do this, it uses the
+<function>parport_register_device</function> function:</para>
+
+<programlisting>
+<![CDATA[
+struct parport_driver {
+ const char *name;
+ void (*attach) (struct parport *);
+ void (*detach) (struct parport *);
+ struct parport_driver *next;
+};
+]]></programlisting>
+
+<funcsynopsis><funcprototype>
+ <funcdef>int <function>parport_register_driver</function></funcdef>
+ <paramdef>struct parport_driver *<parameter>driver</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>In other words, the device driver passes pointers to a couple of
+functions to <filename>parport</filename>, and
+<filename>parport</filename> calls <function>attach</function> for
+each port that's detected (and <function>detach</function> for each
+port that disappears -- yes, this can happen).</para>
+
+<para>The next thing that happens is that the device driver tells
+<filename>parport</filename> that it thinks there's a device on the
+port that it can drive. This typically will happen in the driver's
+<function>attach</function> function, and is done with
+<function>parport_register_device</function>:</para>
+
+<funcsynopsis><funcprototype>
+ <funcdef>struct pardevice *<function>parport_register_device</function></funcdef>
+ <paramdef>struct parport *<parameter>port</parameter></paramdef>
+ <paramdef>const char *<parameter>name</parameter></paramdef>
+ <paramdef>int <parameter>(*pf)</parameter>
+ <funcparams>void *</funcparams></paramdef>
+ <paramdef>void <parameter>(*kf)</parameter>
+ <funcparams>void *</funcparams></paramdef>
+ <paramdef>void <parameter>(*irq_func)</parameter>
+ <funcparams>int, void *, struct pt_regs *</funcparams></paramdef>
+ <paramdef>int <parameter>flags</parameter></paramdef>
+ <paramdef>void *<parameter>handle</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>The <parameter>port</parameter> comes from the parameter supplied
+to the <function>attach</function> function when it is called, or
+alternatively can be found from the list of detected parallel ports
+directly with the (now deprecated)
+<function>parport_enumerate</function> function.</para>
+
+<para>The next three parameters, <parameter>pf</parameter>,
+<parameter>kf</parameter>, and <parameter>irq_func</parameter>, are
+more function pointers. These callback functions get called under
+various circumstances, and are always given the
+<parameter>handle</parameter> as one of their parameters.</para>
+
+<para>The preemption callback, <parameter>pf</parameter>, is called
+when the driver has claimed access to the port but another device
+driver wants access. If the driver is willing to let the port go, it
+should return zero and the port will be released on its behalf. There
+is no need to call <function>parport_release</function>. If
+<parameter>pf</parameter> gets called at a bad time for letting the
+port go, it should return non-zero and no action will be taken. It is
+good manners for the driver to try to release the port at the earliest
+opportunity after its preemption callback is called.</para>
+
+<para>The <quote>kick</quote> callback, <parameter>kf</parameter>, is
+called when the port can be claimed for exclusive access; that is,
+<function>parport_claim</function> is guaranteed to succeed inside the
+<quote>kick</quote> callback. If the driver wants to claim the port
+it should do so; otherwise, it need not take any action.</para>
+
+<para>The <parameter>irq_func</parameter> callback is called,
+predictably, when a parallel port interrupt is generated. But it is
+not the only code that hooks on the interrupt. The sequence is this:
+the lowlevel driver is the one that has done
+<function>request_irq</function>; it then does whatever
+hardware-specific things it needs to do to the parallel port hardware
+(for PC-style ports, there is nothing special to do); it then tells
+the IEEE 1284 code about the interrupt, which may involve reacting to
+an IEEE 1284 event, depending on the current IEEE 1284 phase; and
+finally the <parameter>irq_func</parameter> function is called.</para>
+
+<para>None of the callback functions are allowed to block.</para>
+
+<para>The <parameter>flags</parameter> are for telling
+<filename>parport</filename> any requirements or hints that are
+useful. The only useful value here (other than
+<constant>0</constant>, which is the usual value) is
+<constant>PARPORT_DEV_EXCL</constant>. The point of that flag is to
+request exclusive access at all times---once a driver has successfully
+called <function>parport_register_device</function> with that flag, no
+other device drivers will be able to register devices on that port
+(until the successful driver deregisters its device, of
+course).</para>
+
+<para>The <constant>PARPORT_DEV_EXCL</constant> flag is for preventing
+port sharing, and so should only be used when sharing the port with
+other device drivers is impossible and would lead to incorrect
+behaviour. Use it sparingly!</para>
+
+<para>Devices can also be registered by device drivers based on their
+device numbers (the same device numbers as in the previous
+section).</para>
+
+<para>The <function>parport_open</function> function is similar to
+<function>parport_register_device</function>, and
+<function>parport_close</function> is the equivalent of
+<function>parport_unregister_device</function>. The difference is
+that <function>parport_open</function> takes a device number rather
+than a pointer to a <structname>struct parport</structname>.</para>
+
+<funcsynopsis><funcprototype>
+ <funcdef>struct pardevice *<function>parport_open</function></funcdef>
+ <paramdef>int <parameter>devnum</parameter></paramdef>
+ <paramdef>int <parameter>(*pf)</parameter>
+ <funcparams>void *</funcparams></paramdef>
+ <paramdef>int <parameter>(*kf)</parameter>
+ <funcparams>void *</funcparams></paramdef>
+ <paramdef>int <parameter>(*irqf)</parameter>
+ <funcparams>int, void *, struct pt_regs *</funcparams></paramdef>
+ <paramdef>int <parameter>flags</parameter></paramdef>
+ <paramdef>void *<parameter>handle</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+ <funcdef>void <function>parport_close</function></funcdef>
+ <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+ <funcdef>struct pardevice *<function>parport_register_device</function></funcdef>
+ <paramdef>struct parport *<parameter>port</parameter></paramdef>
+ <paramdef>const char *<parameter>name</parameter></paramdef>
+ <paramdef>int <parameter>(*pf)</parameter>
+ <funcparams>void *</funcparams></paramdef>
+ <paramdef>int <parameter>(*kf)</parameter>
+ <funcparams>void *</funcparams></paramdef>
+ <paramdef>int <parameter>(*irqf)</parameter>
+ <funcparams>int, void *, struct pt_regs *</funcparams></paramdef>
+ <paramdef>int <parameter>flags</parameter></paramdef>
+ <paramdef>void *<parameter>handle</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+ <funcdef>void <function>parport_unregister_device</function></funcdef>
+ <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>The intended use of these functions is during driver
+initialisation while the driver looks for devices that it supports, as
+demonstrated by the following code fragment:</para>
+
+<programlisting>
+<![CDATA[
+int devnum = -1;
+while ((devnum = parport_find_class (PARPORT_CLASS_DIGCAM,
+ devnum)) != -1) {
+ struct pardevice *dev = parport_open (devnum, ...);
+ ...
+}
+]]></programlisting>
+
+<para>Once your device driver has registered its device and been
+handed a pointer to a <structname>struct pardevice</structname>, the
+next thing you are likely to want to do is communicate with the device
+you think is there. To do that you'll need to claim access to the
+port.</para>
+
+<funcsynopsis><funcprototype>
+ <funcdef>int <function>parport_claim</function></funcdef>
+ <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+ <funcdef>int <function>parport_claim_or_block</function></funcdef>
+ <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+ <funcdef>void <function>parport_release</function></funcdef>
+ <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>To claim access to the port, use
+<function>parport_claim</function> or
+<function>parport_claim_or_block</function>. The first of these will
+not block, and so can be used from interrupt context. If
+<function>parport_claim</function> succeeds it will return zero and
+the port is available to use. It may fail (returning non-zero) if the
+port is in use by another driver and that driver is not willing to
+relinquish control of the port.</para>
+
+<para>The other function, <function>parport_claim_or_block</function>,
+will block if necessary to wait for the port to be free. If it slept,
+it returns <constant>1</constant>; if it succeeded without needing to
+sleep it returns <constant>0</constant>. If it fails it will return a
+negative error code.</para>
+
+<para>When you have finished communicating with the device, you can
+give up access to the port so that other drivers can communicate with
+their devices. The <function>parport_release</function> function
+cannot fail, but it should not be called without the port claimed.
+Similarly, you should not try to claim the port if you already have it
+claimed.</para>
+
+<para>You may find that although there are convenient points for your
+driver to relinquish the parallel port and allow other drivers to talk
+to their devices, it would be preferable to keep hold of the port.
+The printer driver only needs the port when there is data to print,
+for example, but a network driver (such as PLIP) could be sent a
+remote packet at any time. With PLIP, it is no huge catastrophe if a
+network packet is dropped, since it will likely be sent again, so it
+is possible for that kind of driver to share the port with other
+(pass-through) devices.</para>
+
+<para>The <function>parport_yield</function> and
+<function>parport_yield_blocking</function> functions are for marking
+points in the driver at which other drivers may claim the port and use
+their devices. Yielding the port is similar to releasing it and
+reclaiming it, but it more efficient because nothing is done if there
+are no other devices needing the port. In fact, nothing is done even
+if there are other devices waiting but the current device is still
+within its <quote>timeslice</quote>. The default timeslice is half a
+second, but it can be adjusted via a <filename>/proc</filename>
+entry.</para>
+
+<funcsynopsis><funcprototype>
+ <funcdef>int <function>parport_yield</function></funcdef>
+ <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+ <funcdef>int <function>parport_yield_blocking</function></funcdef>
+ <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>The first of these, <function>parport_yield</function>, will not
+block but as a result may fail. The return value for
+<function>parport_yield</function> is the same as for
+<function>parport_claim</function>. The blocking version,
+<function>parport_yield_blocking</function>, has the same return code
+as <function>parport_claim_or_block</function>.</para>
+
+<para>Once the port has been claimed, the device driver can use the
+functions in the <structname>struct parport_operations</structname>
+pointer in the <structname>struct parport</structname> it has a
+pointer to. For example:</para>
+
+<programlisting>
+<![CDATA[
+port->ops->write_data (port, d);
+]]></programlisting>
+
+<para>Some of these operations have <quote>shortcuts</quote>. For
+instance, <function>parport_write_data</function> is equivalent to the
+above, but may be a little bit faster (it's a macro that in some cases
+can avoid needing to indirect through <varname>port</varname> and
+<varname>ops</varname>).</para>
+
+</chapter>
+
+<chapter id="portdrivers">
+<title>Port drivers</title>
+
+<!-- What port drivers are for (i.e. implementing parport objects). -->
+
+<para>To recap, then:</para>
+
+<itemizedlist spacing=compact>
+
+<listitem>
+<para>
+The device driver registers itself with <filename>parport</filename>.
+</para>
+</listitem>
+
+<listitem>
+<para>
+A low-level driver finds a parallel port and registers it with
+<filename>parport</filename> (these first two things can happen in
+either order). This registration creates a <structname>struct
+parport</structname> which is linked onto a list of known ports.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<filename>parport</filename> calls the <function>attach</function>
+function of each registered device driver, passing it the pointer to
+the new <structname>struct parport</structname>.
+</para>
+</listitem>
+
+<listitem>
+<para>
+The device driver gets a handle from <filename>parport</filename>, for
+use with
+<function>parport_claim</function>/<function>release</function>. This
+handle takes the form of a pointer to a <structname>struct
+pardevice</structname>, representing a particular device on the
+parallel port, and is acquired using
+<function>parport_register_device</function>.
+</para>
+</listitem>
+
+<listitem>
+<para>
+The device driver claims the port using
+<function>parport_claim</function> (or
+<function>function_claim_or_block</function>).
+</para>
+</listitem>
+
+<listitem>
+<para>
+Then it goes ahead and uses the port. When finished it releases the
+port.
+</para>
+</listitem>
+
+</itemizedlist>
+
+<para>The purpose of the low-level drivers, then, is to detect
+parallel ports and provide methods of accessing them
+(i.e. implementing the operations in <structname>struct
+parport_operations</structname>).</para>
+
+<!-- Interaction with sharing engine; port state -->
+<!-- What did I mean by that? -->
+
+<!-- Talk about parport_pc implementation, and contrast with e.g. amiga -->
+
+<para>A more complete description of which operation is supposed to do
+what is available in
+<filename>Documentation/parport-lowlevel.txt</filename>.</para>
+
+</chapter>
+
+<chapter id="lp">
+<title>The printer driver</title>
+
+<!-- Talk the reader through the printer driver. -->
+<!-- Could even talk about parallel port console here. -->
+
+<para>The printer driver, <filename>lp</filename> is a character
+special device driver and a <filename>parport</filename> client. As a
+character special device driver it registers a <structname>struct
+file_operations</structname> using
+<function>register_chrdev</function>, with pointers filled in for
+<structfield>write</structfield>, <structfield>ioctl</structfield>,
+<structfield>open</structfield> and
+<structfield>release</structfield>. As a client of
+<filename>parport</filename>, it registers a <structname>struct
+parport_driver</structname> using
+<function>parport_register_driver</function>, so that
+<filename>parport</filename> knows to call
+<function>lp_attach</function> when a new parallel port is discovered
+(and <function>lp_detach</function> when it goes away).</para>
+
+<para>The parallel port console functionality is also implemented in
+<filename>lp.c</filename>, but that won't be covered here (it's quite
+simple though).</para>
+
+<para>The initialisation of the driver is quite easy to understand
+(see <function>lp_init</function>). The <varname>lp_table</varname>
+is an array of structures that contain information about a specific
+device (the <structname>struct pardevice</structname> associated with
+it, for example). That array is initialised to sensible values first
+of all.</para>
+
+<para>Next, the printer driver calls
+<function>register_chrdev</function> passing it a pointer to
+<varname>lp_fops</varname>, which contains function pointers for the
+printer driver's implementation of <function>open</function>,
+<function>write</function>, and so on. This part is the same as for
+any character special device driver.</para>
+
+<para>After successfully registering itself as a character special
+device driver, the printer driver registers itself as a
+<filename>parport</filename> client using
+<function>parport_register_driver</function>. It passes a pointer to
+this structure:</para>
+
+<programlisting>
+<![CDATA[
+static struct parport_driver lp_driver = {
+ "lp",
+ lp_attach,
+ lp_detach,
+ NULL
+};
+]]></programlisting>
+
+<para>The <function>lp_detach</function> function is not very
+interesting (it does nothing); the interesting bit is
+<function>lp_attach</function>. What goes on here depends on whether
+the user supplied any parameters. The possibilities are: no
+parameters supplied, in which case the printer driver uses every port
+that is detected; the user supplied the parameter <quote>auto</quote>,
+in which case only ports on which the device ID string indicates a
+printer is present are used; or the user supplied a list of parallel
+port numbers to try, in which case only those are used.</para>
+
+<para>For each port that the printer driver wants to use (see
+<function>lp_register</function>), it calls
+<function>parport_register_device</function> and stores the resulting
+<structname>struct pardevice</structname> pointer in the
+<varname>lp_table</varname>. If the user told it to do so, it then
+resets the printer.</para>
+
+<para>The other interesting piece of the printer driver, from the
+point of view of <filename>parport</filename>, is
+<function>lp_write</function>. In this function, the user space
+process has data that it wants printed, and the printer driver hands
+it off to the <filename>parport</filename> code to deal with.</para>
+
+<para>The <filename>parport</filename> functions it uses that we have
+not seen yet are <function>parport_negotiate</function>,
+<function>parport_set_timeout</function>, and
+<function>parport_write</function>. These functions are part of the
+IEEE 1284 implementation.</para>
+
+<para>The way the IEEE 1284 protocol works is that the host tells the
+peripheral what transfer mode it would like to use, and the peripheral
+either accepts that mode or rejects it; if the mode is rejected, the
+host can try again with a different mode. This is the negotation
+phase. Once the peripheral has accepted a particular transfer mode,
+data transfer can begin that mode.</para>
+
+<para>The particular transfer mode that the printer driver wants to
+use is named in IEEE 1284 as <quote>compatibility</quote> mode, and
+the function to request a particular mode is called
+<function>parport_negotiate</function>.</para>
+
+<funcsynopsis><funcprototype>
+ <funcdef>int <function>parport_negotiate</function></funcdef>
+ <paramdef>struct parport *<parameter>port</parameter></paramdef>
+ <paramdef>int <parameter>mode</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>The <parameter>modes</parameter> parameter is a symbolic
+constant representing an IEEE 1284 mode; in this instance, it is
+<constant>IEEE1284_MODE_COMPAT</constant>. (Compatibility mode is
+slightly different to the other modes---rather than being specifically
+requested, it is the default until another mode is selected.)</para>
+
+<para>Back to <function>lp_write</function> then. First, access to
+the parallel port is secured with
+<function>parport_claim_or_block</function>. At this point the driver
+might sleep, waiting for another driver (perhaps a Zip drive driver,
+for instance) to let the port go. Next, it goes to compatibility mode
+using <function>parport_negotiate</function>.</para>
+
+<para>The main work is done in the write-loop. In particular, the
+line that hands the data over to <filename>parport</filename>
+reads:</para>
+
+<programlisting>
+<![CDATA[
+ written = parport_write (port, kbuf, copy_size);
+]]></programlisting>
+
+<para>The <function>parport_write</function> function writes data to
+the peripheral using the currently selected transfer mode
+(compatibility mode, in this case). It returns the number of bytes
+successfully written:</para>
+
+<funcsynopsis><funcprototype>
+ <funcdef>ssize_t <function>parport_write</function></funcdef>
+ <paramdef>struct parport *<parameter>port</parameter></paramdef>
+ <paramdef>const void *<parameter>buf</parameter></paramdef>
+ <paramdef>size_t <parameter>len</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+ <funcdef>ssize_t <function>parport_read</function></funcdef>
+ <paramdef>struct parport *<parameter>port</parameter></paramdef>
+ <paramdef>void *<parameter>buf</parameter></paramdef>
+ <paramdef>size_t <parameter>len</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>(<function>parport_read</function> does what it sounds like, but
+only works for modes in which reverse transfer is possible. Of
+course, <function>parport_write</function> only works in modes in
+which forward transfer is possible, too.)</para>
+
+<para>The <parameter>buf</parameter> pointer should be to kernel space
+memory, and obviously the <parameter>len</parameter> parameter
+specifies the amount of data to transfer.</para>
+
+<para>In fact what <function>parport_write</function> does is call the
+appropriate block transfer function from the <structname>struct
+parport_operations</structname>:</para>
+
+<programlisting>
+<![CDATA[
+struct parport_operations {
+ [...]
+
+ /* Block read/write */
+ size_t (*epp_write_data) (struct parport *port, const void *buf,
+ size_t len, int flags);
+ size_t (*epp_read_data) (struct parport *port, void *buf, size_t len,
+ int flags);
+ size_t (*epp_write_addr) (struct parport *port, const void *buf,
+ size_t len, int flags);
+ size_t (*epp_read_addr) (struct parport *port, void *buf, size_t len,
+ int flags);
+
+ size_t (*ecp_write_data) (struct parport *port, const void *buf,
+ size_t len, int flags);
+ size_t (*ecp_read_data) (struct parport *port, void *buf, size_t len,
+ int flags);
+ size_t (*ecp_write_addr) (struct parport *port, const void *buf,
+ size_t len, int flags);
+
+ size_t (*compat_write_data) (struct parport *port, const void *buf,
+ size_t len, int flags);
+ size_t (*nibble_read_data) (struct parport *port, void *buf,
+ size_t len, int flags);
+ size_t (*byte_read_data) (struct parport *port, void *buf,
+ size_t len, int flags);
+};
+]]></programlisting>
+
+<para>The transfer code in <filename>parport</filename> will tolerate
+a data transfer stall only for so long, and this timeout can be
+specified with <function>parport_set_timeout</function>, which returns
+the previous timeout:</para>
+
+<funcsynopsis><funcprototype>
+ <funcdef>long <function>parport_set_timeout</function></funcdef>
+ <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+ <paramdef>long <parameter>inactivity</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>This timeout is specific to the device, and is restored on
+<function>parport_claim</function>.</para>
+
+</chapter>
+
+<chapter id="ppdev">
+<title>User-level device drivers</title>
+
+<!-- ppdev -->
+<sect1>
+<title>Introduction to ppdev</title>
+
+<para>The printer is accessible through <filename>/dev/lp0</filename>;
+in the same way, the parallel port itself is accessible through
+<filename>/dev/parport0</filename>. The difference is in the level of
+control that you have over the wires in the parallel port
+cable.</para>
+
+<para>With the printer driver, a user-space program (such as the
+printer spooler) can send bytes in <quote>printer protocol</quote>.
+Briefly, this means that for each byte, the eight data lines are set
+up, then a <quote>strobe</quote> line tells the printer to look at the
+data lines, and the printer sets an <quote>acknowledgement</quote>
+line to say that it got the byte. The printer driver also allows the
+user-space program to read bytes in <quote>nibble mode</quote>, which
+is a way of transferring data from the peripheral to the computer half
+a byte at a time (and so it's quite slow).</para>
+
+<para>In contrast, the <filename>ppdev</filename> driver (accessed via
+<filename>/dev/parport0</filename>) allows you to:</para>
+
+<itemizedlist spacing=compact>
+
+<listitem>
+<para>
+examine status lines,
+</para>
+</listitem>
+
+<listitem>
+<para>
+set control lines,
+</para>
+</listitem>
+
+<listitem>
+<para>
+set/examine data lines (and control the direction of the data lines),
+</para>
+</listitem>
+
+<listitem>
+<para>
+wait for an interrupt (triggered by one of the status lines),
+</para>
+</listitem>
+
+<listitem>
+<para>
+find out how many new interrupts have occurred,
+</para>
+</listitem>
+
+<listitem>
+<para>
+set up a response to an interrupt,
+</para>
+</listitem>
+
+<listitem>
+<para>
+use IEEE 1284 negotiation (for telling peripheral which transfer mode,
+to use)
+</para>
+</listitem>
+
+<listitem>
+<para>
+transfer data using a specified IEEE 1284 mode.
+</para>
+</listitem>
+
+</itemizedlist>
+
+</sect1>
+
+<sect1>
+<title>User-level or kernel-level driver?</title>
+
+<para>The decision of whether to choose to write a kernel-level device
+driver or a user-level device driver depends on several factors. One
+of the main ones from a practical point of view is speed: kernel-level
+device drivers get to run faster because they are not preemptable,
+unlike user-level applications.</para>
+
+<para>Another factor is ease of development. It is in general easier
+to write a user-level driver because (a) one wrong move does not
+result in a crashed machine, (b) you have access to user libraries
+(such as the C library), and (c) debugging is easier.</para>
+
+</sect1>
+
+<sect1>
+<title>Programming interface</title>
+
+<para>The <filename>ppdev</filename> interface is largely the same as
+that of other character special devices, in that it supports
+<function>open</function>, <function>close</function>,
+<function>read</function>, <function>write</function>, and
+<function>ioctl</function>.</para>
+
+<sect2>
+<title>Starting and stopping: <function>open</function> and
+<function>close</function></title>
+
+<para>The device node <filename>/dev/parport0</filename> represents
+any device that is connected to <filename>parport0</filename>, the
+first parallel port in the system. Each time the device node is
+opened, it represents (to the process doing the opening) a different
+device. It can be opened more than once, but only one instance can
+actually be in control of the parallel port at any time. A process
+that has opened <filename>/dev/parport0</filename> shares the parallel
+port in the same way as any other device driver. A user-land driver
+may be sharing the parallel port with in-kernel device drivers as well
+as other user-land drivers.</para>
+</sect2>
+
+<sect2>
+<title>Control: <function>ioctl</function></title>
+
+<para>Most of the control is done, naturally enough, via the
+<function>ioctl</function> call. Using <function>ioctl</function>,
+the user-land driver can control both the <filename>ppdev</filename>
+driver in the kernel and the physical parallel port itself. The
+<function>ioctl</function> call takes as parameters a file descriptor
+(the one returned from opening the device node), a command, and
+optionally (a pointer to) some data.</para>
+
+<variablelist>
+<varlistentry><term><constant>PPCLAIM</constant></term>
+<listitem>
+
+<para>Claims access to the port. As a user-land device driver writer,
+you will need to do this before you are able to actually change the
+state of the parallel port in any way. Note that some operations only
+affect the <filename>ppdev</filename> driver and not the port, such as
+<constant>PPSETMODE</constant>; they can be performed while access to
+the port is not claimed.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPEXCL</constant></term>
+<listitem>
+
+<para>Instructs the kernel driver to forbid any sharing of the port
+with other drivers, i.e. it requests exclusivity. The
+<constant>PPEXCL</constant> command is only valid when the port is not
+already claimed for use, and it may mean that the next
+<constant>PPCLAIM</constant> <function>ioctl</function> will fail:
+some other driver may already have registered itself on that
+port.</para>
+
+<para>Most device drivers don't need exclusive access to the port.
+It's only provided in case it is really needed, for example for
+devices where access to the port is required for extensive periods of
+time (many seconds).</para>
+
+<para>Note that the <constant>PPEXCL</constant>
+<function>ioctl</function> doesn't actually claim the port there and
+then---action is deferred until the <constant>PPCLAIM</constant>
+<function>ioctl</function> is performed.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPRELEASE</constant></term>
+<listitem>
+
+<para>Releases the port. Releasing the port undoes the effect of
+claiming the port. It allows other device drivers to talk to their
+devices (assuming that there are any).</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPYIELD</constant></term>
+<listitem>
+
+<para>Yields the port to another driver. This
+<function>ioctl</function> is a kind of short-hand for releasing the
+port and immediately reclaiming it. It gives other drivers a chance
+to talk to their devices, but afterwards claims the port back. An
+example of using this would be in a user-land printer driver: once a
+few characters have been written we could give the port to another
+device driver for a while, but if we still have characters to send to
+the printer we would want the port back as soon as possible.</para>
+
+<para>It is important not to claim the parallel port for too long, as
+other device drivers will have no time to service their devices. If
+your device does not allow for parallel port sharing at all, it is
+better to claim the parallel port exclusively (see
+<constant>PPEXCL</constant>).</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPNEGOT</constant></term>
+<listitem>
+
+<para>Performs IEEE 1284 negotiation into a particular mode. Briefly,
+negotiation is the method by which the host and the peripheral decide
+on a protocol to use when transferring data.</para>
+
+<para>An IEEE 1284 compliant device will start out in compatibility
+mode, and then the host can negotiate to another mode (such as
+ECP).</para>
+
+<para>The <function>ioctl</function> parameter should be a pointer to
+an <type>int</type>; values for this are in
+<filename>parport.h</filename> and include:</para>
+
+<itemizedlist spacing=compact>
+<listitem><para><constant>IEEE1284_MODE_COMPAT</constant></para></listitem>
+<listitem><para><constant>IEEE1284_MODE_NIBBLE</constant></para></listitem>
+<listitem><para><constant>IEEE1284_MODE_BYTE</constant></para></listitem>
+<listitem><para><constant>IEEE1284_MODE_EPP</constant></para></listitem>
+<listitem><para><constant>IEEE1284_MODE_ECP</constant></para></listitem>
+</itemizedlist>
+
+<para>The <constant>PPNEGOT</constant> <function>ioctl</function>
+actually does two things: it performs the on-the-wire negotiation, and
+it sets the behaviour of subsequent
+<function>read</function>/<function>write</function> calls so that
+they use that mode (but see <constant>PPSETMODE</constant>).</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPSETMODE</constant></term>
+<listitem>
+
+<para>Sets which IEEE 1284 protocol to use for the
+<function>read</function> and <function>write</function> calls.</para>
+
+<para>The <function>ioctl</function> parameter should be a pointer to
+an <type>int</type>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPGETTIME</constant></term>
+<listitem>
+
+<para>Retrieves the time-out value. The <function>read</function> and
+<function>write</function> calls will time out if the peripheral
+doesn't respond quickly enough. The <constant>PPGETTIME</constant>
+<function>ioctl</function> retrieves the length of time that the
+peripheral is allowed to have before giving up.</para>
+
+<para>The <function>ioctl</function> parameter should be a pointer to
+a <structname>struct timeval</structname>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPSETTIME</constant></term>
+<listitem>
+
+<para>Sets the time-out. The <function>ioctl</function> parameter
+should be a pointer to a <structname>struct
+timeval</structname>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPWCONTROL</constant></term>
+<listitem>
+
+<para>Sets the control lines. The <function>ioctl</function>
+parameter is a pointer to an <type>unsigned char</type>, the bitwise
+OR of the control line values in
+<filename>parport.h</filename>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPRCONTROL</constant></term>
+<listitem>
+
+<para>Returns the last value written to the control register, in the
+form of an <type>unsigned char</type>: each bit corresponds to a
+control line (although some are unused). The
+<function>ioctl</function> parameter should be a pointer to an
+<type>unsigned char</type>.</para>
+
+<para>This doesn't actually touch the hardware; the last value written
+is remembered in software. This is because some parallel port
+hardware does not offer read access to the control register.</para>
+
+<para>The control lines bits are defined in
+<filename>parport.h</filename>:</para>
+
+<itemizedlist spacing=compact>
+<listitem><para><constant>PARPORT_CONTROL_STROBE</constant></para></listitem>
+<listitem><para><constant>PARPORT_CONTROL_AUTOFD</constant></para></listitem>
+<listitem><para><constant>PARPORT_CONTROL_SELECT</constant></para></listitem>
+<listitem><para><constant>PARPORT_CONTROL_INIT</constant></para></listitem>
+</itemizedlist>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPFCONTROL</constant></term>
+<listitem>
+
+<para>Frobs the control lines. Since a common operation is to change
+one of the control signals while leaving the others alone, it would be
+quite inefficient for the user-land driver to have to use
+<constant>PPRCONTROL</constant>, make the change, and then use
+<constant>PPWCONTROL</constant>. Of course, each driver could
+remember what state the control lines are supposed to be in (they are
+never changed by anything else), but in order to provide
+<constant>PPRCONTROL</constant>, <filename>ppdev</filename> must
+remember the state of the control lines anyway.</para>
+
+<para>The <constant>PPFCONTROL</constant> <function>ioctl</function>
+is for <quote>frobbing</quote> control lines, and is like
+<constant>PPWCONTROL</constant> but acts on a restricted set of
+control lines. The <function>ioctl</function> parameter is a pointer
+to a <structname>struct ppdev_frob_struct</structname>:</para>
+
+<programlisting>
+<![CDATA[
+struct ppdev_frob_struct {
+ unsigned char mask;
+ unsigned char val;
+};
+]]>
+</programlisting>
+
+<para>The <structfield>mask</structfield> and
+<structfield>val</structfield> fields are bitwise ORs of control line
+names (such as in <constant>PPWCONTROL</constant>). The operation
+performed by <constant>PPFCONTROL</constant> is:</para>
+
+<programlisting>
+<![CDATA[new_ctr = (old_ctr & ~mask) | val;]]>
+</programlisting>
+
+<para>In other words, the signals named in
+<structfield>mask</structfield> are set to the values in
+<structfield>val</structfield>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPRSTATUS</constant></term>
+<listitem>
+
+<para>Returns an <type>unsigned char</type> containing bits set for
+each status line that is set (for instance,
+<constant>PARPORT_STATUS_BUSY</constant>). The
+<function>ioctl</function> parameter should be a pointer to an
+<type>unsigned char</type>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPDATADIR</constant></term>
+<listitem>
+
+<para>Controls the data line drivers. Normally the computer's
+parallel port will drive the data lines, but for byte-wide transfers
+from the peripheral to the host it is useful to turn off those drivers
+and let the peripheral drive the signals. (If the drivers on the
+computer's parallel port are left on when this happens, the port might
+be damaged.)</para>
+
+<para>This is only needed in conjunction with
+<constant>PPWDATA</constant> or <constant>PPRDATA</constant>.</para>
+
+<para>The <function>ioctl</function> parameter is a pointer to an
+<type>int</type>. If the <type>int</type> is zero, the drivers are
+turned on (forward direction); if non-zero, the drivers are turned off
+(reverse direction).</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPWDATA</constant></term>
+<listitem>
+
+<para>Sets the data lines (if in forward mode). The
+<function>ioctl</function> parameter is a pointer to an <type>unsigned
+char</type>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPRDATA</constant></term>
+<listitem>
+
+<para>Reads the data lines (if in reverse mode). The
+<function>ioctl</function> parameter is a pointer to an <type>unsigned
+char</type>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPCLRIRQ</constant></term>
+<listitem>
+
+<para>Clears the interrupt count. The <filename>ppdev</filename>
+driver keeps a count of interrupts as they are triggered.
+<constant>PPCLRIRQ</constant> stores this count in an
+<type>int</type>, a pointer to which is passed in as the
+<function>ioctl</function> parameter.</para>
+
+<para>In addition, the interrupt count is reset to zero.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPWCTLONIRQ</constant></term>
+<listitem>
+
+<para>Set a trigger response. Afterwards when an interrupt is
+triggered, the interrupt handler will set the control lines as
+requested. The <function>ioctl</function> parameter is a pointer to
+an <type>unsigned char</type>, which is interpreted in the same way as
+for <constant>PPWCONTROL</constant>.</para>
+
+<para>The reason for this <function>ioctl</function> is simply speed.
+Without this <function>ioctl</function>, responding to an interrupt
+would start in the interrupt handler, switch context to the user-land
+driver via <function>poll</function> or <function>select</function>,
+and then switch context back to the kernel in order to handle
+<constant>PPWCONTROL</constant>. Doing the whole lot in the interrupt
+handler is a lot faster.</para>
+
+</listitem></varlistentry>
+
+<!-- PPSETPHASE? -->
+
+</variablelist>
+
+</sect2>
+
+<sect2>
+<title>Transferring data: <function>read</function> and
+<function>write</function></title>
+
+<para>Transferring data using <function>read</function> and
+<function>write</function> is straightforward. The data is
+transferring using the current IEEE 1284 mode (see the
+<constant>PPSETMODE</constant> <function>ioctl</function>). For modes
+which can only transfer data in one direction, only the appropriate
+function will work, of course.</para>
+</sect2>
+
+<sect2>
+<title>Waiting for events: <function>poll</function> and
+<function>select</function></title>
+
+<para>The <filename>ppdev</filename> driver provides user-land device
+drivers with the ability to wait for interrupts, and this is done
+using <function>poll</function> (and <function>select</function>,
+which is implemented in terms of <function>poll</function>).</para>
+
+<para>When a user-land device driver wants to wait for an interrupt,
+it sleeps with <function>poll</function>. When the interrupt arrives,
+<filename>ppdev</filename> wakes it up (with a <quote>read</quote>
+event, although strictly speaking there is nothing to actually
+<function>read</function>).</para>
+
+</sect2>
+
+</sect1>
+
+<sect1>
+<title>Examples</title>
+
+<para>Presented here are two demonstrations of how to write a simple
+printer driver for <filename>ppdev</filename>. Firstly we will use
+the <function>write</function> function, and after that we will drive
+the control and data lines directly.</para>
+
+<para>The first thing to do is to actually open the device.</para>
+
+<programlisting><![CDATA[
+int drive_printer (const char *name)
+{
+ int fd;
+ int mode; /* We'll need this later. */
+
+ fd = open (name, O_RDWR);
+ if (fd == -1) {
+ perror ("open");
+ return 1;
+ }
+]]></programlisting>
+
+<para>Here <varname>name</varname> should be something along the lines
+of <filename>"/dev/parport0"</filename>. (If you don't have any
+<filename>/dev/parport</filename> files, you can make them with
+<command>mknod</command>; they are character special device nodes with
+major 99.)</para>
+
+<para>In order to do anything with the port we need to claim access to
+it.</para>
+
+<programlisting><![CDATA[
+ if (ioctl (fd, PPCLAIM)) {
+ perror ("PPCLAIM");
+ close (fd);
+ return 1;
+ }
+]]></programlisting>
+
+<para>Our printer driver will copy its input (from
+<varname>stdin</varname>) to the printer, and it can do that it one of
+two ways. The first way is to hand it all off to the kernel driver,
+with the knowledge that the protocol that the printer speaks is IEEE
+1284's <quote>compatibility</quote> mode.</para>
+
+<programlisting><![CDATA[
+ /* Switch to compatibility mode. (In fact we don't need
+ * to do this, since we start off in compatibility mode
+ * anyway, but this demonstrates PPNEGOT.)
+ mode = IEEE1284_MODE_COMPAT;
+ if (ioctl (fd, PPNEGOT, &mode)) {
+ perror ("PPNEGOT");
+ close (fd);
+ return 1;
+ }
+
+ for (;;) {
+ char buffer[1000];
+ char *ptr = buffer;
+ size_t got;
+
+ got = read (0 /* stdin */, buffer, 1000);
+ if (got < 0) {
+ perror ("read");
+ close (fd);
+ return 1;
+ }
+
+ if (got == 0)
+ /* End of input */
+ break;
+
+ while (got > 0) {
+ int written = write_printer (fd, ptr, got);
+
+ if (written < 0) {
+ perror ("write");
+ close (fd);
+ return 1;
+ }
+
+ ptr += written;
+ got -= written;
+ }
+ }
+]]></programlisting>
+
+<para>The <function>write_printer</function> function is not pictured
+above. This is because the main loop that is shown can be used for
+both methods of driving the printer. Here is one implementation of
+<function>write_printer</function>:</para>
+
+<programlisting><![CDATA[
+ssize_t write_printer (int fd, const void *ptr, size_t count)
+{
+ return write (fd, ptr, count);
+}
+]]></programlisting>
+
+<para>We hand the data to the kernel-level driver (using
+<function>write</function>) and it handles the printer
+protocol.</para>
+
+<para>Now let's do it the hard way! In this particular example there
+is no practical reason to do anything other than just call
+<function>write</function>, because we know that the printer talks an
+IEEE 1284 protocol. On the other hand, this particular example does
+not even need a user-land driver since there is already a kernel-level
+one; for the purpose of this discussion, try to imagine that the
+printer speaks a protocol that is not already implemented under
+Linux.</para>
+
+<para>So, here is the alternative implementation of
+<function>write_printer</function> (for brevity, error checking has
+been omitted):</para>
+
+<programlisting><![CDATA[
+ssize_t write_printer (int fd, const void *ptr, size_t count)
+{
+ ssize_t wrote = 0;
+
+ while (wrote < count) {
+ unsigned char status, control, data;
+ unsigned char mask = (PARPORT_STATUS_ERROR
+ | PARPORT_STATUS_BUSY);
+ unsigned char val = (PARPORT_STATUS_ERROR
+ | PARPORT_STATUS_BUSY);
+ struct parport_frob_struct frob;
+ struct timespec ts;
+
+ /* Wait for printer to be ready */
+ for (;;) {
+ ioctl (fd, PPRSTATUS, &status);
+
+ if ((status & mask) == val)
+ break;
+
+ ioctl (fd, PPRELEASE);
+ sleep (1);
+ ioctl (fd, PPCLAIM);
+ }
+
+ /* Set the data lines */
+ data = * ((char *) ptr)++;
+ ioctl (fd, PPWDATA, &data);
+
+ /* Delay for a bit */
+ ts.tv_sec = 0;
+ ts.tv_nsec = 1000;
+ nanosleep (&ts, NULL);
+
+ /* Pulse strobe */
+ frob.mask = PARPORT_CONTROL_STROBE;
+ frob.val = PARPORT_CONTROL_STROBE;
+ ioctl (fd, PPFCONTROL, &frob);
+ nanosleep (&ts, NULL);
+
+ /* End the pulse */
+ frob.val = 0;
+ ioctl (fd, PPFCONTROL, &frob);
+ nanosleep (&ts, NULL);
+
+ wrote++;
+ }
+
+ return wrote;
+}
+]]></programlisting>
+
+<para>To show a bit more of the <filename>ppdev</filename> interface,
+here is a small piece of code that is intended to mimic the printer's
+side of printer protocol.</para>
+
+<programlisting><![CDATA[
+ for (;;)
+ {
+ int irqc;
+ int busy = nAck | nFault;
+ int acking = nFault;
+ int ready = Busy | nAck | nFault;
+ char ch;
+
+ /* Set up the control lines when an interrupt happens. */
+ ioctl (fd, PPWCTLONIRQ, &busy);
+
+ /* Now we're ready. */
+ ioctl (fd, PPWCONTROL, &ready);
+
+ /* Wait for an interrupt. */
+ {
+ fd_set rfds;
+ FD_ZERO (&rfds);
+ FD_SET (fd, &rfds);
+ if (!select (fd + 1, &rfds, NULL, NULL, NULL))
+ /* Caught a signal? */
+ continue;
+ }
+
+ /* We are now marked as busy. */
+
+ /* Fetch the data. */
+ ioctl (fd, PPRDATA, &ch);
+
+ /* Clear the interrupt. */
+ ioctl (fd, PPCLRIRQ, &irqc);
+ if (irqc > 1)
+ fprintf (stderr, "Arghh! Missed %d interrupt%s!\n",
+ irqc - 1, irqc == 2 ? "s" : "");
+
+ /* Ack it. */
+ ioctl (fd, PPWCONTROL, &acking);
+ usleep (2);
+ ioctl (fd, PPWCONTROL, &busy);
+
+ putchar (ch);
+ }
+]]></programlisting>
+
+</sect1>
+
+</chapter>
+</book>
\ No newline at end of file
<chapter id="intro">
<title>Introduction</title>
<para>
- Parts of this document first appeared in Linux Magazine under a
- ninety day exclusivity.
+ Parts of this document first appeared in Linux Magazine under a
+ ninety day exclusivity.
</para>
<para>
- Video4Linux is intended to provide a common programming interface
- for the many TV and capture cards now on the market, as well as
- parallel port and USB video cameras. Radio, teletext decoders and
- vertical blanking data interfaces are also provided.
+ Video4Linux is intended to provide a common programming interface
+ for the many TV and capture cards now on the market, as well as
+ parallel port and USB video cameras. Radio, teletext decoders and
+ vertical blanking data interfaces are also provided.
</para>
</chapter>
<chapter>
- <title>Radio Devices</title>
+ <title>Radio Devices</title>
<para>
- There are a wide variety of radio interfaces available for PC's, and these
- are generally very simple to program. The biggest problem with supporting
- such devices is normally extracting documentation from the vendor.
+ There are a wide variety of radio interfaces available for PC's, and these
+ are generally very simple to program. The biggest problem with supporting
+ such devices is normally extracting documentation from the vendor.
</para>
<para>
- The radio interface supports a simple set of control ioctls standardised
- across all radio and tv interfaces. It does not support read or write, which
- are used for video streams. The reason radio cards do not allow you to read
- the audio stream into an application is that without exception they provide
- a connection on to a soundcard. Soundcards can be used to read the radio
- data just fine.
+ The radio interface supports a simple set of control ioctls standardised
+ across all radio and tv interfaces. It does not support read or write, which
+ are used for video streams. The reason radio cards do not allow you to read
+ the audio stream into an application is that without exception they provide
+ a connection on to a soundcard. Soundcards can be used to read the radio
+ data just fine.
</para>
<sect1 id="registerradio">
<title>Registering Radio Devices</title>
<para>
- The Video4linux core provides an interface for registering devices. The
- first step in writing our radio card driver is to register it.
+ The Video4linux core provides an interface for registering devices. The
+ first step in writing our radio card driver is to register it.
</para>
<programlisting>
static struct video_device my_radio
{
- "My radio",
- VID_TYPE_TUNER,
- VID_HARDWARE_MYRADIO,
- radio_open.
- radio_close,
- NULL, /* no read */
- NULL, /* no write */
- NULL, /* no poll */
- radio_ioctl,
- NULL, /* no special init function */
- NULL /* no private data */
+ "My radio",
+ VID_TYPE_TUNER,
+ VID_HARDWARE_MYRADIO,
+ radio_open.
+ radio_close,
+ NULL, /* no read */
+ NULL, /* no write */
+ NULL, /* no poll */
+ radio_ioctl,
+ NULL, /* no special init function */
+ NULL /* no private data */
};
</programlisting>
<para>
- This declares our video4linux device driver interface. The VID_TYPE_ value
- defines what kind of an interface we are, and defines basic capabilities.
+ This declares our video4linux device driver interface. The VID_TYPE_ value
+ defines what kind of an interface we are, and defines basic capabilities.
</para>
<para>
- The only defined value relevant for a radio card is VID_TYPE_TUNER which
- indicates that the device can be tuned. Clearly our radio is going to have some
- way to change channel so it is tuneable.
+ The only defined value relevant for a radio card is VID_TYPE_TUNER which
+ indicates that the device can be tuned. Clearly our radio is going to have some
+ way to change channel so it is tuneable.
</para>
<para>
- The VID_HARDWARE_ types are unique to each device. Numbers are assigned by
- alan@redhat.com when device drivers are going to be released. Until then you
- can pull a suitably large number out of your hat and use it. 10000 should be
- safe for a very long time even allowing for the huge number of vendors
- making new and different radio cards at the moment.
+ The VID_HARDWARE_ types are unique to each device. Numbers are assigned by
+ <email>alan@redhat.com</email> when device drivers are going to be released. Until then you
+ can pull a suitably large number out of your hat and use it. 10000 should be
+ safe for a very long time even allowing for the huge number of vendors
+ making new and different radio cards at the moment.
</para>
<para>
- We declare an open and close routine, but we do not need read or write,
- which are used to read and write video data to or from the card itself. As
- we have no read or write there is no poll function.
+ We declare an open and close routine, but we do not need read or write,
+ which are used to read and write video data to or from the card itself. As
+ we have no read or write there is no poll function.
</para>
<para>
- The private initialise function is run when the device is registered. In
- this driver we've already done all the work needed. The final pointer is a
- private data pointer that can be used by the device driver to attach and
- retrieve private data structures. We set this field "priv" to NULL for
- the moment.
+ The private initialise function is run when the device is registered. In
+ this driver we've already done all the work needed. The final pointer is a
+ private data pointer that can be used by the device driver to attach and
+ retrieve private data structures. We set this field "priv" to NULL for
+ the moment.
</para>
<para>
- Having the structure defined is all very well but we now need to register it
- with the kernel.
+ Having the structure defined is all very well but we now need to register it
+ with the kernel.
</para>
<programlisting>
int __init myradio_init(struct video_init *v)
{
- if(check_region(io, MY_IO_SIZE))
- {
- printk(KERN_ERR "myradio: port 0x%03X is in use.\n", io);
- return -EBUSY;
- }
-
- if(video_device_register(&my_radio, VFL_TYPE_RADIO)==-1)
- return -EINVAL;
- request_region(io, MY_IO_SIZE, "myradio");
- return 0;
+ if(check_region(io, MY_IO_SIZE))
+ {
+ printk(KERN_ERR
+ "myradio: port 0x%03X is in use.\n", io);
+ return -EBUSY;
+ }
+
+ if(video_device_register(&my_radio, VFL_TYPE_RADIO)==-1)
+ return -EINVAL;
+ request_region(io, MY_IO_SIZE, "myradio");
+ return 0;
}
</programlisting>
<para>
- The first stage of the initialisation, as is normally the case, is to check
- that the I/O space we are about to fiddle with doesn't belong to some other
- driver. If it is we leave well alone. If the user gives the address of the
- wrong device then we will spot this. These policies will generally avoid
- crashing the machine.
- </para>
- <para>
- Now we ask the Video4Linux layer to register the device for us. We hand it
- our carefully designed video_device structure and also tell it which group
- of devices we want it registered with. In this case VFL_TYPE_RADIO.
- </para>
- <para>
- The types available are
-
- VFL_TYPE_RADIO /dev/radio{n}
-
- Radio devices are assigned in this block. As with all of these
- selections the actual number assignment is done by the video layer
- accordijng to what is free.
-
- VFL_TYPE_GRABBER /dev/video{n}
-
- Video capture devices and also -- counter-intuitively for the name --
- hardware video playback devices such as MPEG2 cards.
-
- VFL_TYPE_VBI /dev/vbi{n}
-
- The VBI devices capture the hidden lines on a television picture
- that carry further information like closed caption data, teletext
- (primarily in Europe) and now Intercast and the ATVEC internet
- television encodings.
-
- VFL_TYPE_VTX /dev/vtx[n}
-
- VTX is 'Videotext' also known as 'Teletext'. This is a system for
- sending numbered, 40x25, mostly textual page images over the hidden
- lines. Unlike the /dev/vbi interfaces, this is for 'smart' decoder
- chips. (The use of the word smart here has to be taken in context,
- the smartest teletext chips are fairly dumb pieces of technology).
-
- </para>
- <para>
- We are most definitely a radio.
- </para>
- <para>
- Finally we allocate our I/O space so that nobody treads on us and return 0
- to signify general happiness with the state of the universe.
+ The first stage of the initialisation, as is normally the case, is to check
+ that the I/O space we are about to fiddle with doesn't belong to some other
+ driver. If it is we leave well alone. If the user gives the address of the
+ wrong device then we will spot this. These policies will generally avoid
+ crashing the machine.
+ </para>
+ <para>
+ Now we ask the Video4Linux layer to register the device for us. We hand it
+ our carefully designed video_device structure and also tell it which group
+ of devices we want it registered with. In this case VFL_TYPE_RADIO.
+ </para>
+ <para>
+ The types available are
+ </para>
+ <table frame=all><title>Device Types</title>
+ <tgroup cols=3 align=left>
+ <tbody>
+ <row>
+ <entry>VFL_TYPE_RADIO</><>/dev/radio{n}</><>
+
+ Radio devices are assigned in this block. As with all of these
+ selections the actual number assignment is done by the video layer
+ accordijng to what is free.</entry>
+ </row><row>
+ <entry>VFL_TYPE_GRABBER</><>/dev/video{n}</><>
+ Video capture devices and also -- counter-intuitively for the name --
+ hardware video playback devices such as MPEG2 cards.</entry>
+ </row><row>
+ <entry>VFL_TYPE_VBI</><>/dev/vbi{n}</><>
+ The VBI devices capture the hidden lines on a television picture
+ that carry further information like closed caption data, teletext
+ (primarily in Europe) and now Intercast and the ATVEC internet
+ television encodings.</entry>
+ </row><row>
+ <entry>VFL_TYPE_VTX</><>/dev/vtx[n}</><>
+ VTX is 'Videotext' also known as 'Teletext'. This is a system for
+ sending numbered, 40x25, mostly textual page images over the hidden
+ lines. Unlike the /dev/vbi interfaces, this is for 'smart' decoder
+ chips. (The use of the word smart here has to be taken in context,
+ the smartest teletext chips are fairly dumb pieces of technology).
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <para>
+ We are most definitely a radio.
+ </para>
+ <para>
+ Finally we allocate our I/O space so that nobody treads on us and return 0
+ to signify general happiness with the state of the universe.
</para>
</sect1>
<sect1 id="openradio">
<title>Opening And Closing The Radio</title>
<para>
- The functions we declared in our video_device are mostly very simple.
- Firstly we can drop in what is basically standard code for open and close.
+ The functions we declared in our video_device are mostly very simple.
+ Firstly we can drop in what is basically standard code for open and close.
</para>
<programlisting>
static int radio_open(stuct video_device *dev, int flags)
{
- if(users)
- return -EBUSY;
- users++;
- MOD_INC_USE_COUNT;
- return 0;
+ if(users)
+ return -EBUSY;
+ users++;
+ MOD_INC_USE_COUNT;
+ return 0;
}
</programlisting>
<para>
- At open time we need to do nothing but check if someone else is also using
- the radio card. If nobody is using it we make a note that we are using it,
- then we ensure that nobody unloads our driver on us.
+ At open time we need to do nothing but check if someone else is also using
+ the radio card. If nobody is using it we make a note that we are using it,
+ then we ensure that nobody unloads our driver on us.
</para>
<programlisting>
static int radio_close(struct video_device *dev)
{
- users--;
- MOD_DEC_USE_COUNT;
+ users--;
+ MOD_DEC_USE_COUNT;
}
</programlisting>
<para>
- At close time we simply need to reduce the user count and allow the module
- to become unloadable.
+ At close time we simply need to reduce the user count and allow the module
+ to become unloadable.
</para>
<para>
- If you are sharp you will have noticed neither the open nor the close
- routines attempt to reset or change the radio settings. This is intentional.
- It allows an application to set up the radio and exit. It avoids a user
- having to leave an application running all the time just to listen to the
- radio.
+ If you are sharp you will have noticed neither the open nor the close
+ routines attempt to reset or change the radio settings. This is intentional.
+ It allows an application to set up the radio and exit. It avoids a user
+ having to leave an application running all the time just to listen to the
+ radio.
</para>
</sect1>
<sect1 id="ioctlradio">
<title>The Ioctl Interface</title>
<para>
- This leaves the ioctl routine, without which the driver will not be
- terribly useful to anyone.
+ This leaves the ioctl routine, without which the driver will not be
+ terribly useful to anyone.
</para>
<programlisting>
static int radio_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
{
- switch(cmd)
- {
- case VIDIOCGCAP:
- {
- struct video_capability v;
- v.type = VID_TYPE_TUNER;
- v.channels = 1;
- v.audios = 1;
- v.maxwidth = 0;
- v.minwidth = 0;
- v.maxheight = 0;
- v.minheight = 0;
- strcpy(v.name, "My Radio");
- if(copy_to_user(arg, &v, sizeof(v)))
- return -EFAULT;
- return 0;
- }
+ switch(cmd)
+ {
+ case VIDIOCGCAP:
+ {
+ struct video_capability v;
+ v.type = VID_TYPE_TUNER;
+ v.channels = 1;
+ v.audios = 1;
+ v.maxwidth = 0;
+ v.minwidth = 0;
+ v.maxheight = 0;
+ v.minheight = 0;
+ strcpy(v.name, "My Radio");
+ if(copy_to_user(arg, &v, sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
</programlisting>
<para>
- VIDIOCGCAP is the first ioctl all video4linux devices must support. It
- allows the applications to find out what sort of a card they have found and
- to figure out what they want to do about it. The fields in the structure are
- </para>
- <para>
- name The device text name. This is intended for the user.
-
- channels The number of different channels you can tune on
- this card. It could even by zero for a card that has
- no tuning capability. For our simple FM radio it is 1.
- An AM/FM radio would report 2.
-
- audios The number of audio inputs on this device. For our
- radio there is only one audio input.
-
- minwidth, The smallest size the card is capable of capturing
- minheight images in. We set these to zero. Radios do not
- capture pictures
-
- maxwidth, The largest image size the card is capable of
- maxheight capturing. For our radio we report 0.
-
-
- type This reports the capabilities of the device, and
- matches the field we filled in in the struct
- video_device when registering.
-
- </para>
- <para>
- Having filled in the fields, we use copy_to_user to copy the structure into
- the users buffer. If the copy fails we return an EFAULT to the application
- so that it knows it tried to feed us garbage.
- </para>
- <para>
- The next pair of ioctl operations select which tuner is to be used and let
- the application find the tuner properties. We have only a single FM band
- tuner in our example device.
+ VIDIOCGCAP is the first ioctl all video4linux devices must support. It
+ allows the applications to find out what sort of a card they have found and
+ to figure out what they want to do about it. The fields in the structure are
+ </para>
+ <table frame=all><title>struct video_capability fields</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+ <entry>name</><>The device text name. This is intended for the user.</>
+ </row><row>
+ <entry>channels</><>The number of different channels you can tune on
+ this card. It could even by zero for a card that has
+ no tuning capability. For our simple FM radio it is 1.
+ An AM/FM radio would report 2.</entry>
+ </row><row>
+ <entry>audios</><>The number of audio inputs on this device. For our
+ radio there is only one audio input.</entry>
+ </row><row>
+ <entry>minwidth,minheight</><>The smallest size the card is capable of capturing
+ images in. We set these to zero. Radios do not
+ capture pictures</entry>
+ </row><row>
+ <entry>maxwidth,maxheight</><>The largest image size the card is capable of
+ capturing. For our radio we report 0.
+ </entry>
+ </row><row>
+ <entry>type</><>This reports the capabilities of the device, and
+ matches the field we filled in in the struct
+ video_device when registering.</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <para>
+ Having filled in the fields, we use copy_to_user to copy the structure into
+ the users buffer. If the copy fails we return an EFAULT to the application
+ so that it knows it tried to feed us garbage.
+ </para>
+ <para>
+ The next pair of ioctl operations select which tuner is to be used and let
+ the application find the tuner properties. We have only a single FM band
+ tuner in our example device.
</para>
<programlisting>
- case VIDIOCGTUNER:
- {
- struct video_tuner v;
- if(copy_from_user(&v, arg, sizeof(v))!=0)
- return -EFAULT;
- if(v.tuner)
- return -EINVAL;
- v.rangelow=(87*16000);
- v.rangehigh=(108*16000);
- v.flags = VIDEO_TUNER_LOW;
- v.mode = VIDEO_MODE_AUTO;
- v.signal = 0xFFFF;
- strcpy(v.name, "FM");
- if(copy_to_user(&v, arg, sizeof(v))!=0)
- return -EFAULT;
- return 0;
- }
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner v;
+ if(copy_from_user(&v, arg, sizeof(v))!=0)
+ return -EFAULT;
+ if(v.tuner)
+ return -EINVAL;
+ v.rangelow=(87*16000);
+ v.rangehigh=(108*16000);
+ v.flags = VIDEO_TUNER_LOW;
+ v.mode = VIDEO_MODE_AUTO;
+ v.signal = 0xFFFF;
+ strcpy(v.name, "FM");
+ if(copy_to_user(&v, arg, sizeof(v))!=0)
+ return -EFAULT;
+ return 0;
+ }
</programlisting>
<para>
- The VIDIOCGTUNER ioctl allows applications to query a tuner. The application
- sets the tuner field to the tuner number it wishes to query. The query does
- not change the tuner that is being used, it merely enquires about the tuner
- in question.
- </para>
- <para>
- We have exactly one tuner so after copying the user buffer to our temporary
- structure we complain if they asked for a tuner other than tuner 0.
- </para>
- <para>
- The video_tuner structure has the following fields
- </para>
- <para>
- int tuner The number of the tuner in question
- char name[32] A text description of this tuner. "FM" will do fine.
- This is intended for the application.
- u32 flags Information on the tuners capabilities
-
- VIDEO_TUNER_PAL
- A PAL TV tuner
- VIDEO_TUNER_NTSC
- An NTSC (US) TV tuner
- VIDEO_TUNER_SECAM
- A SECAM (Frenc) TV tuner
- VIDEO_TUNER_LOW
- The tuner frequency is scaled in 1/16th of a KHz
- steps. If not it is in 1/16th of a MHz steps
- VIDEO_TUNER_NORM
- The tuner can set its format
- VIDEO_TUNER_STEREO_ON
- The tuner is currently receiving a stereo signal
-
- u16 mode The current reception mode
-
- VIDEO_MODE_PAL The TV formats
- VIDEO_MODE_NTSC
- VIDEO_MODE_SECAM
- VIDEO_MODE_AUTO A device that does not need to do
- TV format switching
-
- u16 signal The signal strength scaled between 0 and 65535. If
- a device cannot tell the signal strength it should
- report 65535. Many simple cards contain only a
- signal/no signal bit. Such cards will report either
- 0 or 65535.
-
- u32 rangelow, rangehigh
-
- The range of frequencies supported by the radio
- or TV. It is scaled according to the VIDEO_TUNER_LOW
- flag.
-
- </para>
- <para>
- The settings for the radio card are thus fairly simple. We report that we
- are a tuner called "FM" for FM radio. In order to get the best tuning
- resolution we report VIDEO_TUNER_LOW and select tuning to 1/16th of KHz. Its
- unlikely our card can do that resolution but it is a fair bet the card can
- do better than 1/16th of a MHz. VIDEO_TUNER_LOW is appropriate to almost all
- radio usage.
- </para>
- <para>
- We report that the tuner automatically handles deciding what format it is
- receiving - true enough as it only handles FM radio. Our example card is
- also incapable of detecting stereo or signal strengths so it reports a
- strength of 0xFFFF (maximum) and no stereo detected.
- </para>
- <para>
- To finish off we set the range that can be tuned to be 87-108Mhz, the normal
- FM broadcast radio range. It is important to find out what the card is
- actually capable of tuning. It is easy enough to simply use the FM broadcast
- range. Unfortunately if you do this you will discover the FM broadcast
- ranges in the USA, Europe and Japan are all subtly different and some users
- cannot receive all the stations they wish.
- </para>
- <para>
- The application also needs to be able to set the tuner it wishes to use. In
- our case, with a single tuner this is rather simple to arrange.
+ The VIDIOCGTUNER ioctl allows applications to query a tuner. The application
+ sets the tuner field to the tuner number it wishes to query. The query does
+ not change the tuner that is being used, it merely enquires about the tuner
+ in question.
+ </para>
+ <para>
+ We have exactly one tuner so after copying the user buffer to our temporary
+ structure we complain if they asked for a tuner other than tuner 0.
+ </para>
+ <para>
+ The video_tuner structure has the following fields
+ </para>
+ <table frame=all><title>struct video_tuner fields</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+ <entry>int tuner</><entry>The number of the tuner in question</entry>
+ </row><row>
+ <entry>char name[32]</><entry>A text description of this tuner. "FM" will do fine.
+ This is intended for the application.</entry>
+ </row><row>
+ <entry>u32 flags</>
+ <entry>Tuner capability flags</entry>
+ </row>
+ <row>
+ <entry>u16 mode</><entry>The current reception mode</entry>
+
+ </row><row>
+ <entry>u16 signal</><entry>The signal strength scaled between 0 and 65535. If
+ a device cannot tell the signal strength it should
+ report 65535. Many simple cards contain only a
+ signal/no signal bit. Such cards will report either
+ 0 or 65535.</entry>
+
+ </row><row>
+ <entry>u32 rangelow, rangehigh</><entry>
+ The range of frequencies supported by the radio
+ or TV. It is scaled according to the VIDEO_TUNER_LOW
+ flag.</entry>
+
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame=all><title>struct video_tuner flags</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+ <entry>VIDEO_TUNER_PAL</><entry>A PAL TV tuner</entry>
+ </row><row>
+ <entry>VIDEO_TUNER_NTSC</><entry>An NTSC (US) TV tuner</entry>
+ </row><row>
+ <entry>VIDEO_TUNER_SECAM</><entry>A SECAM (French) TV tuner</entry>
+ </row><row>
+ <entry>VIDEO_TUNER_LOW</><>
+ The tuner frequency is scaled in 1/16th of a KHz
+ steps. If not it is in 1/16th of a MHz steps
+ </entry>
+ </row><row>
+ <entry>VIDEO_TUNER_NORM</><entry>The tuner can set its format</entry>
+ </row><row>
+ <entry>VIDEO_TUNER_STEREO_ON</><entry>The tuner is currently receiving a stereo signal</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame=all><title>struct video_tuner modes</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+ <entry>VIDEO_MODE_PAL</><>PAL Format</entry>
+ </row><row>
+ <entry>VIDEO_MODE_NTSC</><>NTSC Format (USA)</entry>
+ </row><row>
+ <entry>VIDEO_MODE_SECAM</><>French Format</entry>
+ </row><row>
+ <entry>VIDEO_MODE_AUTO</><>A device that does not need to do
+ TV format switching</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <para>
+ The settings for the radio card are thus fairly simple. We report that we
+ are a tuner called "FM" for FM radio. In order to get the best tuning
+ resolution we report VIDEO_TUNER_LOW and select tuning to 1/16th of KHz. Its
+ unlikely our card can do that resolution but it is a fair bet the card can
+ do better than 1/16th of a MHz. VIDEO_TUNER_LOW is appropriate to almost all
+ radio usage.
+ </para>
+ <para>
+ We report that the tuner automatically handles deciding what format it is
+ receiving - true enough as it only handles FM radio. Our example card is
+ also incapable of detecting stereo or signal strengths so it reports a
+ strength of 0xFFFF (maximum) and no stereo detected.
+ </para>
+ <para>
+ To finish off we set the range that can be tuned to be 87-108Mhz, the normal
+ FM broadcast radio range. It is important to find out what the card is
+ actually capable of tuning. It is easy enough to simply use the FM broadcast
+ range. Unfortunately if you do this you will discover the FM broadcast
+ ranges in the USA, Europe and Japan are all subtly different and some users
+ cannot receive all the stations they wish.
+ </para>
+ <para>
+ The application also needs to be able to set the tuner it wishes to use. In
+ our case, with a single tuner this is rather simple to arrange.
</para>
<programlisting>
- case VIDIOCSTUNER:
- {
- struct video_tuner v;
- if(copy_from_user(&v, arg, sizeof(v)))
- return -EFAULT;
- if(v.tuner != 0)
- return -EINVAL;
- return 0;
- }
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner v;
+ if(copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ if(v.tuner != 0)
+ return -EINVAL;
+ return 0;
+ }
</programlisting>
<para>
- We copy the user supplied structure into kernel memory so we can examine it.
- If the user has selected a tuner other than zero we reject the request. If
- they wanted tuner 0 then, suprisingly enough, that is the current tuner already.
+ We copy the user supplied structure into kernel memory so we can examine it.
+ If the user has selected a tuner other than zero we reject the request. If
+ they wanted tuner 0 then, suprisingly enough, that is the current tuner already.
</para>
<para>
- The next two ioctls we need to provide are to get and set the frequency of
- the radio. These both use an unsigned long argument which is the frequency.
- The scale of the frequency depends on the VIDEO_TUNER_LOW flag as I
- mentioned earlier on. Since we have VIDEO_TUNER_LOW set this will be in
- 1/16ths of a KHz.
+ The next two ioctls we need to provide are to get and set the frequency of
+ the radio. These both use an unsigned long argument which is the frequency.
+ The scale of the frequency depends on the VIDEO_TUNER_LOW flag as I
+ mentioned earlier on. Since we have VIDEO_TUNER_LOW set this will be in
+ 1/16ths of a KHz.
</para>
<programlisting>
- case VIDIOCGFREQ:
- if(copy_to_user(arg, &current_freq,
- sizeof(unsigned long))
- return -EFAULT;
- return 0;
+ case VIDIOCGFREQ:
+ if(copy_to_user(arg, &current_freq,
+ sizeof(unsigned long))
+ return -EFAULT;
+ return 0;
</programlisting>
<para>
- Querying the frequency in our case is relatively simple. Our radio card is
- too dumb to let us query the signal strength so we remember our setting if
- we know it. All we have to do is copy it to the user.
+ Querying the frequency in our case is relatively simple. Our radio card is
+ too dumb to let us query the signal strength so we remember our setting if
+ we know it. All we have to do is copy it to the user.
</para>
<programlisting>
- case VIDIOCSFREQ:
- {
- u32 freq;
- if(copy_from_user(arg, &freq,
- sizeof(unsigned long))!=0)
- return -EFAULT;
- if(hardware_set_freq(freq)<0)
- return -EINVAL;
- current_freq = freq;
- return 0;
- }
+ case VIDIOCSFREQ:
+ {
+ u32 freq;
+ if(copy_from_user(arg, &freq,
+ sizeof(unsigned long))!=0)
+ return -EFAULT;
+ if(hardware_set_freq(freq)<0)
+ return -EINVAL;
+ current_freq = freq;
+ return 0;
+ }
</programlisting>
<para>
- Setting the frequency is a little more complex. We begin by copying the
- desired frequency into kernel space. Next we call a hardware specific routine
- to set the radio up. This might be as simple as some scaling and a few
- writes to an I/O port. For most radio cards it turns out a good deal more
- complicated and may involve programming things like a phase locked loop on
- the card. This is what documentation is for.
+ Setting the frequency is a little more complex. We begin by copying the
+ desired frequency into kernel space. Next we call a hardware specific routine
+ to set the radio up. This might be as simple as some scaling and a few
+ writes to an I/O port. For most radio cards it turns out a good deal more
+ complicated and may involve programming things like a phase locked loop on
+ the card. This is what documentation is for.
</para>
<para>
- The final set of operations we need to provide for our radio are the
- volume controls. Not all radio cards can even do volume control. After all
- there is a perfectly good volume control on the sound card. We will assume
- our radio card has a simple 4 step volume control.
+ The final set of operations we need to provide for our radio are the
+ volume controls. Not all radio cards can even do volume control. After all
+ there is a perfectly good volume control on the sound card. We will assume
+ our radio card has a simple 4 step volume control.
</para>
<para>
- There are two ioctls with audio we need to support
+ There are two ioctls with audio we need to support
</para>
<programlisting>
static int current_volume=0;
- case VIDIOCGAUDIO:
- {
- struct video_audio v;
- if(copy_from_user(&v, arg, sizeof(v)))
- return -EFAULT;
- if(v.audio != 0)
- return -EINVAL;
- v.volume = 16384*current_volume;
- v.step = 16384;
- strcpy(v.name, "Radio");
- v.mode = VIDEO_SOUND_MONO;
- v.balance = 0;
- v.base = 0;
- v.treble = 0;
-
- if(copy_to_user(arg. &v, sizeof(v)))
- return -EFAULT;
- return 0;
- }
+ case VIDIOCGAUDIO:
+ {
+ struct video_audio v;
+ if(copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ if(v.audio != 0)
+ return -EINVAL;
+ v.volume = 16384*current_volume;
+ v.step = 16384;
+ strcpy(v.name, "Radio");
+ v.mode = VIDEO_SOUND_MONO;
+ v.balance = 0;
+ v.base = 0;
+ v.treble = 0;
+
+ if(copy_to_user(arg. &v, sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
</programlisting>
<para>
- Much like the tuner we start by copying the user structure into kernel
- space. Again we check if the user has asked for a valid audio input. We have
- only input 0 and we punt if they ask for another input.
- </para>
- <para>
- Then we fill in the video_audio structure. This has the following format
- </para>
- <para>
-
- audio The input the user wishes to query
- volume The volume setting on a scale of 0-65535
- base The base level on a scale of 0-65535
- treble The treble level on a scale of 0-65535
-
- flags The features this audio device supports
-
- VIDEO_AUDIO_MUTE The audio is currently muted. We
- could fake this in our driver but we
- choose not to bother.
- VIDEO_AUDIO_MUTABLE The input has a mute option
- VIDEO_AUDIO_TREBLE The input has a treble control
- VIDEO_AUDIO_BASS The input has a base control
-
- name A text name to display to the user. We picked
- "Radio" as it explains things quite nicely.
-
- mode The current reception mode for the audio
-
- VIDEO_SOUND_MONO Mono sound
- VIDEO_SOUND_STEREO Stereo sound
- VIDEO_SOUND_LANG1 Alternative language 1 (TV specific)
- VIDEO_SOUND_LANG2 Alternative language 2 (TV specific)
-
- We report MONO because our card is too stupid to know if it is in
- mono or stereo.
-
- balance The stereo balance on a scale of 0-65535, 32768 is
- middle.
-
- step The step by which the volume control jumps. This is
- used to help make it easy for applications to set
- slider behaviour.
-
- </para>
- <para>
- Having filled in the structure we copy it back to user space.
- </para>
- <para>
- The VIDIOCSAUDIO ioctl allows the user to set the audio parameters in the
- video_audio stucture. The driver does its best to honour the request.
+ Much like the tuner we start by copying the user structure into kernel
+ space. Again we check if the user has asked for a valid audio input. We have
+ only input 0 and we punt if they ask for another input.
+ </para>
+ <para>
+ Then we fill in the video_audio structure. This has the following format
+ </para>
+ <table frame=all><title>struct video_audio fields</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+ <entry>audio</><>The input the user wishes to query</>
+ </row><row>
+ <entry>volume</><>The volume setting on a scale of 0-65535</>
+ </row><row>
+ <entry>base</><>The base level on a scale of 0-65535</>
+ </row><row>
+ <entry>treble</><>The treble level on a scale of 0-65535</>
+ </row><row>
+ <entry>flags</><>The features this audio device supports
+ </entry>
+ </row><row>
+ <entry>name</><>A text name to display to the user. We picked
+ "Radio" as it explains things quite nicely.</>
+ </row><row>
+ <entry>mode</><>The current reception mode for the audio
+
+ We report MONO because our card is too stupid to know if it is in
+ mono or stereo.
+ </entry>
+ </row><row>
+ <entry>balance</><>The stereo balance on a scale of 0-65535, 32768 is
+ middle.</>
+ </row><row>
+ <entry>step</><>The step by which the volume control jumps. This is
+ used to help make it easy for applications to set
+ slider behaviour.</>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame=all><title>struct video_audio flags</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+ <entry>VIDEO_AUDIO_MUTE</><>The audio is currently muted. We
+ could fake this in our driver but we
+ choose not to bother.</entry>
+ </row><row>
+ <entry>VIDEO_AUDIO_MUTABLE</><>The input has a mute option</entry>
+ </row><row>
+ <entry>VIDEO_AUDIO_TREBLE</><>The input has a treble control</entry>
+ </row><row>
+ <entry>VIDEO_AUDIO_BASS</><>The input has a base control</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame=all><title>struct video_audio modes</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+ <entry>VIDEO_SOUND_MONO</><>Mono sound</entry>
+ </row><row>
+ <entry>VIDEO_SOUND_STEREO</><>Stereo sound</entry>
+ </row><row>
+ <entry>VIDEO_SOUND_LANG1</><>Alternative language 1 (TV specific)</entry>
+ </row><row>
+ <entry>VIDEO_SOUND_LANG2</><>Alternative language 2 (TV specific)</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <para>
+ Having filled in the structure we copy it back to user space.
+ </para>
+ <para>
+ The VIDIOCSAUDIO ioctl allows the user to set the audio parameters in the
+ video_audio stucture. The driver does its best to honour the request.
</para>
<programlisting>
- case VIDIOCSAUDIO:
- {
- struct video_audio v;
- if(copy_from_user(&v, arg, sizeof(v)))
- return -EFAULT;
- if(v.audio)
- return -EINVAL;
- current_volume = v/16384;
- hardware_set_volume(current_volume);
- return 0;
- }
+ case VIDIOCSAUDIO:
+ {
+ struct video_audio v;
+ if(copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ if(v.audio)
+ return -EINVAL;
+ current_volume = v/16384;
+ hardware_set_volume(current_volume);
+ return 0;
+ }
</programlisting>
<para>
- In our case there is very little that the user can set. The volume is
- basically the limit. Note that we could pretend to have a mute feature
- by rewriting this to
+ In our case there is very little that the user can set. The volume is
+ basically the limit. Note that we could pretend to have a mute feature
+ by rewriting this to
</para>
<programlisting>
- case VIDIOCSAUDIO:
- {
- struct video_audio v;
- if(copy_from_user(&v, arg, sizeof(v)))
- return -EFAULT;
- if(v.audio)
- return -EINVAL;
- current_volume = v/16384;
- if(v.flags&VIDEO_AUDIO_MUTE)
- hardware_set_volume(0);
- else
- hardware_set_volume(current_volume);
- current_muted = v.flags&VIDEO_AUDIO_MUTE;
- return 0;
- }
+ case VIDIOCSAUDIO:
+ {
+ struct video_audio v;
+ if(copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ if(v.audio)
+ return -EINVAL;
+ current_volume = v/16384;
+ if(v.flags&VIDEO_AUDIO_MUTE)
+ hardware_set_volume(0);
+ else
+ hardware_set_volume(current_volume);
+ current_muted = v.flags &
+ VIDEO_AUDIO_MUTE;
+ return 0;
+ }
</programlisting>
<para>
- This with the corresponding changes to the VIDIOCGAUDIO code to report the
- state of the mute flag we save and to report the card has a mute function,
- will allow applications to use a mute facility with this card. It is
- questionable whether this is a good idea however. User applications can already
- fake this themselves and kernel space is precious.
+ This with the corresponding changes to the VIDIOCGAUDIO code to report the
+ state of the mute flag we save and to report the card has a mute function,
+ will allow applications to use a mute facility with this card. It is
+ questionable whether this is a good idea however. User applications can already
+ fake this themselves and kernel space is precious.
</para>
<para>
- We now have a working radio ioctl handler. So we just wrap up the function
+ We now have a working radio ioctl handler. So we just wrap up the function
</para>
<programlisting>
- }
- return -ENOIOCTLCMD;
+ }
+ return -ENOIOCTLCMD;
}
</programlisting>
<para>
- and pass the Video4Linux layer back an error so that it knows we did not
- understand the request we got passed.
+ and pass the Video4Linux layer back an error so that it knows we did not
+ understand the request we got passed.
</para>
</sect1>
<sect1 id="modradio">
<title>Module Wrapper</title>
<para>
- Finally we add in the usual module wrapping and the driver is done.
+ Finally we add in the usual module wrapping and the driver is done.
</para>
<programlisting>
int init_module(void)
{
- if(io==-1)
- {
- printk(KERN_ERR "You must set an I/O address with io=0x???\n");
- return -EINVAL;
- }
- return myradio_init(NULL);
+ if(io==-1)
+ {
+ printk(KERN_ERR
+ "You must set an I/O address with io=0x???\n");
+ return -EINVAL;
+ }
+ return myradio_init(NULL);
}
void cleanup_module(void)
{
- video_unregister_device(&my_radio);
- release_region(io, MY_IO_SIZE);
+ video_unregister_device(&my_radio);
+ release_region(io, MY_IO_SIZE);
}
#endif
</programlisting>
<para>
- In this example we set the IO base by default if the driver is compiled into
- the kernel where you cannot pass a parameter. For the module we require the
- user sets the parameter. We set io to a nonsense port (-1) so that we can
- tell if the user supplied an io parameter or not.
+ In this example we set the IO base by default if the driver is compiled into
+ the kernel where you cannot pass a parameter. For the module we require the
+ user sets the parameter. We set io to a nonsense port (-1) so that we can
+ tell if the user supplied an io parameter or not.
</para>
<para>
- We use MODULE_ defines to give an author for the card driver and a
- description. We also use them to declare that io is an integer and it is the
- address of the card.
+ We use MODULE_ defines to give an author for the card driver and a
+ description. We also use them to declare that io is an integer and it is the
+ address of the card.
</para>
<para>
- The clean-up routine unregisters the video_device we registered, and frees
- up the I/O space. Note that the unregister takes the actual video_device
- structure as its argument. Unlike the file operations structure which can be
- shared by all instances of a device a video_device structure as an actual
- instance of the device. If you are registering multiple radio devices you
- need to fill in one structure per device (most likely by setting up a
- template and copying it to each of the actual device structures).
+ The clean-up routine unregisters the video_device we registered, and frees
+ up the I/O space. Note that the unregister takes the actual video_device
+ structure as its argument. Unlike the file operations structure which can be
+ shared by all instances of a device a video_device structure as an actual
+ instance of the device. If you are registering multiple radio devices you
+ need to fill in one structure per device (most likely by setting up a
+ template and copying it to each of the actual device structures).
</para>
</sect1>
</chapter>
<chapter>
- <title>Video Capture Devices</title>
+ <title>Video Capture Devices</title>
<sect1 id="introvid">
<title>Video Capture Device Types</title>
<para>
- The video capture devices share the same interfaces as radio devices. In
- order to explain the video capture interface I will use the example of a
- camera that has no tuners or audio input. This keeps the example relatively
- clean. To get both combine the two driver examples.
+ The video capture devices share the same interfaces as radio devices. In
+ order to explain the video capture interface I will use the example of a
+ camera that has no tuners or audio input. This keeps the example relatively
+ clean. To get both combine the two driver examples.
</para>
<para>
- Video capture devices divide into four categories. A little technology
- backgrounder. Full motion video even at television resolution (which is
- actually fairly low) is pretty resource-intensive. You are continually
- passing megabytes of data every second from the capture card to the display.
- several alternative approaches have emerged because copying this through the
- processor and the user program is a particularly bad idea .
+ Video capture devices divide into four categories. A little technology
+ backgrounder. Full motion video even at television resolution (which is
+ actually fairly low) is pretty resource-intensive. You are continually
+ passing megabytes of data every second from the capture card to the display.
+ several alternative approaches have emerged because copying this through the
+ processor and the user program is a particularly bad idea .
</para>
<para>
- The first is to add the television image onto the video output directly.
- This is also how some 3D cards work. These basic cards can generally drop the
- video into any chosen rectangle of the display. Cards like this, which
- include most mpeg1 cards that used the feature connector, aren't very
- friendly in a windowing environment. They don't understand windows or
- clipping. The video window is always on the top of the display.
+ The first is to add the television image onto the video output directly.
+ This is also how some 3D cards work. These basic cards can generally drop the
+ video into any chosen rectangle of the display. Cards like this, which
+ include most mpeg1 cards that used the feature connector, aren't very
+ friendly in a windowing environment. They don't understand windows or
+ clipping. The video window is always on the top of the display.
</para>
<para>
- Chroma keying is a technique used by cards to get around this. It is an old
- television mixing trick where you mark all the areas you wish to replace
- with a single clear colour that isn't used in the image - TV people use an
- incredibly bright blue while computing people often use a paticularly
- virulent purple. Bright blue occurs on the desktop. Anyone with virulent
- purple windows has another problem besides their TV overlay.
+ Chroma keying is a technique used by cards to get around this. It is an old
+ television mixing trick where you mark all the areas you wish to replace
+ with a single clear colour that isn't used in the image - TV people use an
+ incredibly bright blue while computing people often use a paticularly
+ virulent purple. Bright blue occurs on the desktop. Anyone with virulent
+ purple windows has another problem besides their TV overlay.
</para>
<para>
- The third approach is to copy the data from the capture card to the video
- card, but to do it directly across the PCI bus. This relieves the processor
- from doing the work but does require some smartness on the part of the video
- capture chip, as well as a suitable video card. Programming this kind of
- card and more so debugging it can be extremely tricky. There are some quite
- complicated interactions with the display and you may also have to cope with
- various chipset bugs that show up when PCI cards start talking to each
- other.
+ The third approach is to copy the data from the capture card to the video
+ card, but to do it directly across the PCI bus. This relieves the processor
+ from doing the work but does require some smartness on the part of the video
+ capture chip, as well as a suitable video card. Programming this kind of
+ card and more so debugging it can be extremely tricky. There are some quite
+ complicated interactions with the display and you may also have to cope with
+ various chipset bugs that show up when PCI cards start talking to each
+ other.
</para>
<para>
- To keep our example fairly simple we will assume a card that supports
- overlaying a flat rectangular image onto the frame buffer output, and which
- can also capture stuff into processor memory.
+ To keep our example fairly simple we will assume a card that supports
+ overlaying a flat rectangular image onto the frame buffer output, and which
+ can also capture stuff into processor memory.
</para>
</sect1>
<sect1 id="regvid">
<title>Registering Video Capture Devices</title>
<para>
- This time we need to add more functions for our camera device.
+ This time we need to add more functions for our camera device.
</para>
<programlisting>
static struct video_device my_camera
{
- "My Camera",
- VID_TYPE_OVERLAY|VID_TYPE_SCALES|VID_TYPE_CAPTURE|VID_TYPE_CHROMAKEY,
- VID_HARDWARE_MYCAMERA,
- camera_open.
- camera_close,
- camera_read, /* no read */
- NULL, /* no write */
- camera_poll, /* no poll */
- camera_ioctl,
- NULL, /* no special init function */
- NULL /* no private data */
+ "My Camera",
+ VID_TYPE_OVERLAY|VID_TYPE_SCALES|\
+ VID_TYPE_CAPTURE|VID_TYPE_CHROMAKEY,
+ VID_HARDWARE_MYCAMERA,
+ camera_open.
+ camera_close,
+ camera_read, /* no read */
+ NULL, /* no write */
+ camera_poll, /* no poll */
+ camera_ioctl,
+ NULL, /* no special init function */
+ NULL /* no private data */
};
</programlisting>
<para>
- We need a read() function which is used for capturing data from
- the card, and we need a poll function so that a driver can wait for the next
- frame to be captured.
- </para>
- <para>
- We use the extra video capability flags that did not apply to the
- radio interface. The video related flags are
-
-VID_TYPE_CAPTURE We support image capture
-VID_TYPE_TELETEXT A teletext capture device (vbi{n])
-VID_TYPE_OVERLAY The image can be directly overlaid onto the
- frame buffer
-VID_TYPE_CHROMAKEY Chromakey can be used to select which parts
- of the image to display
-VID_TYPE_CLIPPING It is possible to give the board a list of
- rectangles to draw around.
-VID_TYPE_FRAMERAM The video capture goes into the video memory
- and actually changes it. Applications need
- to know this so they can clean up after the
- card
-VID_TYPE_SCALES The image can be scaled to various sizes,
- rather than being a single fixed size.
-VID_TYPE_MONOCHROME The capture will be monochrome. This isn't a
- complete answer to the question since a mono
- camera on a colour capture card will still
- produce mono output.
-VID_TYPE_SUBCAPTURE The card allows only part of its field of
- view to be captured. This enables
- applications to avoid copying all of a large
- image into memory when only some section is
- relevant.
-
- We set VID_TYPE_CAPTURE so that we are seen as a capture card,
- VID_TYPE_CHROMAKEY so the application knows it is time to draw in virulent
- purple, and VID_TYPE_SCALES because we can be resized.
- </para>
- <para>
- Our setup is fairly similar. This time we also want an interrupt line
- for the 'frame captured' signal. Not all cards have this so some of them
- cannot handle poll().
+ We need a read() function which is used for capturing data from
+ the card, and we need a poll function so that a driver can wait for the next
+ frame to be captured.
+ </para>
+ <para>
+ We use the extra video capability flags that did not apply to the
+ radio interface. The video related flags are
+ </para>
+ <table frame=all><title>Capture Capabilities</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+<entry>VID_TYPE_CAPTURE</><>We support image capture</>
+</row><row>
+<entry>VID_TYPE_TELETEXT</><>A teletext capture device (vbi{n])</>
+</row><row>
+<entry>VID_TYPE_OVERLAY</><>The image can be directly overlaid onto the
+ frame buffer</>
+</row><row>
+<entry>VID_TYPE_CHROMAKEY</><>Chromakey can be used to select which parts
+ of the image to display</>
+</row><row>
+<entry>VID_TYPE_CLIPPING</><>It is possible to give the board a list of
+ rectangles to draw around. </>
+</row><row>
+<entry>VID_TYPE_FRAMERAM</><>The video capture goes into the video memory
+ and actually changes it. Applications need
+ to know this so they can clean up after the
+ card</>
+</row><row>
+<entry>VID_TYPE_SCALES</><>The image can be scaled to various sizes,
+ rather than being a single fixed size.</>
+</row><row>
+<entry>VID_TYPE_MONOCHROME</><>The capture will be monochrome. This isn't a
+ complete answer to the question since a mono
+ camera on a colour capture card will still
+ produce mono output.</>
+</row><row>
+<entry>VID_TYPE_SUBCAPTURE</><>The card allows only part of its field of
+ view to be captured. This enables
+ applications to avoid copying all of a large
+ image into memory when only some section is
+ relevant.</>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <para>
+ We set VID_TYPE_CAPTURE so that we are seen as a capture card,
+ VID_TYPE_CHROMAKEY so the application knows it is time to draw in virulent
+ purple, and VID_TYPE_SCALES because we can be resized.
+ </para>
+ <para>
+ Our setup is fairly similar. This time we also want an interrupt line
+ for the 'frame captured' signal. Not all cards have this so some of them
+ cannot handle poll().
</para>
<programlisting>
int __init mycamera_init(struct video_init *v)
{
- if(check_region(io, MY_IO_SIZE))
- {
- printk(KERN_ERR "mycamera: port 0x%03X is in use.\n", io);
- return -EBUSY;
- }
-
- if(video_device_register(&my_camera, VFL_TYPE_GRABBER)==-1)
- return -EINVAL;
- request_region(io, MY_IO_SIZE, "mycamera");
- return 0;
+ if(check_region(io, MY_IO_SIZE))
+ {
+ printk(KERN_ERR
+ "mycamera: port 0x%03X is in use.\n", io);
+ return -EBUSY;
+ }
+
+ if(video_device_register(&my_camera,
+ VFL_TYPE_GRABBER)==-1)
+ return -EINVAL;
+ request_region(io, MY_IO_SIZE, "mycamera");
+ return 0;
}
</programlisting>
<para>
- This is little changed from the needs of the radio card. We specify
- VFL_TYPE_GRABBER this time as we want to be allocated a /dev/video name.
+ This is little changed from the needs of the radio card. We specify
+ VFL_TYPE_GRABBER this time as we want to be allocated a /dev/video name.
</para>
</sect1>
<sect1 id="opvid">
static int camera_open(stuct video_device *dev, int flags)
{
- if(users)
- return -EBUSY;
- if(request_irq(irq, camera_irq, 0, "camera", dev)<0)
- return -EBUSY;
- users++;
- MOD_INC_USE_COUNT;
- return 0;
+ if(users)
+ return -EBUSY;
+ if(request_irq(irq, camera_irq, 0, "camera", dev)<0)
+ return -EBUSY;
+ users++;
+ MOD_INC_USE_COUNT;
+ return 0;
}
static int camera_close(struct video_device *dev)
{
- users--;
- free_irq(irq, dev);
- MOD_DEC_USE_COUNT;
+ users--;
+ free_irq(irq, dev);
+ MOD_DEC_USE_COUNT;
}
</programlisting>
<para>
- The open and close routines are also quite similar. The only real change is
- that we now request an interrupt for the camera device interrupt line. If we
- cannot get the interrupt we report EBUSY to the application and give up.
+ The open and close routines are also quite similar. The only real change is
+ that we now request an interrupt for the camera device interrupt line. If we
+ cannot get the interrupt we report EBUSY to the application and give up.
</para>
</sect1>
<sect1 id="irqvid">
<title>Interrupt Handling</title>
<para>
- Our example handler is for an ISA bus device. If it was PCI you would be
- able to share the interrupt and would have set SA_SHIRQ to indicate a
- shared IRQ. We pass the device pointer as the interrupt routine argument. We
- don't need to since we only support one card but doing this will make it
- easier to upgrade the driver for multiple devices in the future.
+ Our example handler is for an ISA bus device. If it was PCI you would be
+ able to share the interrupt and would have set SA_SHIRQ to indicate a
+ shared IRQ. We pass the device pointer as the interrupt routine argument. We
+ don't need to since we only support one card but doing this will make it
+ easier to upgrade the driver for multiple devices in the future.
</para>
<para>
- Our interrupt routine needs to do little if we assume the card can simply
- queue one frame to be read after it captures it.
+ Our interrupt routine needs to do little if we assume the card can simply
+ queue one frame to be read after it captures it.
</para>
<programlisting>
static struct wait_queue *capture_wait;
static int capture_ready = 0;
-static void camera_irq(int irq, void *dev_id, struct pt_regs *regs)
+static void camera_irq(int irq, void *dev_id,
+ struct pt_regs *regs)
{
- capture_ready=1;
- wake_up_interruptible(&capture_wait);
+ capture_ready=1;
+ wake_up_interruptible(&capture_wait);
}
</programlisting>
<para>
- The interrupt handler is nice and simple for this card as we are assuming
- the card is buffering the frame for us. This means we have little to do but
- wake up anybody interested. We also set a capture_ready flag, as we may
- capture a frame before an application needs it. In this case we need to know
- that a frame is ready. If we had to collect the frame on the interrupt life
- would be more complex.
+ The interrupt handler is nice and simple for this card as we are assuming
+ the card is buffering the frame for us. This means we have little to do but
+ wake up anybody interested. We also set a capture_ready flag, as we may
+ capture a frame before an application needs it. In this case we need to know
+ that a frame is ready. If we had to collect the frame on the interrupt life
+ would be more complex.
</para>
<para>
- The two new routines we need to supply are camera_read which returns a
- frame, and camera_poll which waits for a frame to become ready.
+ The two new routines we need to supply are camera_read which returns a
+ frame, and camera_poll which waits for a frame to become ready.
</para>
<programlisting>
-static int camera_poll(struct video_device *dev, struct file *file, struct poll_table *wait)
+static int camera_poll(struct video_device *dev,
+ struct file *file, struct poll_table *wait)
{
- poll_wait(file, &capture_wait, wait);
- if(capture_read)
- return POLLIN|POLLRDNORM;
- return 0;
+ poll_wait(file, &capture_wait, wait);
+ if(capture_read)
+ return POLLIN|POLLRDNORM;
+ return 0;
}
</programlisting>
<para>
- Our wait queue for polling is the capture_wait queue. This will cause the
- task to be woken up by our camera_irq routine. We check capture_read to see
- if there is an image present and if so report that it is readable.
+ Our wait queue for polling is the capture_wait queue. This will cause the
+ task to be woken up by our camera_irq routine. We check capture_read to see
+ if there is an image present and if so report that it is readable.
</para>
</sect1>
<sect1 id="rdvid">
static long camera_read(struct video_device *dev, char *buf,
- unsigned long count)
+ unsigned long count)
{
- struct wait_queue wait = { current, NULL };
- u8 *ptr;
- int len;
- int i;
-
- add_wait_queue(&capture_wait, &wait);
-
- while(!capture_ready)
- {
- if(file->flags&O_NDELAY)
- {
- remove_wait_queue(&capture_wait, &wait);
- current->state = TASK_RUNNING;
- return -EWOULDBLOCK;
- }
- if(signal_pending(current))
- {
- remove_wait_queue(&capture_wait, &wait);
- current->state = TASK_RUNNING;
- return -ERESTARTSYS;
- }
- schedule();
- current->state = TASK_INTERRUPTIBLE;
- }
- remove_wait_queue(&capture_wait, &wait);
- current->state = TASK_RUNNING;
+ struct wait_queue wait = { current, NULL };
+ u8 *ptr;
+ int len;
+ int i;
+
+ add_wait_queue(&capture_wait, &wait);
+
+ while(!capture_ready)
+ {
+ if(file->flags&O_NDELAY)
+ {
+ remove_wait_queue(&capture_wait, &wait);
+ current->state = TASK_RUNNING;
+ return -EWOULDBLOCK;
+ }
+ if(signal_pending(current))
+ {
+ remove_wait_queue(&capture_wait, &wait);
+ current->state = TASK_RUNNING;
+ return -ERESTARTSYS;
+ }
+ schedule();
+ current->state = TASK_INTERRUPTIBLE;
+ }
+ remove_wait_queue(&capture_wait, &wait);
+ current->state = TASK_RUNNING;
</programlisting>
<para>
- The first thing we have to do is to ensure that the application waits until
- the next frame is ready. The code here is almost identical to the mouse code
- we used earlier in this chapter. It is one of the common building blocks of
- Linux device driver code and probably one which you will find occurs in any
- drivers you write.
+ The first thing we have to do is to ensure that the application waits until
+ the next frame is ready. The code here is almost identical to the mouse code
+ we used earlier in this chapter. It is one of the common building blocks of
+ Linux device driver code and probably one which you will find occurs in any
+ drivers you write.
</para>
<para>
- We wait for a frame to be ready, or for a signal to interrupt our waiting. If a
- signal occurs we need to return from the system call so that the signal can
- be sent to the application itself. We also check to see if the user actually
- wanted to avoid waiting - ie if they are using non-blocking I/O and have other things
- to get on with.
+ We wait for a frame to be ready, or for a signal to interrupt our waiting. If a
+ signal occurs we need to return from the system call so that the signal can
+ be sent to the application itself. We also check to see if the user actually
+ wanted to avoid waiting - ie if they are using non-blocking I/O and have other things
+ to get on with.
</para>
<para>
- Next we copy the data from the card to the user application. This is rarely
- as easy as our example makes out. We will add capture_w, and capture_h here
- to hold the width and height of the captured image. We assume the card only
- supports 24bit RGB for now.
+ Next we copy the data from the card to the user application. This is rarely
+ as easy as our example makes out. We will add capture_w, and capture_h here
+ to hold the width and height of the captured image. We assume the card only
+ supports 24bit RGB for now.
</para>
<programlisting>
- capture_ready = 0;
+ capture_ready = 0;
- ptr=(u8 *)buf;
- len = capture_w * 3 * capture_h; /* 24bit RGB */
+ ptr=(u8 *)buf;
+ len = capture_w * 3 * capture_h; /* 24bit RGB */
- if(len>count)
- len=count; /* Doesn't all fit */
+ if(len>count)
+ len=count; /* Doesn't all fit */
- for(i=0; i<len; i++)
- {
- put_user(inb(io+IMAGE_DATA), ptr);
- ptr++;
- }
+ for(i=0; i<len; i++)
+ {
+ put_user(inb(io+IMAGE_DATA), ptr);
+ ptr++;
+ }
- hardware_restart_capture();
-
- return i;
+ hardware_restart_capture();
+
+ return i;
}
</programlisting>
<para>
- For a real hardware device you would try to avoid the loop with put_user().
- Each call to put_user() has a time overhead checking whether the accesses to user
- space are allowed. It would be better to read a line into a temporary buffer
- then copy this to user space in one go.
+ For a real hardware device you would try to avoid the loop with put_user().
+ Each call to put_user() has a time overhead checking whether the accesses to user
+ space are allowed. It would be better to read a line into a temporary buffer
+ then copy this to user space in one go.
</para>
<para>
- Having captured the image and put it into user space we can kick the card to
- get the next frame acquired.
+ Having captured the image and put it into user space we can kick the card to
+ get the next frame acquired.
</para>
</sect1>
<sect1 id="iocvid">
<title>Video Ioctl Handling</title>
<para>
- As with the radio driver the major control interface is via the ioctl()
- function. Video capture devices support the same tuner calls as a radio
- device and also support additional calls to control how the video functions
- are handled. In this simple example the card has no tuners to avoid making
- the code complex.
+ As with the radio driver the major control interface is via the ioctl()
+ function. Video capture devices support the same tuner calls as a radio
+ device and also support additional calls to control how the video functions
+ are handled. In this simple example the card has no tuners to avoid making
+ the code complex.
</para>
<programlisting>
static int camera_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
{
- switch(cmd)
- {
- case VIDIOCGCAP:
- {
- struct video_capability v;
- v.type = VID_TYPE_CAPTURE|VID_TYPE_CHROMAKEY|\
- VID_TYPE_SCALES|VID_TYPE_OVERLAY;
- v.channels = 1;
- v.audios = 0;
- v.maxwidth = 640;
- v.minwidth = 16;
- v.maxheight = 480;
- v.minheight = 16;
- strcpy(v.name, "My Camera");
- if(copy_to_user(arg, &v, sizeof(v)))
- return -EFAULT;
- return 0;
- }
+ switch(cmd)
+ {
+ case VIDIOCGCAP:
+ {
+ struct video_capability v;
+ v.type = VID_TYPE_CAPTURE|\
+ VID_TYPE_CHROMAKEY|\
+ VID_TYPE_SCALES|\
+ VID_TYPE_OVERLAY;
+ v.channels = 1;
+ v.audios = 0;
+ v.maxwidth = 640;
+ v.minwidth = 16;
+ v.maxheight = 480;
+ v.minheight = 16;
+ strcpy(v.name, "My Camera");
+ if(copy_to_user(arg, &v, sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
</programlisting>
<para>
- The first ioctl we must support and which all video capture and radio
- devices are required to support is VIDIOCGCAP. This behaves exactly the same
- as with a radio device. This time, however, we report the extra capabilities
- we outlined earlier on when defining our video_dev structure.
+ The first ioctl we must support and which all video capture and radio
+ devices are required to support is VIDIOCGCAP. This behaves exactly the same
+ as with a radio device. This time, however, we report the extra capabilities
+ we outlined earlier on when defining our video_dev structure.
</para>
<para>
- We now set the video flags saying that we support overlay, capture,
- scaling and chromakey. We also report size limits - our smallest image is
- 16x16 pixels, our largest is 640x480.
+ We now set the video flags saying that we support overlay, capture,
+ scaling and chromakey. We also report size limits - our smallest image is
+ 16x16 pixels, our largest is 640x480.
</para>
<para>
- To keep things simple we report no audio and no tuning capabilities at all.
+ To keep things simple we report no audio and no tuning capabilities at all.
</para>
- <programlisting>
+ <programlisting>
- case VIDIOCGCHAN:
- {
- struct video_channel v;
- if(copy_from_user(&v, arg, sizeof(v)))
- return -EFAULT;
- if(v.channel != 0)
- return -EINVAL;
- v.flags = 0;
- v.tuners = 0;
- v.type = VIDEO_TYPE_CAMERA;
- v.norm = VIDEO_MODE_AUTO;
- strcpy(v.name, "Camera Input");break;
- if(copy_to_user(&v, arg, sizeof(v)))
- return -EFAULT;
- return 0;
- }
+ case VIDIOCGCHAN:
+ {
+ struct video_channel v;
+ if(copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ if(v.channel != 0)
+ return -EINVAL;
+ v.flags = 0;
+ v.tuners = 0;
+ v.type = VIDEO_TYPE_CAMERA;
+ v.norm = VIDEO_MODE_AUTO;
+ strcpy(v.name, "Camera Input");break;
+ if(copy_to_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
</programlisting>
<para>
- This follows what is very much the standard way an ioctl handler looks
- in Linux. We copy the data into a kernel space variable and we check that the
- request is valid (in this case that the input is 0). Finally we copy the
- camera info back to the user.
- </para>
- <para>
- The VIDIOCGCHAN ioctl allows a user to ask about video channels (that is
- inputs to the video card). Our example card has a single camera input. The
- fields in the structure are
-
- channel The channel number we are selecting
- name The name for this channel. This is intended
- to describe the port to the user.
- Appropriate names are therefore things like
- "Camera" "SCART input"
- flags
-
- VIDEO_VC_TUNER Channel has a tuner.
- VIDEO_VC_AUDIO Channel has audio.
-
- type
-
- VIDEO_TYPE_TV Television input.
- VIDEO_TYPE_CAMERA Fixed camera input.
-
- If the input type is unknown then zero is returned.
-
- norm The current television encoding being used
- if relevant for this channel.
-
- This is one of
-
- VIDEO_MODE_PAL PAL encoded Television
- VIDEO_MODE_NTSC NTSC (US) encoded Television
- VIDEO_MODE_SECAM SECAM (French) Televison
- VIDEO_MODE_AUTO Automatic switching, or format does not
- matter
-
-
-
- The corresponding VIDIOCSCHAN ioctl allows a user to change channel and to
- request the norm is changed - for exaple to switch between a PAL or an NTSC
- format camera.
+ This follows what is very much the standard way an ioctl handler looks
+ in Linux. We copy the data into a kernel space variable and we check that the
+ request is valid (in this case that the input is 0). Finally we copy the
+ camera info back to the user.
+ </para>
+ <para>
+ The VIDIOCGCHAN ioctl allows a user to ask about video channels (that is
+ inputs to the video card). Our example card has a single camera input. The
+ fields in the structure are
+ </para>
+ <table frame=all><title>struct video_channel fields</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+
+ <entry>channel</><>The channel number we are selecting</entry>
+ </row><row>
+ <entry>name</><>The name for this channel. This is intended
+ to describe the port to the user.
+ Appropriate names are therefore things like
+ "Camera" "SCART input"</entry>
+ </row><row>
+ <entry>flags</><>Channel properties</entry>
+ </row><row>
+ <entry>type</><>Input type</entry>
+ </row><row>
+ <entry>norm</><>The current television encoding being used
+ if relevant for this channel.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <table frame=all><title>struct video_channel flags</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+ <entry>VIDEO_VC_TUNER</><>Channel has a tuner.</entry>
+ </row><row>
+ <entry>VIDEO_VC_AUDIO</><>Channel has audio.</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <table frame=all><title>struct video_channel types</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+ <entry>VIDEO_TYPE_TV</><>Television input.</entry>
+ </row><row>
+ <entry>VIDEO_TYPE_CAMERA</><>Fixed camera input.</entry>
+ </row><row>
+ <entry>0</><>Type is unknown.</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <table frame=all><title>struct video_channel norms</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+ <entry>VIDEO_MODE_PAL</><>PAL encoded Television</entry>
+ </row><row>
+ <entry>VIDEO_MODE_NTSC</><>NTSC (US) encoded Television</entry>
+ </row><row>
+ <entry>VIDEO_MODE_SECAM</><>SECAM (French) Televison </entry>
+ </row><row>
+ <entry>VIDEO_MODE_AUTO</><>Automatic switching, or format does not
+ matter</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <para>
+ The corresponding VIDIOCSCHAN ioctl allows a user to change channel and to
+ request the norm is changed - for exaple to switch between a PAL or an NTSC
+ format camera.
</para>
<programlisting>
- case VIDIOCSCHAN:
- {
- struct video_channel v;
- if(copy_from_user(&v, arg, sizeof(v)))
- return -EFAULT;
- if(v.channel != 0)
- return -EINVAL;
- if(v.norm != VIDEO_MODE_AUTO)
- return -EINVAL;
- return 0;
- }
+ case VIDIOCSCHAN:
+ {
+ struct video_channel v;
+ if(copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ if(v.channel != 0)
+ return -EINVAL;
+ if(v.norm != VIDEO_MODE_AUTO)
+ return -EINVAL;
+ return 0;
+ }
</programlisting>
<para>
- The implementation of this call in our driver is remarkably easy. Because we
- are assuming fixed format hardware we need only check that the user has not
- tried to change anything.
+ The implementation of this call in our driver is remarkably easy. Because we
+ are assuming fixed format hardware we need only check that the user has not
+ tried to change anything.
</para>
<para>
- The user also needs to be able to configure and adjust the picture they are
- seeing. This is much like adjusting a television set. A user application
- also needs to know the palette being used so that it knows how to display
- the image that has been captured. The VIDIOCGPICT and VIDIOCSPICT ioctl
- calls provide this information.
+ The user also needs to be able to configure and adjust the picture they are
+ seeing. This is much like adjusting a television set. A user application
+ also needs to know the palette being used so that it knows how to display
+ the image that has been captured. The VIDIOCGPICT and VIDIOCSPICT ioctl
+ calls provide this information.
</para>
<programlisting>
- case VIDIOCGPICT
- {
- struct video_picture v;
- v.brightness = hardware_brightness();
- v.hue = hardware_hue();
- v.colour = hardware_saturation();
- v.contrast = hardware_brightness();
- v.whiteness = 32768; /* Not settable */
- v.depth = 24; /* 24bit */
- v.palette = VIDEO_PALETTE_RGB24;
- if(copy_to_user(&v, arg, sizeof(v)))
- return -EFAULT;
- return 0;
- }
+ case VIDIOCGPICT
+ {
+ struct video_picture v;
+ v.brightness = hardware_brightness();
+ v.hue = hardware_hue();
+ v.colour = hardware_saturation();
+ v.contrast = hardware_brightness();
+ /* Not settable */
+ v.whiteness = 32768;
+ v.depth = 24; /* 24bit */
+ v.palette = VIDEO_PALETTE_RGB24;
+ if(copy_to_user(&v, arg,
+ sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
</programlisting>
<para>
- The brightness, hue, color, and contrast provide the picture controls that
- are akin to a conventional television. Whiteness provides additional
- control for greyscale images. All of these values are scaled between 0-65535
- and have 32768 as the mid point setting. The scaling means that applications
- do not have to worry about the capability range of the hardware but can let
- it make a best effort attempt.
- </para>
- <para>
- Our depth is 24, as this is in bits. We will be returing RGB24 format. This
- has one byte of red, then one of green, then one of blue. This then repeats
- for every other pixel in the image. The other common formats the interface
- defines are
-
- GREY Linear greyscale. This is for simple cameras and the
- like
-
- RGB565 The top 5 bits hold 32 red levels, the next six bits
- hold green and the low 5 bits hold blue.
-
- RGB555 The top bit is clear. The red green and blue levels
- each occupy five bits.
-
-
- Additional modes are support for YUV capture formats. These are common for
- TV and video conferencing applictations.
- </para>
- <para>
- The VIDIOCSPICT ioctl allows a user to set some of the picture parameters.
- Exactly which ones are supported depends heavily on the card itself. It is
- possible to support many modes and effects in software. In general doing
- this in the kernel is a bad idea. Video capture is a performance-sensitive
- application and the programs can often do better if they aren't being
- 'helped' by an overkeen driver writer. Thus for our device we will report
- RGB24 only and refuse to allow a change.
+ The brightness, hue, color, and contrast provide the picture controls that
+ are akin to a conventional television. Whiteness provides additional
+ control for greyscale images. All of these values are scaled between 0-65535
+ and have 32768 as the mid point setting. The scaling means that applications
+ do not have to worry about the capability range of the hardware but can let
+ it make a best effort attempt.
+ </para>
+ <para>
+ Our depth is 24, as this is in bits. We will be returing RGB24 format. This
+ has one byte of red, then one of green, then one of blue. This then repeats
+ for every other pixel in the image. The other common formats the interface
+ defines are
+ </para>
+ <table frame=all><title>Framebuffer Encodings</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+ <entry>GREY</><>Linear greyscale. This is for simple cameras and the
+ like</>
+ </row><row>
+ <entry>RGB565</><>The top 5 bits hold 32 red levels, the next six bits
+ hold green and the low 5 bits hold blue. </>
+ </row><row>
+ <entry>RGB555</><>The top bit is clear. The red green and blue levels
+ each occupy five bits.</>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <para>
+ Additional modes are support for YUV capture formats. These are common for
+ TV and video conferencing applications.
+ </para>
+ <para>
+ The VIDIOCSPICT ioctl allows a user to set some of the picture parameters.
+ Exactly which ones are supported depends heavily on the card itself. It is
+ possible to support many modes and effects in software. In general doing
+ this in the kernel is a bad idea. Video capture is a performance-sensitive
+ application and the programs can often do better if they aren't being
+ 'helped' by an overkeen driver writer. Thus for our device we will report
+ RGB24 only and refuse to allow a change.
</para>
<programlisting>
- case VIDIOCSPICT:
- {
- struct video_picture v;
- if(copy_from_user(&v, arg, sizeof(v)))
- return -EFAULT;
- if(v.depth!=24 || v.palette != VIDEO_PALETTE_RGB24)
- return -EINVAL;
- set_hardware_brightness(v.brightness);
- set_hardware_hue(v.hue);
- set_hardware_saturation(v.colour);
- set_hardware_brightness(v.contrast);
- return 0;
- }
+ case VIDIOCSPICT:
+ {
+ struct video_picture v;
+ if(copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ if(v.depth!=24 ||
+ v.palette != VIDEO_PALETTE_RGB24)
+ return -EINVAL;
+ set_hardware_brightness(v.brightness);
+ set_hardware_hue(v.hue);
+ set_hardware_saturation(v.colour);
+ set_hardware_brightness(v.contrast);
+ return 0;
+ }
</programlisting>
<para>
- We check the user has not tried to change the palette or the depth. We do
- not want to carry out some of the changes and then return an error. This may
- confuse the application which will be assuming no change occurred.
+ We check the user has not tried to change the palette or the depth. We do
+ not want to carry out some of the changes and then return an error. This may
+ confuse the application which will be assuming no change occurred.
</para>
<para>
- In much the same way as you need to be able to set the picture controls to
- get the right capture images, many cards need to know what they are
- displaying onto when generating overlay output. In some cases getting this
- wrong even makes a nasty mess or may crash the computer. For that reason
- the VIDIOCSBUF ioctl used to set up the frame buffer information may well
- only be usable by root.
+ In much the same way as you need to be able to set the picture controls to
+ get the right capture images, many cards need to know what they are
+ displaying onto when generating overlay output. In some cases getting this
+ wrong even makes a nasty mess or may crash the computer. For that reason
+ the VIDIOCSBUF ioctl used to set up the frame buffer information may well
+ only be usable by root.
</para>
<para>
- We will assume our card is one of the old ISA devices with feature connector
- and only supports a couple of standard video modes. Very common for older
- cards although the PCI devices are way smarter than this.
+ We will assume our card is one of the old ISA devices with feature connector
+ and only supports a couple of standard video modes. Very common for older
+ cards although the PCI devices are way smarter than this.
</para>
<programlisting>
static struct video_buffer capture_fb;
- case VIDIOCGFBUF:
- {
- if(copy_to_user(arg, &capture_fb, sizeof(capture_fb)))
- return -EFAULT;
- return 0;
-
- }
+ case VIDIOCGFBUF:
+ {
+ if(copy_to_user(arg, &capture_fb,
+ sizeof(capture_fb)))
+ return -EFAULT;
+ return 0;
+
+ }
</programlisting>
<para>
- We keep the frame buffer information in the format the ioctl uses. This
- makes it nice and easy to work with in the ioctl calls.
+ We keep the frame buffer information in the format the ioctl uses. This
+ makes it nice and easy to work with in the ioctl calls.
</para>
<programlisting>
- case VIDIOCSFBUF:
- {
- struct video_buffer v;
+ case VIDIOCSFBUF:
+ {
+ struct video_buffer v;
- if(!capable(CAP_SYS_ADMIN))
- return -EPERM;
+ if(!capable(CAP_SYS_ADMIN))
+ return -EPERM;
- if(copy_from_user(&v, arg, sizeof(v)))
- return -EFAULT;
- if(v.width!=320 && v.width!=640)
- return -EINVAL;
- if(v.height!=200 && v.height!=240 && v.height!=400
- && v.height !=480)
- return -EINVAL;
- memcpy(&capture_fb, &v, sizeof(v));
- hardware_set_fb(&v);
- return 0;
- }
+ if(copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ if(v.width!=320 && v.width!=640)
+ return -EINVAL;
+ if(v.height!=200 && v.height!=240
+ && v.height!=400
+ && v.height !=480)
+ return -EINVAL;
+ memcpy(&capture_fb, &v, sizeof(v));
+ hardware_set_fb(&v);
+ return 0;
+ }
</programlisting>
<para>
- The capable() function checks a user has the required capability. The Linux
- operating system has a set of about 30 capabilities indicating privileged
- access to services. The default set up gives the superuser (uid 0) all of
- them and nobody else has any.
+ The capable() function checks a user has the required capability. The Linux
+ operating system has a set of about 30 capabilities indicating privileged
+ access to services. The default set up gives the superuser (uid 0) all of
+ them and nobody else has any.
</para>
<para>
- We check that the user has the SYS_ADMIN capability, that is they are
- allowed to operate as the machine administrator. We don't want anyone but
- the administrator making a mess of the display.
+ We check that the user has the SYS_ADMIN capability, that is they are
+ allowed to operate as the machine administrator. We don't want anyone but
+ the administrator making a mess of the display.
</para>
<para>
- Next we check for standard PC video modes (320 or 640 wide with either
- EGA or VGA depths). If the mode is not a standard video mode we reject it as
- not supported by our card. If the mode is acceptable we save it so that
- VIDIOCFBUF will give the right answer next time it is called. The
- hardware_set_fb() function is some undescribed card specific function to
- program the card for the desired mode.
+ Next we check for standard PC video modes (320 or 640 wide with either
+ EGA or VGA depths). If the mode is not a standard video mode we reject it as
+ not supported by our card. If the mode is acceptable we save it so that
+ VIDIOCFBUF will give the right answer next time it is called. The
+ hardware_set_fb() function is some undescribed card specific function to
+ program the card for the desired mode.
</para>
<para>
- Before the driver can display an overlay window it needs to know where the
- window should be placed, and also how large it should be. If the card
- supports clipping it needs to know which rectangles to omit from the
- display. The video_window structure is used to describe the way the image
- should be displayed.
-
- width The width in pixels of the desired image. The card
- may use a smaller size if this size is not available
-
- height The height of the image. The card may use a smaller
- size if this size is not available.
-
- x The X position of the top left of the window. This
- is in pixels relative to the left hand edge of the
- picture. Not all cards can display images aligned on
- any pixel boundary. If the position is unsuitable
- the card adjusts the image right and reduces the
- width.
-
- y The Y position of the top left of the window. This
- is counted in pixels relative to the top edge of the
- picture. As with the width if the card cannot
- display starting on this line it will adjust the
- values.
-
- chromakey The colour (expressed in RGB32 format) for the
- chromakey colour if chroma keying is being used.
-
- clips An array of rectangles that must not be drawn over.
-
- clipcount The number of clips in this array.
-
-
- Each clip is a struct video_clip which has the following fields
-
- x, y Co-ordinates relative to the display
-
- width, height Width and height in pixels
-
- next A spare field for the application to use
-
-
- The driver is required to ensure it always draws in the area requested or a
- smaller area, and that it never draws in any of the areas that are clipped.
- This may well mean it has to leave alone. small areas the application wished to be
- drawn.
+ Before the driver can display an overlay window it needs to know where the
+ window should be placed, and also how large it should be. If the card
+ supports clipping it needs to know which rectangles to omit from the
+ display. The video_window structure is used to describe the way the image
+ should be displayed.
+ </para>
+ <table frame=all><title>struct video_window fields</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+ <entry>width</><>The width in pixels of the desired image. The card
+ may use a smaller size if this size is not available</>
+ </row><row>
+ <entry>height</><>The height of the image. The card may use a smaller
+ size if this size is not available.</>
+ </row><row>
+ <entry>x</><> The X position of the top left of the window. This
+ is in pixels relative to the left hand edge of the
+ picture. Not all cards can display images aligned on
+ any pixel boundary. If the position is unsuitable
+ the card adjusts the image right and reduces the
+ width.</>
+ </row><row>
+ <entry>y</><> The Y position of the top left of the window. This
+ is counted in pixels relative to the top edge of the
+ picture. As with the width if the card cannot
+ display starting on this line it will adjust the
+ values.</>
+ </row><row>
+ <entry>chromakey</><>The colour (expressed in RGB32 format) for the
+ chromakey colour if chroma keying is being used. </>
+ </row><row>
+ <entry>clips</><>An array of rectangles that must not be drawn
+ over.</>
+ </row><row>
+ <entry>clipcount</><>The number of clips in this array.</>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <para>
+ Each clip is a struct video_clip which has the following fields
+ </para>
+ <table frame=all><title>video_clip fields</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+ <entry>x, y</><>Co-ordinates relative to the display</>
+ </row><row>
+ <entry>width, height</><>Width and height in pixels</>
+ </row><row>
+ <entry>next</><>A spare field for the application to use</>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <para>
+ The driver is required to ensure it always draws in the area requested or a smaller area, and that it never draws in any of the areas that are clipped.
+ This may well mean it has to leave alone. small areas the application wished to be
+ drawn.
</para>
<para>
- Our example card uses chromakey so does not have to address most of the
- clipping. We will add a video_window structure to our global variables to
- remember our parameters, as we did with the frame buffer.
+ Our example card uses chromakey so does not have to address most of the
+ clipping. We will add a video_window structure to our global variables to
+ remember our parameters, as we did with the frame buffer.
</para>
<programlisting>
- case VIDIOCGWIN:
- {
- if(copy_to_user(arg, &capture_win, sizeof(capture_win)))
- return -EFAULT;
- return 0;
- }
-
-
- case VIDIOCSWIN:
- {
- struct video_window v;
- if(copy_from_user(&v, arg, sizeof(v)))
- return -EFAULT;
- if(v.width > 640 || v.height > 480)
- return -EINVAL;
- if(v.width < 16 || v.height < 16)
- return -EINVAL;
- hardware_set_key(v.chromakey);
- hardware_set_window(v);
- memcpy(&capture_win, &v, sizeof(v));
- capture_w = v.width;
- capture_h = v.height;
- return 0;
- }
+ case VIDIOCGWIN:
+ {
+ if(copy_to_user(arg, &capture_win,
+ sizeof(capture_win)))
+ return -EFAULT;
+ return 0;
+ }
+
+
+ case VIDIOCSWIN:
+ {
+ struct video_window v;
+ if(copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ if(v.width > 640 || v.height > 480)
+ return -EINVAL;
+ if(v.width < 16 || v.height < 16)
+ return -EINVAL;
+ hardware_set_key(v.chromakey);
+ hardware_set_window(v);
+ memcpy(&capture_win, &v, sizeof(v));
+ capture_w = v.width;
+ capture_h = v.height;
+ return 0;
+ }
</programlisting>
<para>
- Because we are using Chromakey our setup is fairly simple. Mostly we have to
- check the values are sane and load them into the capture card.
+ Because we are using Chromakey our setup is fairly simple. Mostly we have to
+ check the values are sane and load them into the capture card.
</para>
<para>
- With all the setup done we can now turn on the actual capture/overlay. This
- is done with the VIDIOCCAPTURE ioctl. This takes a single integer argument
- where 0 is on and 1 is off.
+ With all the setup done we can now turn on the actual capture/overlay. This
+ is done with the VIDIOCCAPTURE ioctl. This takes a single integer argument
+ where 0 is on and 1 is off.
</para>
<programlisting>
- case VIDIOCCAPTURE:
- {
- int v;
- if(get_user(v, (int *)arg))
- return -EFAULT;
- if(v==0)
- hardware_capture_off();
- else
- {
- if(capture_fb.width == 0 || capture_w == 0)
- return -EINVAL;
- hardware_capture_on();
- }
- return 0;
- }
+ case VIDIOCCAPTURE:
+ {
+ int v;
+ if(get_user(v, (int *)arg))
+ return -EFAULT;
+ if(v==0)
+ hardware_capture_off();
+ else
+ {
+ if(capture_fb.width == 0
+ || capture_w == 0)
+ return -EINVAL;
+ hardware_capture_on();
+ }
+ return 0;
+ }
</programlisting>
<para>
- We grab the flag from user space and either enable or disable according to
- its value. There is one small corner case we have to consider here. Suppose
- that the capture was requested before the video window or the frame buffer
- had been set up. In those cases there will be unconfigured fields in our
- card data, as well as unconfigured hardware settings. We check for this case and
- return an error if the frame buffer or the capture window width is zero.
+ We grab the flag from user space and either enable or disable according to
+ its value. There is one small corner case we have to consider here. Suppose
+ that the capture was requested before the video window or the frame buffer
+ had been set up. In those cases there will be unconfigured fields in our
+ card data, as well as unconfigured hardware settings. We check for this case and
+ return an error if the frame buffer or the capture window width is zero.
</para>
<programlisting>
- default:
- return -ENOIOCTLCMD;
- }
+ default:
+ return -ENOIOCTLCMD;
+ }
}
</programlisting>
<para>
- We don't need to support any other ioctls, so if we get this far, it is time
- to tell the video layer that we don't now what the user is talking about.
+ We don't need to support any other ioctls, so if we get this far, it is time
+ to tell the video layer that we don't now what the user is talking about.
</para>
</sect1>
<sect1 id="endvid">
<title>Other Functionality</title>
<para>
- The Video4Linux layer supports additional features, including a high
- performance mmap() based capture mode and capturing part of the image.
- These features are out of the scope of the book. You should however have enough
- example code to implement most simple video4linux devices for radio and TV
- cards.
+ The Video4Linux layer supports additional features, including a high
+ performance mmap() based capture mode and capturing part of the image.
+ These features are out of the scope of the book. You should however have enough
+ example code to implement most simple video4linux devices for radio and TV
+ cards.
</para>
</sect1>
</chapter>
<varlistentry><term>Multiple Opens</term>
<listitem>
<para>
- The driver assumes multiple opens should not be allowed. A driver
- can work around this but not cleanly.
+ The driver assumes multiple opens should not be allowed. A driver
+ can work around this but not cleanly.
</para>
</listitem></varlistentry>
<varlistentry><term>API Deficiences</term>
<listitem>
<para>
- The existing API poorly reflects compression capable devices. There
- are plans afoot to merge V4L, V4L2 and some other ideas into a
- better interface.
+ The existing API poorly reflects compression capable devices. There
+ are plans afoot to merge V4L, V4L2 and some other ideas into a
+ better interface.
</para>
</listitem></varlistentry>
</variablelist>
-
+
</para>
</chapter>
RAGE128 FRAMEBUFFER DISPLAY DRIVER
P: Brad Douglas
M: brad@neruo.com
-P: Anthony Tong
-M: atong@uiuc.edu
-L: linux-fbdev@vcuser.vc.union.edu
+L: linux-fbdev@vuser.vc.union.edu
S: Maintained
RAYLINK/WEBGEAR 802.11 WIRELESS LAN DRIVER
L: linux-usb@suse.com
S: Maintained
+USB PEGASUS DRIVER
+P: Petko Manolov
+M: petkan@spct.net
+L: linux-usb@suse.com
+S: Maintained
+
USB PRINTER DRIVER
P: Vojtech Pavlik
M: vojtech@suse.cz
VERSION = 2
PATCHLEVEL = 3
-SUBLEVEL = 52
-EXTRAVERSION =
+SUBLEVEL = 99
+EXTRAVERSION = -pre1
ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
LIBS =$(TOPDIR)/lib/lib.a
SUBDIRS =kernel drivers mm fs net ipc lib
-ifdef CONFIG_DRM
-DRIVERS += drivers/char/drm/drm.o
-endif
-
-ifeq ($(CONFIG_AGP),y)
-DRIVERS += drivers/char/agp/agp.o
-endif
-
-ifdef CONFIG_NUBUS
-DRIVERS := $(DRIVERS) drivers/nubus/nubus.a
-endif
-
-ifeq ($(CONFIG_ISDN),y)
-DRIVERS := $(DRIVERS) drivers/isdn/isdn.a
-endif
-
-ifdef CONFIG_NET_FC
-DRIVERS := $(DRIVERS) drivers/net/fc/fc.a
-endif
-
-ifdef CONFIG_APPLETALK
-DRIVERS := $(DRIVERS) drivers/net/appletalk/appletalk.a
-endif
-
-ifdef CONFIG_TR
-DRIVERS := $(DRIVERS) drivers/net/tokenring/tr.a
-endif
-
-ifdef CONFIG_WAN
-DRIVERS := $(DRIVERS) drivers/net/wan/wan.a
-endif
-
-ifeq ($(CONFIG_ARCNET),y)
-DRIVERS := $(DRIVERS) drivers/net/arcnet/arcnet.a
-endif
-
-ifdef CONFIG_ATM
-DRIVERS := $(DRIVERS) drivers/atm/atm.a
-endif
-
-ifeq ($(CONFIG_IDE),y)
-DRIVERS := $(DRIVERS) drivers/ide/ide.a
-endif
-
-ifeq ($(CONFIG_SCSI),y)
-DRIVERS := $(DRIVERS) drivers/scsi/scsi.a
-endif
-
-ifeq ($(CONFIG_IEEE1394),y)
-DRIVERS := $(DRIVERS) drivers/ieee1394/ieee1394.a
-endif
+DRIVERS-n :=
+DRIVERS-y :=
+DRIVERS-m :=
+DRIVERS- :=
+
+DRIVERS-$(CONFIG_DRM) += drivers/char/drm/drm.o
+DRIVERS-$(CONFIG_AGP) += drivers/char/agp/agp.o
+DRIVERS-$(CONFIG_NUBUS) += drivers/nubus/nubus.a
+DRIVERS-$(CONFIG_ISDN) += drivers/isdn/isdn.a
+DRIVERS-$(CONFIG_NET_FC) += drivers/net/fc/fc.a
+DRIVERS-$(CONFIG_APPLETALK) += drivers/net/appletalk/appletalk.a
+DRIVERS-$(CONFIG_TR) += drivers/net/tokenring/tr.a
+DRIVERS-$(CONFIG_WAN) += drivers/net/wan/wan.a
+DRIVERS-$(CONFIG_ARCNET) += drivers/net/arcnet/arcnet.a
+DRIVERS-$(CONFIG_ATM) += drivers/atm/atm.a
+DRIVERS-$(CONFIG_IDE) += drivers/ide/ide.a
+DRIVERS-$(CONFIG_SCSI) += drivers/scsi/scsi.a
+DRIVERS-$(CONFIG_IEEE1394) += drivers/ieee1394/ieee1394.a
ifneq ($(CONFIG_CD_NO_IDESCSI)$(CONFIG_BLK_DEV_IDECD)$(CONFIG_BLK_DEV_SR)$(CONFIG_PARIDE_PCD),)
-DRIVERS := $(DRIVERS) drivers/cdrom/cdrom.a
-endif
-
-ifeq ($(CONFIG_SOUND),y)
-DRIVERS := $(DRIVERS) drivers/sound/sounddrivers.o
-endif
-
-ifdef CONFIG_PCI
-DRIVERS := $(DRIVERS) drivers/pci/pci.a
-endif
-
-ifeq ($(CONFIG_PCMCIA),y)
-DRIVERS := $(DRIVERS) drivers/pcmcia/pcmcia.o
-endif
-
-ifeq ($(CONFIG_PCMCIA_NETCARD),y)
-DRIVERS := $(DRIVERS) drivers/net/pcmcia/pcmcia_net.o
-endif
-
-ifeq ($(CONFIG_PCMCIA_CHRDEV),y)
-DRIVERS := $(DRIVERS) drivers/char/pcmcia/pcmcia_char.o
-endif
-
-ifdef CONFIG_DIO
-DRIVERS := $(DRIVERS) drivers/dio/dio.a
-endif
-
-ifdef CONFIG_SBUS
-DRIVERS := $(DRIVERS) drivers/sbus/sbus.a
-endif
-
-ifdef CONFIG_ZORRO
-DRIVERS := $(DRIVERS) drivers/zorro/zorro.a
-endif
-
-ifeq ($(CONFIG_FC4),y)
-DRIVERS := $(DRIVERS) drivers/fc4/fc4.a
-endif
-
-ifdef CONFIG_PPC
-DRIVERS := $(DRIVERS) drivers/macintosh/macintosh.a
-endif
-
-ifdef CONFIG_MAC
-DRIVERS := $(DRIVERS) drivers/macintosh/macintosh.a
-endif
-
-ifeq ($(CONFIG_ISAPNP),y)
-DRIVERS := $(DRIVERS) drivers/pnp/pnp.o
-endif
-
-ifdef CONFIG_SGI_IP22
-DRIVERS := $(DRIVERS) drivers/sgi/sgi.a
-endif
-
-ifdef CONFIG_VT
-DRIVERS := $(DRIVERS) drivers/video/video.o
-endif
-
-ifeq ($(CONFIG_PARIDE),y)
-DRIVERS := $(DRIVERS) drivers/block/paride/paride.a
-endif
-
-ifdef CONFIG_HAMRADIO
-DRIVERS := $(DRIVERS) drivers/net/hamradio/hamradio.o
-endif
-
-ifeq ($(CONFIG_TC),y)
-DRIVERS := $(DRIVERS) drivers/tc/tc.a
-endif
-
-ifeq ($(CONFIG_USB),y)
-DRIVERS := $(DRIVERS) drivers/usb/usbdrv.o
-endif
-
-ifeq ($(CONFIG_I2O),y)
-DRIVERS := $(DRIVERS) drivers/i2o/i2o.a
-endif
-
-ifeq ($(CONFIG_IRDA),y)
-DRIVERS := $(DRIVERS) drivers/net/irda/irda_drivers.a
-endif
-
-ifeq ($(CONFIG_I2C),y)
-DRIVERS := $(DRIVERS) drivers/i2c/i2c.a
-endif
-
-ifeq ($(CONFIG_PHONE),y)
-DRIVERS := $(DRIVERS) drivers/telephony/telephony.a
-endif
+DRIVERS-y += drivers/cdrom/cdrom.a
+endif
+
+DRIVERS-$(CONFIG_SOUND) += drivers/sound/sounddrivers.o
+DRIVERS-$(CONFIG_PCI) += drivers/pci/pci.a
+DRIVERS-$(CONFIG_PCMCIA) += drivers/pcmcia/pcmcia.o
+DRIVERS-$(CONFIG_PCMCIA_NETCARD) += drivers/net/pcmcia/pcmcia_net.o
+DRIVERS-$(CONFIG_PCMCIA_CHRDEV) += drivers/char/pcmcia/pcmcia_char.o
+DRIVERS-$(CONFIG_DIO) += drivers/dio/dio.a
+DRIVERS-$(CONFIG_SBUS) += drivers/sbus/sbus.a
+DRIVERS-$(CONFIG_ZORRO) += drivers/zorro/zorro.a
+DRIVERS-$(CONFIG_FC4) += drivers/fc4/fc4.a
+DRIVERS-$(CONFIG_PPC) += drivers/macintosh/macintosh.a
+DRIVERS-$(CONFIG_MAC) += drivers/macintosh/macintosh.a
+DRIVERS-$(CONFIG_ISAPNP) += drivers/pnp/pnp.o
+DRIVERS-$(CONFIG_SGI_IP22) += drivers/sgi/sgi.a
+DRIVERS-$(CONFIG_VT) += drivers/video/video.o
+DRIVERS-$(CONFIG_PARIDE) += drivers/block/paride/paride.a
+DRIVERS-$(CONFIG_HAMRADIO) += drivers/net/hamradio/hamradio.o
+DRIVERS-$(CONFIG_TC) += drivers/tc/tc.a
+DRIVERS-$(CONFIG_USB) += drivers/usb/usbdrv.o
+DRIVERS-$(CONFIG_I2O) += drivers/i2o/i2o.a
+DRIVERS-$(CONFIG_IRDA) += drivers/net/irda/irda_drivers.a
+DRIVERS-$(CONFIG_I2C) += drivers/i2c/i2c.a
+DRIVERS-$(CONFIG_PHONE) += drivers/telephony/telephony.a
+
+DRIVERS += $(DRIVERS-y)
include arch/$(ARCH)/Makefile
.long SYMBOL_NAME(sys_setfsgid)
.long SYMBOL_NAME(sys_pivot_root)
.long SYMBOL_NAME(sys_mincore)
+ .long SYMBOL_NAME(sys_madvise)
/*
* entries. Don't panic if you notice that this hasn't
* been shrunk every time we add a new system call.
*/
- .rept NR_syscalls-218
+ .rept NR_syscalls-219
.long SYMBOL_NAME(sys_ni_syscall)
.endr
* hardware disable after having gotten the irq
* controller lock.
*/
+
+/**
+ * disable_irq_nosync - disable an irq without waiting
+ * @irq: Interrupt to disable
+ *
+ * Disable the selected interrupt line. Disables of an interrupt
+ * stack. Unlike disable_irq, this function does not ensure existing
+ * instances of the irq handler have completed before returning.
+ *
+ * This function may be called from IRQ context.
+ */
+
void inline disable_irq_nosync(unsigned int irq)
{
irq_desc_t *desc = irq_desc + irq;
spin_unlock_irqrestore(&desc->lock, flags);
}
-/*
- * Synchronous version of the above, making sure the IRQ is
- * no longer running on any other IRQ..
+/**
+ * disable_irq - disable an irq and wait for completion
+ * @irq: Interrupt to disable
+ *
+ * Disable the selected interrupt line. Disables of an interrupt
+ * stack. That is for two disables you need two enables. This
+ * function waits for any pending IRQ handlers for this interrupt
+ * to complete before returning. If you use this function while
+ * holding a resource the IRQ handler may need you will deadlock.
+ *
+ * This function may be called - with care - from IRQ context.
*/
+
void disable_irq(unsigned int irq)
{
disable_irq_nosync(irq);
}
}
+/**
+ * enable_irq - enable interrupt handling on an irq
+ * @irq: Interrupt to enable
+ *
+ * Re-enables the processing of interrupts on this IRQ line
+ * providing no disable_irq calls are now in effect.
+ *
+ * This function may be called from IRQ context.
+ */
+
void enable_irq(unsigned int irq)
{
irq_desc_t *desc = irq_desc + irq;
return 1;
}
+/**
+ * request_irq - allocate an interrupt line
+ * @irq: Interrupt line to allocate
+ * @handler: Function to be called when the IRQ occurs
+ * @irqflags: Interrupt type flags
+ * @devname: An ascii name for the claiming device
+ * @dev_id: A cookie passed back to the handler function
+ *
+ * This call allocates interrupt resources and enables the
+ * interrupt line and IRQ handling. From the point this
+ * call is made your handler function may be invoked. Since
+ * your handler function must clear any interrupt the board
+ * raises, you must take care both to initialise your hardware
+ * and to set up the interrupt handler in the right order.
+ *
+ * Dev_id must be globally unique. Normally the address of the
+ * device data structure is used as the cookie. Since the handler
+ * receives this value it makes sense to use it.
+ *
+ * If your interrupt is shared you must pass a non NULL dev_id
+ * as this is required when freeing the interrupt.
+ *
+ * Flags:
+ *
+ * SA_SHIRQ Interrupt is shared
+ *
+ * SA_INTERRUPT Disable local interrupts while processing
+ *
+ * SA_SAMPLE_RANDOM The interrupt can be used for entropy
+ *
+ */
+
int request_irq(unsigned int irq,
void (*handler)(int, void *, struct pt_regs *),
unsigned long irqflags,
kfree(action);
return retval;
}
-
+
+/**
+ * free_irq - free an interrupt
+ * @irq: Interrupt line to free
+ * @dev_id: Device identity to free
+ *
+ * Remove an interrupt handler. The handler is removed and if the
+ * interrupt line is no longer in use by any driver it is disabled.
+ * On a shared IRQ the caller must ensure the interrupt is disabled
+ * on the card it drives before calling this function. The function
+ * does not return until any executing interrupts for this IRQ
+ * have completed.
+ *
+ * This function may be called from interrupt context.
+ *
+ * Bugs: Attempting to free an irq in a handler for the same irq hangs
+ * the machine.
+ */
+
void free_irq(unsigned int irq, void *dev_id)
{
irq_desc_t *desc;
* with "IRQ_WAITING" cleared and the interrupt
* disabled.
*/
+
+/**
+ * probe_irq_on - begin an interrupt autodetect
+ *
+ * Commence probing for an interrupt. The interrupts are scanned
+ * and a mask of potential interrupt lines is returned.
+ *
+ */
+
unsigned long probe_irq_on(void)
{
unsigned int i;
* Return a mask of triggered interrupts (this
* can handle only legacy ISA interrupts).
*/
+
+/**
+ * probe_irq_mask
+ * @val: mask of interrupts to consider
+ *
+ * Scan the ISA bus interrupt lines and return a bitmap of
+ * active interrupts. The interrupt probe logic state is then
+ * returned to its previous value.
+ */
+
unsigned int probe_irq_mask(unsigned long val)
{
int i;
/*
* Return the one interrupt that triggered (this can
- * handle any interrupt source)
+ * handle any interrupt source).
+ */
+
+/**
+ * probe_irq_off - end an interrupt autodetect
+ * @val: mask of potential interrupts (unused)
+ *
+ * Scans the unused interrupt lines and returns the line which
+ * appears to have triggered the interrupt. If no interrupt was
+ * found then zero is returned. If more than one interrupt is
+ * found then minus the first candidate is returned to indicate
+ * their is doubt.
+ *
+ * The interrupt probe logic state is returned to its previous
+ * value.
+ *
+ * BUGS: When used in a module (which arguably shouldnt happen)
+ * nothing prevents two IRQ probe callers from overlapping. The
+ * results of this are non-optimal.
*/
+
int probe_irq_off(unsigned long val)
{
int i, irq_found, nr_irqs;
static int (*get_free_region) (unsigned long base,
unsigned long size) = generic_get_free_region;
-int mtrr_add (unsigned long base, unsigned long size, unsigned int type,
- char increment)
+/**
+ * mtrr_add - Add a memory type region
+ * @base: Physical base address of region
+ * @size: Physical size of region
+ * @type: Type of MTRR desired
+ * @increment: If this is true do usage counting on the region
+ *
+ * Memory type region registers control the caching on newer Intel and
+ * non Intel processors. This function allows drivers to request an
+ * MTRR is added. The details and hardware specifics of each processors
+ * implementation are hidden from the caller, but nevertheless the
+ * caller should expect to need to provide a power of two size on an
+ * equivalent power of two boundary.
+ *
+ * If the region cannot be added either because all regions are in use
+ * or the CPU cannot support it a negative value is returned. On success
+ * the register number for this entry is returned, but should be treated
+ * as a cookie only.
+ *
+ * On a multiprocessor machine the changes are made to all processors.
+ * This is required on x86 by the Intel processors.
+ *
+ * The available types are
+ *
+ * MTRR_TYPE_UNCACHEABLE - No caching
+ *
+ * MTRR_TYPE_WRITEBACK - Write data back in bursts whenever
+ *
+ * MTRR_TYPE_WRCOMB - Write data back soon but allow bursts
+ *
+ * MTRR_TYPE_WRTHROUGH - Cache reads but not writes
+ *
+ * BUGS: Needs a quiet flag for the cases where drivers do not mind
+ * failures and do not wish system log messages to be sent.
+ */
+
+int mtrr_add(unsigned long base, unsigned long size, unsigned int type, char increment)
+{
/* [SUMMARY] Add an MTRR entry.
<base> The starting (base) address of the region.
<size> The size (in bytes) of the region.
the error code.
[NOTE] This routine uses a spinlock.
*/
-{
int i, max;
mtrr_type ltype;
unsigned long lbase, lsize, last;
if ( (boot_cpu_data.x86 == 6) && (boot_cpu_data.x86_model == 1) &&
(boot_cpu_data.x86_mask <= 7) && ( base & ( (1 << 22) -1 ) ) )
{
- printk ("mtrr: base(0x%lx) is not 4 MiB aligned\n", base);
+ printk (KERN_WARNING "mtrr: base(0x%lx) is not 4 MiB aligned\n", base);
return -EINVAL;
}
}
{
if (type != MTRR_TYPE_WRCOMB)
{
- printk ("mtrr: only write-combining is supported\n");
+ printk (KERN_WARNING "mtrr: only write-combining is supported\n");
return -EINVAL;
}
}
else if (base + size < 0x100000)
{
- printk ("mtrr: cannot set region below 1 MiB (0x%lx,0x%lx)\n",
+ printk (KERN_WARNING "mtrr: cannot set region below 1 MiB (0x%lx,0x%lx)\n",
base, size);
return -EINVAL;
}
lbase = lbase >> 1, last = last >> 1);
if (lbase != last)
{
- printk ("mtrr: base(0x%lx) is not aligned on a size(0x%lx) boundary\n",
+ printk (KERN_WARNING "mtrr: base(0x%lx) is not aligned on a size(0x%lx) boundary\n",
base, size);
return -EINVAL;
}
/* If the type is WC, check that this processor supports it */
if ( (type == MTRR_TYPE_WRCOMB) && !have_wrcomb () )
{
- printk ("mtrr: your processor doesn't support write-combining\n");
+ printk (KERN_WARNING "mtrr: your processor doesn't support write-combining\n");
return -ENOSYS;
}
increment = increment ? 1 : 0;
if ( (base < lbase) || (base + size > lbase + lsize) )
{
up(&main_lock);
- printk ("mtrr: 0x%lx,0x%lx overlaps existing 0x%lx,0x%lx\n",
+ printk (KERN_WARNING "mtrr: 0x%lx,0x%lx overlaps existing 0x%lx,0x%lx\n",
base, size, lbase, lsize);
return -EINVAL;
}
return i;
} /* End Function mtrr_add */
+/**
+ * mtrr_del
+ * @reg: Register returned by mtrr_add
+ * @base: Physical base address
+ * @size: Size of region
+ *
+ * If register is supplied then base and size are ignored. This is
+ * how drivers should call it.
+ *
+ * Releases an MTRR region. If the usage count drops to zero the
+ * register is freed and the region returns to default state.
+ * On success the register is returned, on failure a negative error
+ * code.
+ */
+
int mtrr_del (int reg, unsigned long base, unsigned long size)
/* [SUMMARY] Delete MTRR/decrement usage count.
<reg> The register. If this is less than 0 then <<base>> and <<size>> must
#include <asm/mipsregs.h>
#include <asm/prctl.h>
-#include <linux/config.h>
-
#define DLINFO_ITEMS 12
#include <linux/elf.h>
-/* $Id: sys_sunos.c,v 1.114 2000/03/07 22:27:27 davem Exp $
+/* $Id: sys_sunos.c,v 1.115 2000/03/13 21:57:23 davem Exp $
* sys_sunos.c: SunOS specific syscall compatibility support.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
unlock_kernel();
}
-/* Places into character array, the status of all the pages in the passed
- * range from 'addr' to 'addr + len'. -1 on failure, 0 on success...
- * The encoding in each character is:
- * low-bit is zero == Page is not in physical ram right now
- * low-bit is one == Page is currently residing in core
- * All other bits are undefined within the character so there...
- * Also, if you try to get stats on an area outside of the user vm area
- * *or* the passed base address is not aligned on a page boundary you
- * get an error.
- */
-asmlinkage int sunos_mincore(unsigned long addr, unsigned long len, char *array)
-{
- pgd_t *pgdp;
- pmd_t *pmdp;
- pte_t *ptep;
- unsigned long limit;
- int num_pages, pnum, retval = -EINVAL;
-
- lock_kernel();
- if(addr & ~(PAGE_MASK))
- goto out;
-
- num_pages = (len / PAGE_SIZE);
- retval = -EFAULT;
- if(verify_area(VERIFY_WRITE, array, num_pages))
- goto out;
- retval = -ENOMEM;
- if((addr >= PAGE_OFFSET) || ((addr + len) > PAGE_OFFSET))
- goto out; /* I'm sure you're curious about kernel mappings.. */
-
- /* Wheee, go through pte's */
- pnum = 0;
- for(limit = addr + len; addr < limit; addr += PAGE_SIZE, pnum++) {
- pgdp = pgd_offset(current->mm, addr);
- if(pgd_none(*pgdp))
- goto out; /* As per SunOS manpage */
- pmdp = pmd_offset(pgdp, addr);
- if(pmd_none(*pmdp))
- goto out; /* As per SunOS manpage */
- ptep = pte_offset(pmdp, addr);
- if(pte_none(*ptep))
- goto out; /* As per SunOS manpage */
- /* Page in core or Swapped page? */
- __put_user((pte_present(*ptep) ? 1 : 0), &array[pnum]);
- }
- retval = 0; /* Success... I think... */
-out:
- unlock_kernel();
- return retval;
-}
-
/* This just wants the soft limit (ie. rlim_cur element) of the RLIMIT_NOFILE
* resource limit and is for backwards compatibility with older sunos
* revs.
-/* $Id: systbls.S,v 1.94 2000/02/16 07:31:30 davem Exp $
+/* $Id: systbls.S,v 1.95 2000/03/13 21:57:23 davem Exp $
* systbls.S: System call entry point tables for OS compatibility.
* The native Linux system call table lives here also.
*
/*60*/ .long sys_umask, sys_chroot, sys_newfstat, sys_fstat64, sys_getpagesize
/*65*/ .long sys_msync, sys_vfork, sys_pread, sys_pwrite, sys_geteuid
/*70*/ .long sys_getegid, sys_mmap, sys_setreuid, sys_munmap, sys_mprotect
-/*75*/ .long sys_nis_syscall, sys_vhangup, sys_truncate64, sys_nis_syscall, sys_getgroups16
+/*75*/ .long sys_nis_syscall, sys_vhangup, sys_truncate64, sys_mincore, sys_getgroups16
/*80*/ .long sys_setgroups16, sys_getpgrp, sys_setgroups, sys_setitimer, sys_ftruncate64
/*85*/ .long sys_swapon, sys_getitimer, sys_setuid, sys_sethostname, sys_setgid
/*90*/ .long sys_dup2, sys_setfsuid, sys_fcntl, sys_select, sys_setfsgid
.long sunos_nosys, sunos_sbrk, sunos_sstk
.long sunos_mmap, sunos_vadvise, sys_munmap
.long sys_mprotect, sunos_madvise, sys_vhangup
- .long sunos_nosys, sunos_mincore, sys_getgroups16
+ .long sunos_nosys, sys_mincore, sys_getgroups16
.long sys_setgroups16, sys_getpgrp, sunos_setpgrp
.long sys_setitimer, sunos_nosys, sys_swapon
.long sys_getitimer, sys_gethostname, sys_sethostname
-/* $Id: ioctl32.c,v 1.81 2000/03/12 04:02:07 davem Exp $
+/* $Id: ioctl32.c,v 1.82 2000/03/13 21:57:27 davem Exp $
* ioctl32.c: Conversion between 32bit and 64bit native ioctls.
*
* Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com)
COMPATIBLE_IOCTL(PPPIOCNEWUNIT)
COMPATIBLE_IOCTL(PPPIOCATTACH)
COMPATIBLE_IOCTL(PPPIOCDETACH)
+COMPATIBLE_IOCTL(PPPIOCSMRRU)
+COMPATIBLE_IOCTL(PPPIOCCONNECT)
+COMPATIBLE_IOCTL(PPPIOCDISCONN)
+COMPATIBLE_IOCTL(PPPIOCATTCHAN)
/* CDROM stuff */
COMPATIBLE_IOCTL(CDROMPAUSE)
COMPATIBLE_IOCTL(CDROMRESUME)
-/* $Id: sys_sparc32.c,v 1.135 2000/03/12 03:52:09 davem Exp $
+/* $Id: sys_sparc32.c,v 1.136 2000/03/13 21:57:29 davem Exp $
* sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
*
* Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
out:
return ret;
}
+
+extern asmlinkage long sys_mincore(unsigned long start, size_t len, unsigned char *vec);
+
+asmlinkage long sys32_mincore(unsigned long start, u32 __len, unsigned char *vec)
+{
+ size_t len = (size_t) __len;
+
+ return sys_mincore(start, len, vec);
+}
-/* $Id: sys_sunos32.c,v 1.40 2000/03/07 22:27:31 davem Exp $
+/* $Id: sys_sunos32.c,v 1.41 2000/03/13 21:57:31 davem Exp $
* sys_sunos32.c: SunOS binary compatability layer on sparc64.
*
* Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
unlock_kernel();
}
-/* Places into character array, the status of all the pages in the passed
- * range from 'addr' to 'addr + len'. -1 on failure, 0 on success...
- * The encoding in each character is:
- * low-bit is zero == Page is not in physical ram right now
- * low-bit is one == Page is currently residing in core
- * All other bits are undefined within the character so there...
- * Also, if you try to get stats on an area outside of the user vm area
- * *or* the passed base address is not aligned on a page boundary you
- * get an error.
- */
-asmlinkage int sunos_mincore(u32 __addr, u32 len, u32 u_array)
-{
- pgd_t *pgdp;
- pmd_t *pmdp;
- pte_t *ptep;
- unsigned long limit, addr = (unsigned long)__addr;
- int num_pages, pnum, retval = -EINVAL;
- char *array = (char *)A(u_array);
-
- lock_kernel();
- if(addr & ~(4096))
- goto out;
- num_pages = (len / 4096);
- retval = -EFAULT;
- if(verify_area(VERIFY_WRITE, array, num_pages))
- goto out;
- retval = -ENOMEM;
- if((addr >= 0xf0000000) || ((addr + len) > 0xf0000000))
- goto out; /* I'm sure you're curious about kernel mappings.. */
- /* Wheee, go through pte's */
- pnum = 0;
- for(limit = addr + len; addr < limit; addr += 4096, pnum++) {
- pgdp = pgd_offset(current->mm, addr);
- if(pgd_none(*pgdp))
- goto out; /* As per SunOS manpage */
- pmdp = pmd_offset(pgdp, addr);
- if(pmd_none(*pmdp))
- goto out; /* As per SunOS manpage */
- ptep = pte_offset(pmdp, addr);
- if(pte_none(*ptep))
- goto out; /* As per SunOS manpage */
- /* Page in core or Swapped page? */
- __put_user((pte_present(*ptep) ? 1 : 0), &array[pnum]);
- }
- retval = 0; /* Success... I think... */
-out:
- unlock_kernel();
- return retval;
-}
-
/* This just wants the soft limit (ie. rlim_cur element) of the RLIMIT_NOFILE
* resource limit and is for backwards compatibility with older sunos
* revs.
-/* $Id: systbls.S,v 1.68 2000/02/16 07:31:38 davem Exp $
+/* $Id: systbls.S,v 1.69 2000/03/13 21:57:28 davem Exp $
* systbls.S: System call entry point tables for OS compatibility.
* The native Linux system call table lives here also.
*
/*60*/ .word sys_umask, sys_chroot, sys32_newfstat, sys_fstat64, sys_getpagesize
.word sys_msync, sys_vfork, sys32_pread, sys32_pwrite, sys_geteuid
/*70*/ .word sys_getegid, sys32_mmap, sys_setreuid, sys_munmap, sys_mprotect
- .word sys_nis_syscall, sys_vhangup, sys32_truncate64, sys_nis_syscall, sys32_getgroups16
+ .word sys_nis_syscall, sys_vhangup, sys32_truncate64, sys32_mincore, sys32_getgroups16
/*80*/ .word sys32_setgroups16, sys_getpgrp, sys_setgroups, sys32_setitimer, sys32_ftruncate64
.word sys_swapon, sys32_getitimer, sys_setuid, sys_sethostname, sys_setgid
/*90*/ .word sys_dup2, sys_setfsuid, sys32_fcntl, sys32_select, sys_setfsgid
/*60*/ .word sys_umask, sys_chroot, sys_newfstat, sys_nis_syscall, sys_getpagesize
.word sys_msync, sys_vfork, sys_pread, sys_pwrite, sys_nis_syscall
/*70*/ .word sys_nis_syscall, sys_mmap, sys_nis_syscall, sys64_munmap, sys_mprotect
- .word sys_nis_syscall, sys_vhangup, sys_nis_syscall, sys_nis_syscall, sys_getgroups
+ .word sys_nis_syscall, sys_vhangup, sys_nis_syscall, sys_mincore, sys_getgroups
/*80*/ .word sys_setgroups, sys_getpgrp, sys_nis_syscall, sys_setitimer, sys_nis_syscall
.word sys_swapon, sys_getitimer, sys_nis_syscall, sys_sethostname, sys_nis_syscall
/*90*/ .word sys_dup2, sys_nis_syscall, sys_fcntl, sys_select, sys_nis_syscall
.word sunos_nosys, sunos_sbrk, sunos_sstk
.word sunos_mmap, sunos_vadvise, sys_munmap
.word sys_mprotect, sunos_madvise, sys_vhangup
- .word sunos_nosys, sunos_mincore, sys32_getgroups16
+ .word sunos_nosys, sys32_mincore, sys32_getgroups16
.word sys32_setgroups16, sys_getpgrp, sunos_setpgrp
.word sys32_setitimer, sunos_nosys, sys_swapon
.word sys32_getitimer, sys_gethostname, sys_sethostname
-/* $Id: misc.c,v 1.22 2000/02/16 07:31:41 davem Exp $
+/* $Id: misc.c,v 1.23 2000/03/13 21:57:34 davem Exp $
* misc.c: Miscelaneous syscall emulation for Solaris
*
* Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
-/* $Id: systbl.S,v 1.10 2000/01/12 02:59:26 davem Exp $
+/* $Id: systbl.S,v 1.11 2000/03/13 21:57:35 davem Exp $
* systbl.S: System call entry point table for Solaris compatibility.
*
* Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
.word solaris_unimplemented /* async 111 */
.word solaris_unimplemented /* priocntlsys 112 */
.word solaris_pathconf /* pathconf sd 113 */
- .word solaris_unimplemented /* mincore xdx 114 */
+ .word CHAIN(mincore) /* mincore d 114 */
.word solaris_mmap /* mmap xxxxdx 115 */
.word CHAIN(mprotect) /* mprotect xdx 116 */
.word CHAIN(munmap) /* munmap xd 117 */
/* auth.c -- IOCTLs for authentication -*- linux-c -*-
* Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 11:31:48 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
- *
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/auth.c,v 1.4 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/auth.c,v 1.1 1999/09/25 14:37:57 dawes Exp $
+ *
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
/* bufs.c -- IOCTLs to manage buffers -*- linux-c -*-
* Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com
- * Revised: Fri Dec 3 12:11:11 1999 by faith@precisioninsight.com
*
- * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/bufs.c,v 1.8 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/bufs.c,v 1.1 1999/09/25 14:37:57 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
#define __NO_VERSION__
-#include <linux/config.h>
#include "drmP.h"
#include "linux/un.h"
/* context.c -- IOCTLs for contexts and DMA queues -*- linux-c -*-
* Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 11:32:09 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/context.c,v 1.5 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/context.c,v 1.1 1999/09/25 14:37:58 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
/* dma.c -- DMA IOCTL and function support -*- linux-c -*-
* Created: Fri Mar 19 14:30:16 1999 by faith@precisioninsight.com
- * Revised: Thu Sep 16 12:55:39 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/dma.c,v 1.7 1999/09/16 16:56:18 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/dma.c,v 1.1 1999/09/25 14:37:58 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
/* drawable.c -- IOCTLs for drawables -*- linux-c -*-
* Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 09:27:03 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drawable.c,v 1.3 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drawable.c,v 1.1 1999/09/25 14:37:58 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
/* drm.h -- Header for Direct Rendering Manager -*- linux-c -*-
* Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com
- * Revised: Mon Dec 6 17:11:19 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All rights reserved.
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drm.h,v 1.46 1999/08/20 20:00:53 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drm.h,v 1.1 1999/09/25 14:37:58 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
* Acknowledgements:
* Dec 1999, Richard Henderson <rth@twiddle.net>, move to generic cmpxchg.
/* drmP.h -- Private header for Direct Rendering Manager -*- linux-c -*-
* Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com
- * Revised: Mon Dec 6 16:06:49 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All rights reserved.
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drmP.h,v 1.58 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drmP.h,v 1.1 1999/09/25 14:37:59 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
#ifdef CONFIG_MTRR
#include <asm/mtrr.h>
#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+#include <asm/spinlock.h>
+#include <linux/poll.h>
+#endif
#include "drm.h"
#define DRM_DEBUG_CODE 2 /* Include debugging code (if > 1, then
extern ssize_t drm_read(struct file *filp, char *buf, size_t count,
loff_t *off);
extern int drm_write_string(drm_device_t *dev, const char *s);
+extern unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait);
/* Mapping support (vm.c) */
#if LINUX_VERSION_CODE < 0x020317
/* fops.c -- File operations for DRM -*- linux-c -*-
* Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com
- * Revised: Fri Dec 3 10:26:26 1999 by faith@precisioninsight.com
*
- * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/fops.c,v 1.3 1999/08/20 15:36:45 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/fops.c,v 1.1 1999/09/25 14:37:59 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ * Daryll Strauss <daryll@precisioninsight.com>
*
*/
wake_up_interruptible(&dev->buf_readers);
return 0;
}
+
+unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait)
+{
+ drm_file_t *priv = filp->private_data;
+ drm_device_t *dev = priv->dev;
+
+ poll_wait(filp, &dev->buf_readers, wait);
+ if (dev->buf_wp != dev->buf_rp) return POLLIN | POLLRDNORM;
+ return 0;
+}
/* gamma_dma.c -- DMA support for GMX 2000 -*- linux-c -*-
* Created: Fri Mar 19 14:30:16 1999 by faith@precisioninsight.com
- * Revised: Thu Sep 16 12:55:37 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_dma.c,v 1.9 1999/09/16 16:56:18 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_dma.c,v 1.1 1999/09/25 14:38:00 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
/* gamma.c -- 3dlabs GMX 2000 driver -*- linux-c -*-
* Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com
- * Revised: Tue Oct 12 08:51:36 1999 by faith@precisioninsight.com
*
- * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_drv.c,v 1.17 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_drv.c,v 1.1 1999/09/25 14:38:00 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
-#include <linux/config.h>
#include "drmP.h"
#include "gamma_drv.h"
EXPORT_SYMBOL(gamma_init);
mmap: drm_mmap,
read: drm_read,
fasync: drm_fasync,
+ poll: drm_poll,
};
static struct miscdevice gamma_misc = {
/* gamma_drv.h -- Private header for 3dlabs GMX 2000 driver -*- linux-c -*-
* Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 09:24:27 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All rights reserved.
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_drv.h,v 1.4 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_drv.h,v 1.1 1999/09/25 14:38:00 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
/* init.c -- Setup/Cleanup for DRM -*- linux-c -*-
* Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 09:27:02 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/init.c,v 1.3 1999/08/20 15:07:01 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/init.c,v 1.1 1999/09/25 14:38:01 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
/* ioctl.c -- IOCTL processing for DRM -*- linux-c -*-
* Created: Fri Jan 8 09:01:26 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 09:27:02 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/ioctl.c,v 1.3 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/ioctl.c,v 1.1 1999/09/25 14:38:01 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
/* lists.c -- Buffer list handling routines -*- linux-c -*-
* Created: Mon Apr 19 20:54:22 1999 by faith@precisioninsight.com
- * Revised: Mon Dec 6 16:04:44 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/lists.c,v 1.3 1999/08/20 15:07:02 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/lists.c,v 1.1 1999/09/25 14:38:01 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
buf->list = DRM_LIST_FREE;
do {
old = bl->next;
- bl->next = old;
+ buf->next = old;
prev = cmpxchg(&bl->next, old, buf);
if (++count > DRM_LOOPING_LIMIT) {
DRM_ERROR("Looping\n");
/* lock.c -- IOCTLs for locking -*- linux-c -*-
* Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com
- * Revised: Mon Dec 6 16:04:44 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/lock.c,v 1.5 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/lock.c,v 1.1 1999/09/25 14:38:01 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
/* memory.c -- Memory management wrappers for DRM -*- linux-c -*-
* Created: Thu Feb 4 14:00:34 1999 by faith@precisioninsight.com
- * Revised: Mon Dec 6 10:28:18 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/memory.c,v 1.4 1999/08/20 20:00:53 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/memory.c,v 1.1 1999/09/25 14:38:02 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
/* proc.c -- /proc support for DRM -*- linux-c -*-
* Created: Mon Jan 11 09:48:47 1999 by faith@precisioninsight.com
- * Revised: Fri Dec 3 09:44:16 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/proc.c,v 1.4 1999/08/20 15:36:46 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/proc.c,v 1.1 1999/09/25 14:38:02 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
struct proc_dir_entry *ent;
int i, j;
- drm_root = create_proc_entry("graphics", S_IFDIR, NULL);
+ drm_root = create_proc_entry("dri", S_IFDIR, NULL);
if (!drm_root) {
- DRM_ERROR("Cannot create /proc/graphics\n");
+ DRM_ERROR("Cannot create /proc/dri\n");
return -1;
}
/* Instead of doing this search, we should
- add some global support for /proc/graphics. */
+ add some global support for /proc/dri. */
for (i = 0; i < 8; i++) {
- sprintf(drm_slot_name, "graphics/%d", i);
+ sprintf(drm_slot_name, "dri/%d", i);
drm_dev_root = create_proc_entry(drm_slot_name, S_IFDIR, NULL);
if (!drm_dev_root) {
DRM_ERROR("Cannot create /proc/%s\n", drm_slot_name);
- remove_proc_entry("graphics", NULL);
+ remove_proc_entry("dri", NULL);
}
if (drm_dev_root->nlink == 2) break;
drm_dev_root = NULL;
}
if (!drm_dev_root) {
- DRM_ERROR("Cannot find slot in /proc/graphics\n");
+ DRM_ERROR("Cannot find slot in /proc/dri\n");
return -1;
}
remove_proc_entry(drm_proc_list[i].name,
drm_dev_root);
remove_proc_entry(drm_slot_name, NULL);
- remove_proc_entry("graphics", NULL);
+ remove_proc_entry("dri", NULL);
return -1;
}
ent->read_proc = drm_proc_list[i].f;
}
remove_proc_entry(drm_slot_name, NULL);
}
- remove_proc_entry("graphics", NULL);
+ remove_proc_entry("dri", NULL);
remove_proc_entry(DRM_NAME, NULL);
}
drm_root = drm_dev_root = NULL;
/* tdfx_context.c -- IOCTLs for tdfx contexts -*- linux-c -*-
* Created: Thu Oct 7 10:50:22 1999 by faith@precisioninsight.com
- * Revised: Sat Oct 9 23:39:56 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI$
- * $XFree86$
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
/* tdfx.c -- tdfx driver -*- linux-c -*-
* Created: Thu Oct 7 10:38:32 1999 by faith@precisioninsight.com
- * Revised: Tue Oct 12 08:51:35 1999 by faith@precisioninsight.com
*
- * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
- *
- * $PI$
- * $XFree86$
+ *
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ * Daryll Strauss <daryll@precisioninsight.com>
*
*/
-#include <linux/config.h>
#include "drmP.h"
#include "tdfx_drv.h"
-EXPORT_SYMBOL(tdfx_init);
-EXPORT_SYMBOL(tdfx_cleanup);
#define TDFX_NAME "tdfx"
#define TDFX_DESC "tdfx"
mmap: drm_mmap,
read: drm_read,
fasync: drm_fasync,
+ poll: drm_poll,
};
static struct miscdevice tdfx_misc = {
#endif
}
}
+
+ if (lock.context != tdfx_res_ctx.handle) {
+ current->counter = 5;
+ current->priority = DEF_PRIORITY/4;
+ }
+
DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
#if DRM_DMA_HISTOGRAM
}
}
+ if (lock.context != tdfx_res_ctx.handle) {
+ current->counter = 5;
+ current->priority = DEF_PRIORITY;
+ }
+
return 0;
}
/* tdfx_drv.h -- Private header for tdfx driver -*- linux-c -*-
* Created: Thu Oct 7 10:40:04 1999 by faith@precisioninsight.com
- * Revised: Sat Oct 9 23:38:19 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All rights reserved.
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI$
- * $XFree86$
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ * Daryll Strauss <daryll@precisioninsight.com>
*
*/
/* vm.c -- Memory mapping for DRM -*- linux-c -*-
* Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com
- * Revised: Mon Dec 6 16:54:35 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/vm.c,v 1.7 1999/08/21 02:48:34 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/vm.c,v 1.1 1999/09/25 14:38:02 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
#define RELEASEIT restore_flags (flags)
#endif
+#define RS_EVENT_WRITE_WAKEUP 1
#ifdef DEBUG
static void my_hd (unsigned char *addr, int len)
open: misc_open,
};
+/**
+ * misc_register - register a miscellaneous device
+ * @misc: device structure
+ *
+ * Register a miscellaneous device with the kernel. If the minor
+ * number is set to MISC_DYNAMIC_MINOR a minor number is assigned
+ * and placed in the minor field of the structure. For other cases
+ * the minor number requested is used.
+ *
+ * The structure passed is linked into the kernel and may not be
+ * destroyed until it has been unregistered
+ *
+ * A zero is returned on success and a negative errno code for
+ * failure.
+ */
+
int misc_register(struct miscdevice * misc)
{
static devfs_handle_t devfs_handle = NULL;
return 0;
}
+/**
+ * misc_deregister - unregister a miscellaneous device
+ * @misc: device to unregister
+ *
+ * Unregister a miscellaneous device that was previously
+ * successfully registered with misc_register. Success
+ * is indicated by a zero return, a negative errno code
+ * indicates an error.
+ */
+
int misc_deregister(struct miscdevice * misc)
{
int i = misc->minor;
unsigned int minor = MINOR (inode->i_rdev);
struct pp_struct *pp = file->private_data;
+ if (pp->pdev->port->ieee1284.mode != IEEE1284_MODE_COMPAT) {
+ if (!(pp->flags & PP_CLAIMED)) {
+ parport_claim_or_block (pp->pdev);
+ pp->flags |= PP_CLAIMED;
+ }
+ parport_negotiate (pp->pdev->port, IEEE1284_MODE_COMPAT);
+ printk (KERN_DEBUG CHRDEV
+ "%x: negotiated back to compatibility mode because "
+ "user-space forgot\n", minor);
+ }
+
if (pp->flags & PP_CLAIMED) {
parport_release (pp->pdev);
printk (KERN_DEBUG CHRDEV "%x: released pardevice because "
}
/*
- * register_serial and unregister_serial allows for serial ports to be
+ * register_serial and unregister_serial allows for 16x50 serial ports to be
* configured at run-time, to support PCMCIA modems.
*/
+
+/**
+ * register_serial - configure a 16x50 serial port at runtime
+ * @req: request structure
+ *
+ * Configure the serial port specified by the request. If the
+ * port exists and is in use an error is returned. If the port
+ * is not currently in the table it is added.
+ *
+ * The port is then probed and if neccessary the IRQ is autodetected
+ * If this fails an error is returned.
+ *
+ * On success the port is ready to use and the line number is returned.
+ */
+
int register_serial(struct serial_struct *req)
{
int i;
if ((state->flags & ASYNC_AUTO_IRQ) && CONFIGURED_SERIAL_PORT(state))
state->irq = detect_uart_irq(state);
- printk(KERN_INFO "ttyS%02d at %s 0x%04lx (irq = %d) is a %s\n",
+ printk(KERN_INFO "ttyS%02d at %s 0x%04lx (irq = %d) is a %s\n",
state->line + SERIAL_DEV_OFFSET,
state->iomem_base ? "iomem" : "port",
state->iomem_base ? (unsigned long)state->iomem_base :
return state->line + SERIAL_DEV_OFFSET;
}
+/**
+ * unregister_serial - deconfigure a 16x50 serial port
+ * @line: line to deconfigure
+ *
+ * The port specified is deconfigured and its resources are freed. Any
+ * user of the port is disconnected as if carrier was dropped. Line is
+ * the port number returned by register_serial.
+ */
+
void unregister_serial(int line)
{
unsigned long flags;
drive->bios_head = page->heads;
drive->bios_sect = page->sectors;
lba_capacity = floppy->blocks * floppy->block_size;
- if (capacity != lba_capacity) {
- printk (KERN_NOTICE "%s: The drive reports both %d and %d bytes as its capacity\n",
- drive->name, capacity, lba_capacity);
- capacity = IDEFLOPPY_MIN(capacity, lba_capacity);
+ if (capacity < lba_capacity) {
+ printk (KERN_NOTICE "%s: The disk reports a capacity of %d bytes, "
+ "but the drive only handles %d\n",
+ drive->name, lba_capacity, capacity);
floppy->blocks = floppy->block_size ? capacity / floppy->block_size : 0;
}
return 0;
module_init(ppp_async_init);
module_exit(ppp_async_cleanup);
-EXPORT_SYMBOL(ppp_async_init); /* for debugging */
}
#endif
+#ifdef CONFIG_INET
+
static int shaper_neigh_setup(struct neighbour *n)
{
if (n->nud_state == NUD_NONE) {
return 0;
}
+#else /* !(CONFIG_INET) */
+
+static int shaper_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p)
+{
+ return 0;
+}
+
+#endif
+
static int shaper_attach(struct net_device *shdev, struct shaper *sh, struct net_device *dev)
{
sh->dev = dev;
+2000-03-13 <twaugh@redhat.com>
+
+ * parport_pc.c (parport_pc_init): Moved from asm/parport.h.
+
+ * Config.in: CONFIG_PARPORT_PC_SUPERIO: new option.
+
+ * parport_pc.c (show_parconfig_smsc37c669): Make __devinit.
+ (show_parconfig_winbond): Likewise.
+ (decode_winbond): Likewise.
+ (decode_smsc): Likewise.
+ (winbond_check): Likewise.
+ (winbond_check2): Likewise.
+ (smsc_check): Likewise.
+ (detect_and_report_winbond): Likewise.
+ (detect_and_report_smsc): Likewise.
+ (get_superio_dma): Likewise.
+ (get_superio_irq): Likewise.
+ (parport_pc_find_isa_ports): New function.
+ (parport_pc_find_ports): New function.
+ (init_module): Make superio a config option, not a parameter.
+
2000-03-10 <twaugh@redhat.com>
* parport_pc.c (decode_winbond): Use correct 83877ATF chip ID.
dep_tristate ' PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT
if [ "$CONFIG_PARPORT_PC" != "n" ]; then
bool ' Use FIFO/DMA if available' CONFIG_PARPORT_PC_FIFO
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ bool ' SuperIO chipset support (EXPERIMENTAL)' CONFIG_PARPORT_PC_SUPERIO
+ fi
fi
if [ "$CONFIG_PARPORT_PC" = "y" ]; then
# Don't bother with this if parport_pc is a module; it only affects
#include <linux/parport_pc.h>
#include <asm/parport.h>
+#define PARPORT_PC_MAX_PORTS PARPORT_MAX
+
/* ECR modes */
#define ECR_SPP 00
#define ECR_PS2 01
int io;
int irq;
int dma;
-} superios[NR_SUPERIOS]= { {0,},};
-
+} superios[NR_SUPERIOS] __devinitdata = { {0,},};
+
+static int user_specified __devinitdata = 0;
+
/* frob_control, but for ECR */
static void frob_econtrol (struct parport *pb, unsigned char m,
unsigned char v)
parport_ieee1284_read_byte,
};
+#ifdef CONFIG_PARPORT_PC_SUPERIO
/* Super-IO chipset detection, Winbond, SMSC */
-
-static void show_parconfig_smsc37c669(int io, int key)
+static void __devinit show_parconfig_smsc37c669(int io, int key)
{
int cr1,cr4,cra,cr23,cr26,cr27,i=0;
char *modes[]={ "SPP and Bidirectional (PS/2)",
}
-static void show_parconfig_winbond(int io, int key)
+static void __devinit show_parconfig_winbond(int io, int key)
{
int cr30,cr60,cr61,cr70,cr74,crf0,i=0;
char *modes[]={ "Standard (SPP) and Bidirectional(PS/2)", /* 0 */
}
}
-static void decode_winbond(int efer, int key, int devid, int devrev, int oldid)
+static void __devinit decode_winbond(int efer, int key, int devid, int devrev, int oldid)
{
char *type=NULL;
int id,progif=2;
return;
}
-static void decode_smsc(int efer, int key, int devid, int devrev)
+static void __devinit decode_smsc(int efer, int key, int devid, int devrev)
{
char *type=NULL;
void (*func)(int io, int key);
}
-static void winbond_check(int io, int key)
+static void __devinit winbond_check(int io, int key)
{
int devid,devrev,oldid;
decode_winbond(io,key,devid,devrev,oldid);
}
-static void winbond_check2(int io,int key)
+static void __devinit winbond_check2(int io,int key)
{
int devid,devrev,oldid;
decode_winbond(io,key,devid,devrev,oldid);
}
-static void smsc_check(int io, int key)
+static void __devinit smsc_check(int io, int key)
{
int devid,devrev;
}
-static void detect_and_report_winbond (void)
+static void __devinit detect_and_report_winbond (void)
{
printk("Winbond Super-IO detection, now testing ports 3F0,370,250,4E,2E ...\n");
winbond_check2(0x250,0x89);
}
-static void detect_and_report_smsc (void)
+static void __devinit detect_and_report_smsc (void)
{
printk("SMSC Super-IO detection, now testing Ports 2F0, 370 ...\n");
smsc_check(0x3f0,0x55);
smsc_check(0x3f0,0x44);
smsc_check(0x370,0x44);
}
+#endif /* CONFIG_PARPORT_PC_SUPERIO */
-static int get_superio_dma (struct parport *p)
+static int __devinit get_superio_dma (struct parport *p)
{
int i=0;
while( (superios[i].io != p->base) && (i<NR_SUPERIOS))
return PARPORT_DMA_NONE;
}
-static int get_superio_irq (struct parport *p)
+static int __devinit get_superio_irq (struct parport *p)
{
int i=0;
while( (superios[i].io != p->base) && (i<NR_SUPERIOS))
probe: parport_pc_pci_probe,
};
-static int __devinit parport_pc_init_superio (void)
+static int __init parport_pc_init_superio (void)
{
#ifdef CONFIG_PCI
const struct pci_device_id *id;
return 0; /* zero devices found */
}
+/* This is called by parport_pc_find_nonpci_ports (in asm/parport.h) */
+static int __init __attribute__((unused))
+parport_pc_find_isa_ports (int autoirq, int autodma)
+{
+ int count = 0;
+
+ if (parport_pc_probe_port(0x3bc, 0x7bc, autoirq, autodma, NULL))
+ count++;
+ if (parport_pc_probe_port(0x378, 0x778, autoirq, autodma, NULL))
+ count++;
+ if (parport_pc_probe_port(0x278, 0x678, autoirq, autodma, NULL))
+ count++;
+
+ return count;
+}
+
+/* This function is called by parport_pc_init if the user didn't
+ * specify any ports to probe. Its job is to find some ports. Order
+ * is important here -- we want ISA ports to be registered first,
+ * followed by PCI cards (for least surprise), but before that we want
+ * to do chipset-specific tests for some onboard ports that we know
+ * about.
+ *
+ * autoirq is PARPORT_IRQ_NONE, PARPORT_IRQ_AUTO, or PARPORT_IRQ_PROBEONLY
+ * autodma is PARPORT_DMA_NONE or PARPORT_DMA_AUTO
+ */
+static int __init parport_pc_find_ports (int autoirq, int autodma)
+{
+ int count = 0, r;
+
+#ifdef CONFIG_PARPORT_PC_SUPERIO
+ detect_and_report_winbond ();
+ detect_and_report_smsc ();
+#endif
+
+ /* Onboard SuperIO chipsets that show themselves on the PCI bus. */
+ count += parport_pc_init_superio ();
+
+ /* ISA ports and whatever (see asm/parport.h). */
+ count += parport_pc_find_nonpci_ports (autoirq, autodma);
+
+ r = pci_register_driver (&parport_pc_pci_driver);
+ if (r > 0)
+ count += r;
+
+ return count;
+}
+
+int __init parport_pc_init (int *io, int *io_hi, int *irq, int *dma)
+{
+ int count = 0, i = 0;
+
+ if (io && *io) {
+ /* Only probe the ports we were given. */
+ user_specified = 1;
+ do {
+ if (!*io_hi) *io_hi = 0x400 + *io;
+ if (parport_pc_probe_port(*(io++), *(io_hi++),
+ *(irq++), *(dma++), NULL))
+ count++;
+ } while (*io && (++i < PARPORT_PC_MAX_PORTS));
+ } else {
+ count += parport_pc_find_ports (irq[0], dma[0]);
+ }
+
+ return count;
+}
+
/* Exported symbols. */
#ifdef CONFIG_PARPORT_PC_PCMCIA
static int irqval[PARPORT_PC_MAX_PORTS] = { [0 ... PARPORT_PC_MAX_PORTS-1] = PARPORT_IRQ_PROBEONLY };
static const char *irq[PARPORT_PC_MAX_PORTS] = { NULL, };
static const char *dma[PARPORT_PC_MAX_PORTS] = { NULL, };
-static int superio = 0;
MODULE_AUTHOR("Phil Blundell, Tim Waugh, others");
MODULE_DESCRIPTION("PC-style parallel port driver");
MODULE_PARM(irq, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "s");
MODULE_PARM_DESC(dma, "DMA channel");
MODULE_PARM(dma, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "s");
-MODULE_PARM_DESC(superio, "Enable Super-IO chipset probe");
-MODULE_PARM(superio, "i");
int init_module(void)
{
/* Work out how many ports we have, then get parport_share to parse
the irq values. */
- unsigned int i, n;
- if (superio) {
- detect_and_report_winbond ();
- detect_and_report_smsc ();
- }
+ unsigned int i;
for (i = 0; i < PARPORT_PC_MAX_PORTS && io[i]; i++);
if (i) {
if (parport_parse_irqs(i, irq, irqval)) return 1;
}
}
- n = parport_pc_init_superio ();
- n += parport_pc_init (io, io_hi, irqval, dmaval);
- i = pci_register_driver (&parport_pc_pci_driver);
-
- if (i > 0) n += i;
- return !n;
+ return !parport_pc_init (io, io_hi, irqval, dmaval);
}
void cleanup_module(void)
}
#if (CONSTANTS & CONST_XSENSE)
-#define D 0x001 /* DIRECT ACCESS DEVICE (disk) */
-#define T 0x002 /* SEQUENTIAL ACCESS DEVICE (tape) */
-#define L 0x004 /* PRINTER DEVICE */
-#define P 0x008 /* PROCESSOR DEVICE */
-#define W 0x010 /* WRITE ONCE READ MULTIPLE DEVICE */
-#define R 0x020 /* READ ONLY (CD-ROM) DEVICE */
-#define S 0x040 /* SCANNER DEVICE */
-#define O 0x080 /* OPTICAL MEMORY DEVICE */
-#define M 0x100 /* MEDIA CHANGER DEVICE */
-#define C 0x200 /* COMMUNICATION DEVICE */
+#define D 0x0001 /* DIRECT ACCESS DEVICE (disk) */
+#define T 0x0002 /* SEQUENTIAL ACCESS DEVICE (tape) */
+#define L 0x0004 /* PRINTER DEVICE */
+#define P 0x0008 /* PROCESSOR DEVICE */
+#define W 0x0010 /* WRITE ONCE READ MULTIPLE DEVICE */
+#define R 0x0020 /* READ ONLY (CD-ROM) DEVICE */
+#define S 0x0040 /* SCANNER DEVICE */
+#define O 0x0080 /* OPTICAL MEMORY DEVICE */
+#define M 0x0100 /* MEDIA CHANGER DEVICE */
+#define C 0x0200 /* COMMUNICATION DEVICE */
+#define A 0x0400 /* ARRAY STORAGE */
+#define E 0x0800 /* ENCLOSURE SERVICES DEVICE */
+#define B 0x1000 /* SIMPLIFIED DIRECT ACCESS DEVICE */
+#define K 0x2000 /* OPTICAL CARD READER/WRITER DEVICE */
struct error_info{
unsigned char code1, code2;
static struct error_info additional[] =
{
+ {0x00,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"No additional sense information"},
{0x00,0x01,T,"Filemark detected"},
{0x00,0x02,T|S,"End-of-partition/medium detected"},
{0x00,0x03,T,"Setmark detected"},
{0x00,0x04,T|S,"Beginning-of-partition/medium detected"},
- {0x00,0x05,T|S,"End-of-data detected"},
- {0x00,0x06,D|T|L|P|W|R|S|O|M|C,"I/O process terminated"},
+ {0x00,0x05,T|L|S,"End-of-data detected"},
+ {0x00,0x06,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"I/O process terminated"},
{0x00,0x11,R,"Audio play operation in progress"},
{0x00,0x12,R,"Audio play operation paused"},
{0x00,0x13,R,"Audio play operation successfully completed"},
{0x00,0x14,R,"Audio play operation stopped due to error"},
{0x00,0x15,R,"No current audio status to return"},
- {0x01,0x00,D|W|O,"No index/sector signal"},
- {0x02,0x00,D|W|R|O|M,"No seek complete"},
- {0x03,0x00,D|T|L|W|S|O,"Peripheral device write fault"},
+ {0x00,0x16,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Operation in progress"},
+ {0x00,0x17,D|T|L|W|R|S|O|M|A|E|B|K,"Cleaning requested"},
+ {0x01,0x00,D|W|O|B|K,"No index/sector signal"},
+ {0x02,0x00,D|W|R|O|M|B|K,"No seek complete"},
+ {0x03,0x00,D|T|L|W|S|O|B|K,"Peripheral device write fault"},
{0x03,0x01,T,"No write current"},
{0x03,0x02,T,"Excessive write errors"},
- {0x04,0x00,D|T|L|P|W|R|S|O|M|C,
- "Logical unit not ready, cause not reportable"},
- {0x04,0x01,D|T|L|P|W|R|S|O|M|C,
- "Logical unit is in process of becoming ready"},
- {0x04,0x02,D|T|L|P|W|R|S|O|M|C,
- "Logical unit not ready, initializing command required"},
- {0x04,0x03,D|T|L|P|W|R|S|O|M|C,
- "Logical unit not ready, manual intervention required"},
- {0x04,0x04,D|T|L|O,"Logical unit not ready, format in progress"},
- {0x05,0x00,D|T|L|W|R|S|O|M|C,"Logical unit does not respond to selection"},
- {0x06,0x00,D|W|R|O|M,"No reference position found"},
- {0x07,0x00,D|T|L|W|R|S|O|M,"Multiple peripheral devices selected"},
- {0x08,0x00,D|T|L|W|R|S|O|M|C,"Logical unit communication failure"},
- {0x08,0x01,D|T|L|W|R|S|O|M|C,"Logical unit communication time-out"},
- {0x08,0x02,D|T|L|W|R|S|O|M|C,"Logical unit communication parity error"},
- {0x09,0x00,D|T|W|R|O,"Track following error"},
- {0x09,0x01,W|R|O,"Tracking servo failure"},
- {0x09,0x02,W|R|O,"Focus servo failure"},
+ {0x04,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,cause not reportable"},
+ {0x04,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit is in process of becoming ready"},
+ {0x04,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,initializing cmd. required"},
+ {0x04,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,manual intervention required"},
+ {0x04,0x04,D|T|L|R|O|B,"Logical unit not ready,format in progress"},
+ {0x04,0x05,D|T|W|O|M|C|A|B|K,"Logical unit not ready,rebuild in progress"},
+ {0x04,0x06,D|T|W|O|M|C|A|B|K,"Logical unit not ready,recalculation in progress"},
+ {0x04,0x07,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,operation in progress"},
+ {0x04,0x08,R,"Logical unit not ready,long write in progress"},
+ {0x04,0x09,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,self-test in progress"},
+ {0x05,0x00,D|T|L|W|R|S|O|M|C|A|E|B|K,"Logical unit does not respond to selection"},
+ {0x06,0x00,D|W|R|O|M|B|K,"No reference position found"},
+ {0x07,0x00,D|T|L|W|R|S|O|M|B|K,"Multiple peripheral devices selected"},
+ {0x08,0x00,D|T|L|W|R|S|O|M|C|A|E|B|K,"Logical unit communication failure"},
+ {0x08,0x01,D|T|L|W|R|S|O|M|C|A|E|B|K,"Logical unit communication time-out"},
+ {0x08,0x02,D|T|L|W|R|S|O|M|C|A|E|B|K,"Logical unit communication parity error"},
+ {0x08,0x03,D|T|R|O|M|B|K,"Logical unit communication CRC error (Ultra-DMA/32)"},
+ {0x08,0x04,D|T|L|P|W|R|S|O|C|K,"Unreachable copy target"},
+ {0x09,0x00,D|T|W|R|O|B,"Track following error"},
+ {0x09,0x01,W|R|O|K,"Tracking servo failure"},
+ {0x09,0x02,W|R|O|K,"Focus servo failure"},
{0x09,0x03,W|R|O,"Spindle servo failure"},
- {0x0A,0x00,D|T|L|P|W|R|S|O|M|C,"Error log overflow"},
- {0x0C,0x00,T|S,"Write error"},
- {0x0C,0x01,D|W|O,"Write error recovered with auto reallocation"},
- {0x0C,0x02,D|W|O,"Write error - auto reallocation failed"},
- {0x10,0x00,D|W|O,"Id crc or ecc error"},
- {0x11,0x00,D|T|W|R|S|O,"Unrecovered read error"},
- {0x11,0x01,D|T|W|S|O,"Read retries exhausted"},
- {0x11,0x02,D|T|W|S|O,"Error too long to correct"},
- {0x11,0x03,D|T|W|S|O,"Multiple read errors"},
- {0x11,0x04,D|W|O,"Unrecovered read error - auto reallocate failed"},
- {0x11,0x05,W|R|O,"L-ec uncorrectable error"},
- {0x11,0x06,W|R|O,"Circ unrecovered error"},
- {0x11,0x07,W|O,"Data resynchronization error"},
+ {0x09,0x04,D|T|W|R|O|B,"Head select fault"},
+ {0x0A,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Error log overflow"},
+ {0x0B,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Warning"},
+ {0x0B,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Warning - specified temperature exceeded"},
+ {0x0B,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Warning - enclosure degraded"},
+ {0x0C,0x00,T|R|S,"Write error"},
+ {0x0C,0x01,K,"Write error - recovered with auto reallocation"},
+ {0x0C,0x02,D|W|O|B|K,"Write error - auto reallocation failed"},
+ {0x0C,0x03,D|W|O|B|K,"Write error - recommend reassignment"},
+ {0x0C,0x04,D|T|W|O|B,"Compression check miscompare error"},
+ {0x0C,0x05,D|T|W|O|B,"Data expansion occurred during compression"},
+ {0x0C,0x06,D|T|W|O|B,"Block not compressible"},
+ {0x0C,0x07,R,"Write error - recovery needed"},
+ {0x0C,0x08,R,"Write error - recovery failed"},
+ {0x0C,0x09,R,"Write error - loss of streaming"},
+ {0x0C,0x0A,R,"Write error - padding blocks added"},
+ {0x10,0x00,D|W|O|B|K,"Id CRC or ECC error"},
+ {0x11,0x00,D|T|W|R|S|O|B|K,"Unrecovered read error"},
+ {0x11,0x01,D|T|W|R|S|O|B|K,"Read retries exhausted"},
+ {0x11,0x02,D|T|W|R|S|O|B|K,"Error too long to correct"},
+ {0x11,0x03,D|T|W|S|O|B|K,"Multiple read errors"},
+ {0x11,0x04,D|W|O|B|K,"Unrecovered read error - auto reallocate failed"},
+ {0x11,0x05,W|R|O|B,"L-EC uncorrectable error"},
+ {0x11,0x06,W|R|O|B,"CIRC unrecovered error"},
+ {0x11,0x07,W|O|B,"Data re-synchronization error"},
{0x11,0x08,T,"Incomplete block read"},
{0x11,0x09,T,"No gap found"},
- {0x11,0x0A,D|T|O,"Miscorrected error"},
- {0x11,0x0B,D|W|O,"Unrecovered read error - recommend reassignment"},
- {0x11,0x0C,D|W|O,"Unrecovered read error - recommend rewrite the data"},
- {0x12,0x00,D|W|O,"Address mark not found for id field"},
- {0x13,0x00,D|W|O,"Address mark not found for data field"},
- {0x14,0x00,D|T|L|W|R|S|O,"Recorded entity not found"},
- {0x14,0x01,D|T|W|R|O,"Record not found"},
+ {0x11,0x0A,D|T|O|B|K,"Miscorrected error"},
+ {0x11,0x0B,D|W|O|B|K,"Unrecovered read error - recommend reassignment"},
+ {0x11,0x0C,D|W|O|B|K,"Unrecovered read error - recommend rewrite the data"},
+ {0x11,0x0D,D|T|W|R|O|B,"De-compression CRC error"},
+ {0x11,0x0E,D|T|W|R|O|B,"Cannot decompress using declared algorithm"},
+ {0x11,0x0F,R,"Error reading UPC/EAN number"},
+ {0x11,0x10,R,"Error reading ISRC number"},
+ {0x11,0x11,R,"Read error - loss of streaming"},
+ {0x12,0x00,D|W|O|B|K,"Address mark not found for id field"},
+ {0x13,0x00,D|W|O|B|K,"Address mark not found for data field"},
+ {0x14,0x00,D|T|L|W|R|S|O|B|K,"Recorded entity not found"},
+ {0x14,0x01,D|T|W|R|O|B|K,"Record not found"},
{0x14,0x02,T,"Filemark or setmark not found"},
{0x14,0x03,T,"End-of-data not found"},
{0x14,0x04,T,"Block sequence error"},
- {0x15,0x00,D|T|L|W|R|S|O|M,"Random positioning error"},
- {0x15,0x01,D|T|L|W|R|S|O|M,"Mechanical positioning error"},
- {0x15,0x02,D|T|W|R|O,"Positioning error detected by read of medium"},
- {0x16,0x00,D|W|O,"Data synchronization mark error"},
- {0x17,0x00,D|T|W|R|S|O,"Recovered data with no error correction applied"},
- {0x17,0x01,D|T|W|R|S|O,"Recovered data with retries"},
- {0x17,0x02,D|T|W|R|O,"Recovered data with positive head offset"},
- {0x17,0x03,D|T|W|R|O,"Recovered data with negative head offset"},
- {0x17,0x04,W|R|O,"Recovered data with retries and/or circ applied"},
- {0x17,0x05,D|W|R|O,"Recovered data using previous sector id"},
- {0x17,0x06,D|W|O,"Recovered data without ecc - data auto-reallocated"},
- {0x17,0x07,D|W|O,"Recovered data without ecc - recommend reassignment"},
- {0x18,0x00,D|T|W|R|O,"Recovered data with error correction applied"},
- {0x18,0x01,D|W|R|O,"Recovered data with error correction and retries applied"},
- {0x18,0x02,D|W|R|O,"Recovered data - data auto-reallocated"},
- {0x18,0x03,R,"Recovered data with circ"},
- {0x18,0x04,R,"Recovered data with lec"},
- {0x18,0x05,D|W|R|O,"Recovered data - recommend reassignment"},
- {0x19,0x00,D|O,"Defect list error"},
- {0x19,0x01,D|O,"Defect list not available"},
- {0x19,0x02,D|O,"Defect list error in primary list"},
- {0x19,0x03,D|O,"Defect list error in grown list"},
- {0x1A,0x00,D|T|L|P|W|R|S|O|M|C,"Parameter list length error"},
- {0x1B,0x00,D|T|L|P|W|R|S|O|M|C,"Synchronous data transfer error"},
- {0x1C,0x00,D|O,"Defect list not found"},
- {0x1C,0x01,D|O,"Primary defect list not found"},
- {0x1C,0x02,D|O,"Grown defect list not found"},
- {0x1D,0x00,D|W|O,"Miscompare during verify operation"},
- {0x1E,0x00,D|W|O,"Recovered id with ecc correction"},
- {0x20,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid command operation code"},
- {0x21,0x00,D|T|W|R|O|M,"Logical block address out of range"},
- {0x21,0x01,M,"Invalid element address"},
- {0x22,0x00,D,"Illegal function (should use 20 00, 24 00, or 26 00)"},
- {0x24,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid field in cdb"},
- {0x25,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit not supported"},
- {0x26,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid field in parameter list"},
- {0x26,0x01,D|T|L|P|W|R|S|O|M|C,"Parameter not supported"},
- {0x26,0x02,D|T|L|P|W|R|S|O|M|C,"Parameter value invalid"},
- {0x26,0x03,D|T|L|P|W|R|S|O|M|C,"Threshold parameters not supported"},
- {0x27,0x00,D|T|W|O,"Write protected"},
- {0x28,0x00,D|T|L|P|W|R|S|O|M|C,"Not ready to ready transition (medium may have changed)"},
- {0x28,0x01,M,"Import or export element accessed"},
- {0x29,0x00,D|T|L|P|W|R|S|O|M|C,"Power on, reset, or bus device reset occurred"},
- {0x2A,0x00,D|T|L|W|R|S|O|M|C,"Parameters changed"},
- {0x2A,0x01,D|T|L|W|R|S|O|M|C,"Mode parameters changed"},
- {0x2A,0x02,D|T|L|W|R|S|O|M|C,"Log parameters changed"},
- {0x2B,0x00,D|T|L|P|W|R|S|O|C,"Copy cannot execute since host cannot disconnect"},
- {0x2C,0x00,D|T|L|P|W|R|S|O|M|C,"Command sequence error"},
+ {0x14,0x05,D|T|W|O|B|K,"Record not found - recommend reassignment"},
+ {0x14,0x06,D|T|W|O|B|K,"Record not found - data auto-reallocated"},
+ {0x15,0x00,D|T|L|W|R|S|O|M|B|K,"Random positioning error"},
+ {0x15,0x01,D|T|L|W|R|S|O|M|B|K,"Mechanical positioning error"},
+ {0x15,0x02,D|T|W|R|O|B|K,"Positioning error detected by read of medium"},
+ {0x16,0x00,D|W|O|B|K,"Data synchronization mark error"},
+ {0x16,0x01,D|W|O|B|K,"Data sync error - data rewritten"},
+ {0x16,0x02,D|W|O|B|K,"Data sync error - recommend rewrite"},
+ {0x16,0x03,D|W|O|B|K,"Data sync error - data auto-reallocated"},
+ {0x16,0x04,D|W|O|B|K,"Data sync error - recommend reassignment"},
+ {0x17,0x00,D|T|W|R|S|O|B|K,"Recovered data with no error correction applied"},
+ {0x17,0x01,D|T|W|R|S|O|B|K,"Recovered data with retries"},
+ {0x17,0x02,D|T|W|R|O|B|K,"Recovered data with positive head offset"},
+ {0x17,0x03,D|T|W|R|O|B|K,"Recovered data with negative head offset"},
+ {0x17,0x04,W|R|O|B,"Recovered data with retries and/or circ applied"},
+ {0x17,0x05,D|W|R|O|B|K,"Recovered data using previous sector id"},
+ {0x17,0x06,D|W|O|B|K,"Recovered data without ecc - data auto-reallocated"},
+ {0x17,0x07,D|W|R|O|B|K,"Recovered data without ecc - recommend reassignment"},
+ {0x17,0x08,D|W|R|O|B|K,"Recovered data without ecc - recommend rewrite"},
+ {0x17,0x09,D|W|R|O|B|K,"Recovered data without ecc - data rewritten"},
+ {0x18,0x00,D|T|W|R|O|B|K,"Recovered data with error correction applied"},
+ {0x18,0x01,D|W|R|O|B|K,"Recovered data with error corr. & retries applied"},
+ {0x18,0x02,D|W|R|O|B|K,"Recovered data - data auto-reallocated"},
+ {0x18,0x03,R,"Recovered data with CIRC"},
+ {0x18,0x04,R,"Recovered data with L-EC"},
+ {0x18,0x05,D|W|R|O|B|K,"Recovered data - recommend reassignment"},
+ {0x18,0x06,D|W|R|O|B|K,"Recovered data - recommend rewrite"},
+ {0x18,0x07,D|W|O|B|K,"Recovered data with ecc - data rewritten"},
+ {0x19,0x00,D|O|K,"Defect list error"},
+ {0x19,0x01,D|O|K,"Defect list not available"},
+ {0x19,0x02,D|O|K,"Defect list error in primary list"},
+ {0x19,0x03,D|O|K,"Defect list error in grown list"},
+ {0x1A,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Parameter list length error"},
+ {0x1B,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Synchronous data transfer error"},
+ {0x1C,0x00,D|O|B|K,"Defect list not found"},
+ {0x1C,0x01,D|O|B|K,"Primary defect list not found"},
+ {0x1C,0x02,D|O|B|K,"Grown defect list not found"},
+ {0x1D,0x00,D|T|W|R|O|B|K,"Miscompare during verify operation"},
+ {0x1E,0x00,D|W|O|B|K,"Recovered id with ecc correction"},
+ {0x1F,0x00,D|O|K,"Partial defect list transfer"},
+ {0x20,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid command operation code"},
+ {0x21,0x00,D|T|W|R|O|M|B|K,"Logical block address out of range"},
+ {0x21,0x01,D|T|W|R|O|M|B|K,"Invalid element address"},
+ {0x22,0x00,D,"Illegal function (use 20 00,24 00,or 26 00)"},
+ {0x24,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid field in cdb"},
+ {0x24,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"CDB decryption error"},
+ {0x25,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not supported"},
+ {0x26,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid field in parameter list"},
+ {0x26,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Parameter not supported"},
+ {0x26,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Parameter value invalid"},
+ {0x26,0x03,D|T|L|P|W|R|S|O|M|C|A|E|K,"Threshold parameters not supported"},
+ {0x26,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid release of persistent reservation"},
+ {0x26,0x05,D|T|L|P|W|R|S|O|M|C|A|B|K,"Data decryption error"},
+ {0x26,0x06,D|T|L|P|W|R|S|O|C|K,"Too many target descriptors"},
+ {0x26,0x07,D|T|L|P|W|R|S|O|C|K,"Unsupported target descriptor type code"},
+ {0x26,0x08,D|T|L|P|W|R|S|O|C|K,"Too many segment descriptors"},
+ {0x26,0x09,D|T|L|P|W|R|S|O|C|K,"Unsupported segment descriptor type code"},
+ {0x26,0x0A,D|T|L|P|W|R|S|O|C|K,"Unexpected inexact segment"},
+ {0x26,0x0B,D|T|L|P|W|R|S|O|C|K,"Inline data length exceeded"},
+ {0x26,0x0C,D|T|L|P|W|R|S|O|C|K,"Invalid operation for copy source or destination"},
+ {0x26,0x0D,D|T|L|P|W|R|S|O|C|K,"Copy segment granularity violation"},
+ {0x27,0x00,D|T|W|R|O|B|K,"Write protected"},
+ {0x27,0x01,D|T|W|R|O|B|K,"Hardware write protected"},
+ {0x27,0x02,D|T|W|R|O|B|K,"Logical unit software write protected"},
+ {0x27,0x03,T|R,"Associated write protect"},
+ {0x27,0x04,T|R,"Persistent write protect"},
+ {0x27,0x05,T|R,"Permanent write protect"},
+ {0x28,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Not ready to ready change,medium may have changed"},
+ {0x28,0x01,D|T|W|R|O|M|B,"Import or export element accessed"},
+ {0x29,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Power on,reset,or bus device reset occurred"},
+ {0x29,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Power on occurred"},
+ {0x29,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Scsi bus reset occurred"},
+ {0x29,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Bus device reset function occurred"},
+ {0x29,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Device internal reset"},
+ {0x29,0x05,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Transceiver mode changed to single-ended"},
+ {0x29,0x06,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Transceiver mode changed to lvd"},
+ {0x2A,0x00,D|T|L|W|R|S|O|M|C|A|E|B|K,"Parameters changed"},
+ {0x2A,0x01,D|T|L|W|R|S|O|M|C|A|E|B|K,"Mode parameters changed"},
+ {0x2A,0x02,D|T|L|W|R|S|O|M|C|A|E|K,"Log parameters changed"},
+ {0x2A,0x03,D|T|L|P|W|R|S|O|M|C|A|E|K,"Reservations preempted"},
+ {0x2A,0x04,D|T|L|P|W|R|S|O|M|C|A|E,"Reservations released"},
+ {0x2A,0x05,D|T|L|P|W|R|S|O|M|C|A|E,"Registrations preempted"},
+ {0x2B,0x00,D|T|L|P|W|R|S|O|C|K,"Copy cannot execute since host cannot disconnect"},
+ {0x2C,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Command sequence error"},
{0x2C,0x01,S,"Too many windows specified"},
{0x2C,0x02,S,"Invalid combination of windows specified"},
+ {0x2C,0x03,R,"Current program area is not empty"},
+ {0x2C,0x04,R,"Current program area is empty"},
+ {0x2C,0x05,B,"Illegal power condition request"},
{0x2D,0x00,T,"Overwrite error on update in place"},
- {0x2F,0x00,D|T|L|P|W|R|S|O|M|C,"Commands cleared by another initiator"},
- {0x30,0x00,D|T|W|R|O|M,"Incompatible medium installed"},
- {0x30,0x01,D|T|W|R|O,"Cannot read medium - unknown format"},
- {0x30,0x02,D|T|W|R|O,"Cannot read medium - incompatible format"},
- {0x30,0x03,D|T,"Cleaning cartridge installed"},
- {0x31,0x00,D|T|W|O,"Medium format corrupted"},
- {0x31,0x01,D|L|O,"Format command failed"},
- {0x32,0x00,D|W|O,"No defect spare location available"},
- {0x32,0x01,D|W|O,"Defect list update failure"},
+ {0x2F,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Commands cleared by another initiator"},
+ {0x30,0x00,D|T|W|R|O|M|B|K,"Incompatible medium installed"},
+ {0x30,0x01,D|T|W|R|O|B|K,"Cannot read medium - unknown format"},
+ {0x30,0x02,D|T|W|R|O|B|K,"Cannot read medium - incompatible format"},
+ {0x30,0x03,D|T|R|K,"Cleaning cartridge installed"},
+ {0x30,0x04,D|T|W|R|O|B|K,"Cannot write medium - unknown format"},
+ {0x30,0x05,D|T|W|R|O|B|K,"Cannot write medium - incompatible format"},
+ {0x30,0x06,D|T|W|R|O|B,"Cannot format medium - incompatible medium"},
+ {0x30,0x07,D|T|L|W|R|S|O|M|A|E|B|K,"Cleaning failure"},
+ {0x30,0x08,R,"Cannot write - application code mismatch"},
+ {0x30,0x09,R,"Current session not fixated for append"},
+ {0x31,0x00,D|T|W|R|O|B|K,"Medium format corrupted"},
+ {0x31,0x01,D|L|R|O|B,"Format command failed"},
+ {0x32,0x00,D|W|O|B|K,"No defect spare location available"},
+ {0x32,0x01,D|W|O|B|K,"Defect list update failure"},
{0x33,0x00,T,"Tape length error"},
- {0x36,0x00,L,"Ribbon, ink, or toner failure"},
- {0x37,0x00,D|T|L|W|R|S|O|M|C,"Rounded parameter"},
- {0x39,0x00,D|T|L|W|R|S|O|M|C,"Saving parameters not supported"},
- {0x3A,0x00,D|T|L|W|R|S|O|M,"Medium not present"},
+ {0x34,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure failure"},
+ {0x35,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure services failure"},
+ {0x35,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Unsupported enclosure function"},
+ {0x35,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure services unavailable"},
+ {0x35,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure services transfer failure"},
+ {0x35,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure services transfer refused"},
+ {0x36,0x00,L,"Ribbon,ink,or toner failure"},
+ {0x37,0x00,D|T|L|W|R|S|O|M|C|A|E|B|K,"Rounded parameter"},
+ {0x38,0x00,B,"Event status notification"},
+ {0x38,0x02,B,"Esn - power management class event"},
+ {0x38,0x04,B,"Esn - media class event"},
+ {0x38,0x06,B,"Esn - device busy class event"},
+ {0x39,0x00,D|T|L|W|R|S|O|M|C|A|E|K,"Saving parameters not supported"},
+ {0x3A,0x00,D|T|L|W|R|S|O|M|B|K,"Medium not present"},
+ {0x3A,0x01,D|T|W|R|O|M|B|K,"Medium not present - tray closed"},
+ {0x3A,0x02,D|T|W|R|O|M|B|K,"Medium not present - tray open"},
+ {0x3A,0x03,D|T|W|R|O|M|B,"Medium not present - loadable"},
+ {0x3A,0x04,D|T|W|R|O|M|B,"Medium not present - medium auxiliary memory accessible"},
{0x3B,0x00,T|L,"Sequential positioning error"},
{0x3B,0x01,T,"Tape position error at beginning-of-medium"},
{0x3B,0x02,T,"Tape position error at end-of-medium"},
{0x3B,0x09,S,"Read past end of medium"},
{0x3B,0x0A,S,"Read past beginning of medium"},
{0x3B,0x0B,S,"Position past end of medium"},
- {0x3B,0x0C,S,"Position past beginning of medium"},
- {0x3B,0x0D,M,"Medium destination element full"},
- {0x3B,0x0E,M,"Medium source element empty"},
- {0x3D,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid bits in identify message"},
- {0x3E,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit has not self-configured yet"},
- {0x3F,0x00,D|T|L|P|W|R|S|O|M|C,"Target operating conditions have changed"},
- {0x3F,0x01,D|T|L|P|W|R|S|O|M|C,"Microcode has been changed"},
- {0x3F,0x02,D|T|L|P|W|R|S|O|M|C,"Changed operating definition"},
- {0x3F,0x03,D|T|L|P|W|R|S|O|M|C,"Inquiry data has changed"},
- {0x43,0x00,D|T|L|P|W|R|S|O|M|C,"Message error"},
- {0x44,0x00,D|T|L|P|W|R|S|O|M|C,"Internal target failure"},
- {0x45,0x00,D|T|L|P|W|R|S|O|M|C,"Select or reselect failure"},
- {0x46,0x00,D|T|L|P|W|R|S|O|M|C,"Unsuccessful soft reset"},
- {0x47,0x00,D|T|L|P|W|R|S|O|M|C,"Scsi parity error"},
- {0x48,0x00,D|T|L|P|W|R|S|O|M|C,"Initiator detected error message received"},
- {0x49,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid message error"},
- {0x4A,0x00,D|T|L|P|W|R|S|O|M|C,"Command phase error"},
- {0x4B,0x00,D|T|L|P|W|R|S|O|M|C,"Data phase error"},
- {0x4C,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit failed self-configuration"},
- {0x4E,0x00,D|T|L|P|W|R|S|O|M|C,"Overlapped commands attempted"},
+ {0x3B,0x0C,T|S,"Position past beginning of medium"},
+ {0x3B,0x0D,D|T|W|R|O|M|B|K,"Medium destination element full"},
+ {0x3B,0x0E,D|T|W|R|O|M|B|K,"Medium source element empty"},
+ {0x3B,0x0F,R,"End of medium reached"},
+ {0x3B,0x11,D|T|W|R|O|M|B|K,"Medium magazine not accessible"},
+ {0x3B,0x12,D|T|W|R|O|M|B|K,"Medium magazine removed"},
+ {0x3B,0x13,D|T|W|R|O|M|B|K,"Medium magazine inserted"},
+ {0x3B,0x14,D|T|W|R|O|M|B|K,"Medium magazine locked"},
+ {0x3B,0x15,D|T|W|R|O|M|B|K,"Medium magazine unlocked"},
+ {0x3B,0x16,R,"Mechanical positioning or changer error"},
+ {0x3D,0x00,D|T|L|P|W|R|S|O|M|C|A|E|K,"Invalid bits in identify message"},
+ {0x3E,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit has not self-configured yet"},
+ {0x3E,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit failure"},
+ {0x3E,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Timeout on logical unit"},
+ {0x3E,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit failed self-test"},
+ {0x3E,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit unable to update self-test log"},
+ {0x3F,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Target operating conditions have changed"},
+ {0x3F,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Microcode has been changed"},
+ {0x3F,0x02,D|T|L|P|W|R|S|O|M|C|B|K,"Changed operating definition"},
+ {0x3F,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Inquiry data has changed"},
+ {0x3F,0x04,D|T|W|R|O|M|C|A|E|B|K,"Component device attached"},
+ {0x3F,0x05,D|T|W|R|O|M|C|A|E|B|K,"Device identifier changed"},
+ {0x3F,0x06,D|T|W|R|O|M|C|A|E|B,"Redundancy group created or modified"},
+ {0x3F,0x07,D|T|W|R|O|M|C|A|E|B,"Redundancy group deleted"},
+ {0x3F,0x08,D|T|W|R|O|M|C|A|E|B,"Spare created or modified"},
+ {0x3F,0x09,D|T|W|R|O|M|C|A|E|B,"Spare deleted"},
+ {0x3F,0x0A,D|T|W|R|O|M|C|A|E|B|K,"Volume set created or modified"},
+ {0x3F,0x0B,D|T|W|R|O|M|C|A|E|B|K,"Volume set deleted"},
+ {0x3F,0x0C,D|T|W|R|O|M|C|A|E|B|K,"Volume set deassigned"},
+ {0x3F,0x0D,D|T|W|R|O|M|C|A|E|B|K,"Volume set reassigned"},
+ {0x3F,0x0E,D|T|L|P|W|R|S|O|M|C|A|E,"Reported luns data has changed"},
+ {0x3F,0x10,D|T|W|R|O|M|B,"Medium loadable"},
+ {0x3F,0x11,D|T|W|R|O|M|B,"Medium auxiliary memory accessible"},
+ {0x40,0x00,D,"Ram failure (should use 40 nn)"},
+ /*
+ * FIXME(eric) - need a way to represent wildcards here.
+ */
+ {0x40,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Diagnostic failure on component nn (80h-ffh)"},
+ {0x41,0x00,D,"Data path failure (should use 40 nn)"},
+ {0x42,0x00,D,"Power-on or self-test failure (should use 40 nn)"},
+ {0x43,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Message error"},
+ {0x44,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Internal target failure"},
+ {0x45,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Select or reselect failure"},
+ {0x46,0x00,D|T|L|P|W|R|S|O|M|C|B|K,"Unsuccessful soft reset"},
+ {0x47,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Scsi parity error"},
+ {0x47,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Data phase CRC error detected"},
+ {0x47,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Scsi parity error detected during st data phase"},
+ {0x47,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Information unit CRC error detected"},
+ {0x47,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Asynchronous information protection error detected"},
+ {0x48,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Initiator detected error message received"},
+ {0x49,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid message error"},
+ {0x4A,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Command phase error"},
+ {0x4B,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Data phase error"},
+ {0x4C,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit failed self-configuration"},
+ /*
+ * FIXME(eric) - need a way to represent wildcards here.
+ */
+ {0x4D,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Tagged overlapped commands (nn = queue tag)"},
+ {0x4E,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Overlapped commands attempted"},
{0x50,0x00,T,"Write append error"},
{0x50,0x01,T,"Write append position error"},
{0x50,0x02,T,"Position error related to timing"},
- {0x51,0x00,T|O,"Erase failure"},
+ {0x51,0x00,T|R|O,"Erase failure"},
{0x52,0x00,T,"Cartridge fault"},
- {0x53,0x00,D|T|L|W|R|S|O|M,"Media load or eject failed"},
+ {0x53,0x00,D|T|L|W|R|S|O|M|B|K,"Media load or eject failed"},
{0x53,0x01,T,"Unload tape failure"},
- {0x53,0x02,D|T|W|R|O|M,"Medium removal prevented"},
+ {0x53,0x02,D|T|W|R|O|M|B|K,"Medium removal prevented"},
{0x54,0x00,P,"Scsi to host system interface failure"},
{0x55,0x00,P,"System resource failure"},
+ {0x55,0x01,D|O|B|K,"System buffer full"},
+ {0x55,0x02,D|T|L|P|W|R|S|O|M|A|E|K,"Insufficient reservation resources"},
+ {0x55,0x03,D|T|L|P|W|R|S|O|M|C|A|E,"Insufficient resources"},
+ {0x55,0x04,D|T|L|P|W|R|S|O|M|A|E,"Insufficient registration resources"},
{0x57,0x00,R,"Unable to recover table-of-contents"},
{0x58,0x00,O,"Generation does not exist"},
{0x59,0x00,O,"Updated block read"},
- {0x5A,0x00,D|T|L|P|W|R|S|O|M,"Operator request or state change input (unspecified)"},
- {0x5A,0x01,D|T|W|R|O|M,"Operator medium removal request"},
- {0x5A,0x02,D|T|W|O,"Operator selected write protect"},
- {0x5A,0x03,D|T|W|O,"Operator selected write permit"},
- {0x5B,0x00,D|T|L|P|W|R|S|O|M,"Log exception"},
- {0x5B,0x01,D|T|L|P|W|R|S|O|M,"Threshold condition met"},
- {0x5B,0x02,D|T|L|P|W|R|S|O|M,"Log counter at maximum"},
- {0x5B,0x03,D|T|L|P|W|R|S|O|M,"Log list codes exhausted"},
+ {0x5A,0x00,D|T|L|P|W|R|S|O|M|B|K,"Operator request or state change input"},
+ {0x5A,0x01,D|T|W|R|O|M|B|K,"Operator medium removal request"},
+ {0x5A,0x02,D|T|W|R|O|A|B|K,"Operator selected write protect"},
+ {0x5A,0x03,D|T|W|R|O|A|B|K,"Operator selected write permit"},
+ {0x5B,0x00,D|T|L|P|W|R|S|O|M|K,"Log exception"},
+ {0x5B,0x01,D|T|L|P|W|R|S|O|M|K,"Threshold condition met"},
+ {0x5B,0x02,D|T|L|P|W|R|S|O|M|K,"Log counter at maximum"},
+ {0x5B,0x03,D|T|L|P|W|R|S|O|M|K,"Log list codes exhausted"},
{0x5C,0x00,D|O,"Rpl status change"},
{0x5C,0x01,D|O,"Spindles synchronized"},
{0x5C,0x02,D|O,"Spindles not synchronized"},
+ {0x5D,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Failure prediction threshold exceeded"},
+ {0x5D,0x01,R|B,"Media failure prediction threshold exceeded"},
+ {0x5D,0x02,R,"Logical unit failure prediction threshold exceeded"},
+ {0x5D,0x10,D|B,"Hardware impending failure general hard drive failure"},
+ {0x5D,0x11,D|B,"Hardware impending failure drive error rate too high"},
+ {0x5D,0x12,D|B,"Hardware impending failure data error rate too high"},
+ {0x5D,0x13,D|B,"Hardware impending failure seek error rate too high"},
+ {0x5D,0x14,D|B,"Hardware impending failure too many block reassigns"},
+ {0x5D,0x15,D|B,"Hardware impending failure access times too high"},
+ {0x5D,0x16,D|B,"Hardware impending failure start unit times too high"},
+ {0x5D,0x17,D|B,"Hardware impending failure channel parametrics"},
+ {0x5D,0x18,D|B,"Hardware impending failure controller detected"},
+ {0x5D,0x19,D|B,"Hardware impending failure throughput performance"},
+ {0x5D,0x1A,D|B,"Hardware impending failure seek time performance"},
+ {0x5D,0x1B,D|B,"Hardware impending failure spin-up retry count"},
+ {0x5D,0x1C,D|B,"Hardware impending failure drive calibration retry count"},
+ {0x5D,0x20,D|B,"Controller impending failure general hard drive failure"},
+ {0x5D,0x21,D|B,"Controller impending failure drive error rate too high"},
+ {0x5D,0x22,D|B,"Controller impending failure data error rate too high"},
+ {0x5D,0x23,D|B,"Controller impending failure seek error rate too high"},
+ {0x5D,0x24,D|B,"Controller impending failure too many block reassigns"},
+ {0x5D,0x25,D|B,"Controller impending failure access times too high"},
+ {0x5D,0x26,D|B,"Controller impending failure start unit times too high"},
+ {0x5D,0x27,D|B,"Controller impending failure channel parametrics"},
+ {0x5D,0x28,D|B,"Controller impending failure controller detected"},
+ {0x5D,0x29,D|B,"Controller impending failure throughput performance"},
+ {0x5D,0x2A,D|B,"Controller impending failure seek time performance"},
+ {0x5D,0x2B,D|B,"Controller impending failure spin-up retry count"},
+ {0x5D,0x2C,D|B,"Controller impending failure drive calibration retry count"},
+ {0x5D,0x30,D|B,"Data channel impending failure general hard drive failure"},
+ {0x5D,0x31,D|B,"Data channel impending failure drive error rate too high"},
+ {0x5D,0x32,D|B,"Data channel impending failure data error rate too high"},
+ {0x5D,0x33,D|B,"Data channel impending failure seek error rate too high"},
+ {0x5D,0x34,D|B,"Data channel impending failure too many block reassigns"},
+ {0x5D,0x35,D|B,"Data channel impending failure access times too high"},
+ {0x5D,0x36,D|B,"Data channel impending failure start unit times too high"},
+ {0x5D,0x37,D|B,"Data channel impending failure channel parametrics"},
+ {0x5D,0x38,D|B,"Data channel impending failure controller detected"},
+ {0x5D,0x39,D|B,"Data channel impending failure throughput performance"},
+ {0x5D,0x3A,D|B,"Data channel impending failure seek time performance"},
+ {0x5D,0x3B,D|B,"Data channel impending failure spin-up retry count"},
+ {0x5D,0x3C,D|B,"Data channel impending failure drive calibration retry count"},
+ {0x5D,0x40,D|B,"Servo impending failure general hard drive failure"},
+ {0x5D,0x41,D|B,"Servo impending failure drive error rate too high"},
+ {0x5D,0x42,D|B,"Servo impending failure data error rate too high"},
+ {0x5D,0x43,D|B,"Servo impending failure seek error rate too high"},
+ {0x5D,0x44,D|B,"Servo impending failure too many block reassigns"},
+ {0x5D,0x45,D|B,"Servo impending failure access times too high"},
+ {0x5D,0x46,D|B,"Servo impending failure start unit times too high"},
+ {0x5D,0x47,D|B,"Servo impending failure channel parametrics"},
+ {0x5D,0x48,D|B,"Servo impending failure controller detected"},
+ {0x5D,0x49,D|B,"Servo impending failure throughput performance"},
+ {0x5D,0x4A,D|B,"Servo impending failure seek time performance"},
+ {0x5D,0x4B,D|B,"Servo impending failure spin-up retry count"},
+ {0x5D,0x4C,D|B,"Servo impending failure drive calibration retry count"},
+ {0x5D,0x50,D|B,"Spindle impending failure general hard drive failure"},
+ {0x5D,0x51,D|B,"Spindle impending failure drive error rate too high"},
+ {0x5D,0x52,D|B,"Spindle impending failure data error rate too high"},
+ {0x5D,0x53,D|B,"Spindle impending failure seek error rate too high"},
+ {0x5D,0x54,D|B,"Spindle impending failure too many block reassigns"},
+ {0x5D,0x55,D|B,"Spindle impending failure access times too high"},
+ {0x5D,0x56,D|B,"Spindle impending failure start unit times too high"},
+ {0x5D,0x57,D|B,"Spindle impending failure channel parametrics"},
+ {0x5D,0x58,D|B,"Spindle impending failure controller detected"},
+ {0x5D,0x59,D|B,"Spindle impending failure throughput performance"},
+ {0x5D,0x5A,D|B,"Spindle impending failure seek time performance"},
+ {0x5D,0x5B,D|B,"Spindle impending failure spin-up retry count"},
+ {0x5D,0x5C,D|B,"Spindle impending failure drive calibration retry count"},
+ {0x5D,0x60,D|B,"Firmware impending failure general hard drive failure"},
+ {0x5D,0x61,D|B,"Firmware impending failure drive error rate too high"},
+ {0x5D,0x62,D|B,"Firmware impending failure data error rate too high"},
+ {0x5D,0x63,D|B,"Firmware impending failure seek error rate too high"},
+ {0x5D,0x64,D|B,"Firmware impending failure too many block reassigns"},
+ {0x5D,0x65,D|B,"Firmware impending failure access times too high"},
+ {0x5D,0x66,D|B,"Firmware impending failure start unit times too high"},
+ {0x5D,0x67,D|B,"Firmware impending failure channel parametrics"},
+ {0x5D,0x68,D|B,"Firmware impending failure controller detected"},
+ {0x5D,0x69,D|B,"Firmware impending failure throughput performance"},
+ {0x5D,0x6A,D|B,"Firmware impending failure seek time performance"},
+ {0x5D,0x6B,D|B,"Firmware impending failure spin-up retry count"},
+ {0x5D,0x6C,D|B,"Firmware impending failure drive calibration retry count"},
+ {0x5D,0xFF,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Failure prediction threshold exceeded (false)"},
+ {0x5E,0x00,D|T|L|P|W|R|S|O|C|A|K,"Low power condition on"},
+ {0x5E,0x01,D|T|L|P|W|R|S|O|C|A|K,"Idle condition activated by timer"},
+ {0x5E,0x02,D|T|L|P|W|R|S|O|C|A|K,"Standby condition activated by timer"},
+ {0x5E,0x03,D|T|L|P|W|R|S|O|C|A|K,"Idle condition activated by command"},
+ {0x5E,0x04,D|T|L|P|W|R|S|O|C|A|K,"Standby condition activated by command"},
+ {0x5E,0x41,B,"Power state change to active"},
+ {0x5E,0x42,B,"Power state change to idle"},
+ {0x5E,0x43,B,"Power state change to standby"},
+ {0x5E,0x45,B,"Power state change to sleep"},
+ {0x5E,0x47,B|K,"Power state change to device control"},
{0x60,0x00,S,"Lamp failure"},
{0x61,0x00,S,"Video acquisition error"},
{0x61,0x01,S,"Unable to acquire video"},
{0x61,0x02,S,"Out of focus"},
{0x62,0x00,S,"Scan head positioning error"},
{0x63,0x00,R,"End of user area encountered on this track"},
+ {0x63,0x01,R,"Packet does not fit in available space"},
{0x64,0x00,R,"Illegal mode for this track"},
+ {0x64,0x01,R,"Invalid packet size"},
+ {0x65,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Voltage fault"},
+ {0x66,0x00,S,"Automatic document feeder cover up"},
+ {0x66,0x01,S,"Automatic document feeder lift up"},
+ {0x66,0x02,S,"Document jam in automatic document feeder"},
+ {0x66,0x03,S,"Document miss feed automatic in document feeder"},
+ {0x67,0x00,A,"Configuration failure"},
+ {0x67,0x01,A,"Configuration of incapable logical units failed"},
+ {0x67,0x02,A,"Add logical unit failed"},
+ {0x67,0x03,A,"Modification of logical unit failed"},
+ {0x67,0x04,A,"Exchange of logical unit failed"},
+ {0x67,0x05,A,"Remove of logical unit failed"},
+ {0x67,0x06,A,"Attachment of logical unit failed"},
+ {0x67,0x07,A,"Creation of logical unit failed"},
+ {0x67,0x08,A,"Assign failure occurred"},
+ {0x67,0x09,A,"Multiply assigned logical unit"},
+ {0x68,0x00,A,"Logical unit not configured"},
+ {0x69,0x00,A,"Data loss on logical unit"},
+ {0x69,0x01,A,"Multiple logical unit failures"},
+ {0x69,0x02,A,"Parity/data mismatch"},
+ {0x6A,0x00,A,"Informational,refer to log"},
+ {0x6B,0x00,A,"State change has occurred"},
+ {0x6B,0x01,A,"Redundancy level got better"},
+ {0x6B,0x02,A,"Redundancy level got worse"},
+ {0x6C,0x00,A,"Rebuild failure occurred"},
+ {0x6D,0x00,A,"Recalculate failure occurred"},
+ {0x6E,0x00,A,"Command to logical unit failed"},
+ {0x6F,0x00,R,"Copy protection key exchange failure - authentication failure"},
+ {0x6F,0x01,R,"Copy protection key exchange failure - key not present"},
+ {0x6F,0x02,R,"Copy protection key exchange failure - key not established"},
+ {0x6F,0x03,R,"Read of scrambled sector without authentication"},
+ {0x6F,0x04,R,"Media region code is mismatched to logical unit region"},
+ {0x6F,0x05,R,"Drive region must be permanent/region reset count error"},
+ /*
+ * FIXME(eric) - need a way to represent wildcards here.
+ */
+ {0x70,0x00,T,"Decompression exception short algorithm id of nn"},
+ {0x71,0x00,T,"Decompression exception long algorithm id"},
+ {0x72,0x00,R,"Session fixation error"},
+ {0x72,0x01,R,"Session fixation error writing lead-in"},
+ {0x72,0x02,R,"Session fixation error writing lead-out"},
+ {0x72,0x03,R,"Session fixation error - incomplete track in session"},
+ {0x72,0x04,R,"Empty or partially written reserved track"},
+ {0x72,0x05,R,"No more track reservations allowed"},
+ {0x73,0x00,R,"Cd control error"},
+ {0x73,0x01,R,"Power calibration area almost full"},
+ {0x73,0x02,R,"Power calibration area is full"},
+ {0x73,0x03,R,"Power calibration area error"},
+ {0x73,0x04,R,"Program memory area update failure"},
+ {0x73,0x05,R,"Program memory area is full"},
+ {0x73,0x06,R,"RMA/PMA is full"},
{0, 0, 0, NULL}
};
#endif
Scsi_Device *scd, *SDev;
struct Scsi_Host *HBA_ptr;
- Scsi_Cmnd * scmd;
+ Scsi_Request * scmd;
char cmnd[MAX_COMMAND_SIZE];
static u8 buff[512];
static u8 buff2[512];
} else {
SDev = scsi_get_host_dev(HBA_ptr);
- scmd = scsi_allocate_device(SDev, 1, FALSE);
+ scmd = scsi_allocate_request(SDev);
cmnd[0] = LOG_SENSE;
cmnd[1] = 0;
cmnd[8] = 0x66;
cmnd[9] = 0;
- scmd->cmd_len = 10;
- scmd->sc_data_direction = SCSI_DATA_READ;
+ scmd->sr_cmd_len = 10;
+ scmd->sr_data_direction = SCSI_DATA_READ;
/*
* Do the command and wait for it to finish.
*/
- scsi_wait_cmd (scmd, cmnd, buff + 0x144, 0x66,
+ scsi_wait_req (scmd, cmnd, buff + 0x144, 0x66,
1 * HZ, 1);
size = sprintf(buffer + len, "IRQ: %2d, %s triggered\n", cc->interrupt,
cmnd[8] = 0x44;
cmnd[9] = 0;
- scmd->cmd_len = 10;
- scmd->sc_data_direction = SCSI_DATA_READ;
+ scmd->sr_cmd_len = 10;
+ scmd->sr_data_direction = SCSI_DATA_READ;
/*
* Do the command and wait for it to finish.
*/
- scsi_wait_cmd (scmd, cmnd, buff2, 0x144,
+ scsi_wait_req (scmd, cmnd, buff2, 0x144,
1 * HZ, 1);
swap_statistics(buff2);
pos = begin + len;
}
- scsi_release_command(scmd);
+ scsi_release_request(scmd);
scsi_free_host_dev(SDev);
}
* Prototypes for functions/data in scsi_scan.c
*/
extern void scan_scsis(struct Scsi_Host *shpnt,
- unchar hardcoded,
- unchar hchannel,
- unchar hid,
- unchar hlun);
+ uint hardcoded,
+ uint hchannel,
+ uint hid,
+ uint hlun);
extern void scsi_mark_host_reset(struct Scsi_Host *Host);
const char * tag;
struct module * module; /* Used for loadable modules */
unsigned char scsi_type;
- unsigned char major;
- unsigned char min_major; /* Minimum major in range. */
- unsigned char max_major; /* Maximum major in range. */
- unsigned char nr_dev; /* Number currently attached */
- unsigned char dev_noticed; /* Number of devices detected. */
- unsigned char dev_max; /* Current size of arrays */
+ unsigned int major;
+ unsigned int min_major; /* Minimum major in range. */
+ unsigned int max_major; /* Maximum major in range. */
+ unsigned int nr_dev; /* Number currently attached */
+ unsigned int dev_noticed; /* Number of devices detected. */
+ unsigned int dev_max; /* Current size of arrays */
unsigned blk:1; /* 0 if character device */
int (*detect)(Scsi_Device *); /* Returns 1 if we can attach this device */
int (*init)(void); /* Sizes arrays based upon number of devices
* Note: These things are all evil and all need to go away. My plan is to
* tackle the character devices first, as there aren't any locking implications
* in the block device layer. The block devices will require more work.
+ *
+ * The generics driver has been updated to resize as required. So as the tape
+ * driver. Two down, two more to go.
*/
#ifndef CONFIG_SD_EXTRA_DEVS
#define CONFIG_SD_EXTRA_DEVS 2
#endif
-#ifndef CONFIG_ST_EXTRA_DEVS
-#define CONFIG_ST_EXTRA_DEVS 2
-#endif
#ifndef CONFIG_SR_EXTRA_DEVS
#define CONFIG_SR_EXTRA_DEVS 2
#endif
#define SD_EXTRA_DEVS CONFIG_SD_EXTRA_DEVS
-#define ST_EXTRA_DEVS CONFIG_ST_EXTRA_DEVS
#define SR_EXTRA_DEVS CONFIG_SR_EXTRA_DEVS
-#define SG_EXTRA_DEVS (SD_EXTRA_DEVS + SR_EXTRA_DEVS + ST_EXTRA_DEVS)
#endif
/*
}
}
-void scsi_wait_cmd (Scsi_Cmnd * SCpnt, const void *cmnd ,
- void *buffer, unsigned bufflen,
- int timeout, int retries)
-{
- DECLARE_MUTEX_LOCKED(sem);
-
- if (buffer != NULL && SCpnt->sc_data_direction == SCSI_DATA_NONE)
- BUG();
- SCpnt->request.sem = &sem;
- SCpnt->request.rq_status = RQ_SCSI_BUSY;
- scsi_do_cmd (SCpnt, (void *) cmnd,
- buffer, bufflen, scsi_wait_done, timeout, retries);
- down (&sem);
- SCpnt->request.sem = NULL;
-}
-
-
/*
* This lock protects the freelist for all devices on the system.
* We could make this finer grained by having a single lock per
atomic_read(&shpnt->host_active),
shpnt->host_blocked,
shpnt->host_self_blocked);
-
}
printk("\n\n");
void *buffer, unsigned bufflen,
void (*done) (struct scsi_cmnd *),
int timeout, int retries);
-extern void scsi_wait_cmd(Scsi_Cmnd *, const void *cmnd,
- void *buffer, unsigned bufflen,
- int timeout, int retries);
extern int scsi_dev_init(void);
/*
Scsi_Cmnd *device_queue; /* queue of SCSI Command structures */
/* public: */
- unsigned char id, lun, channel;
+ unsigned int id, lun, channel;
unsigned int manufacturer; /* Manufacturer of device, for using
* vendor-specific cmd's */
/* public: */
- unsigned char target;
- unsigned char lun;
- unsigned char channel;
+ unsigned int target;
+ unsigned int lun;
+ unsigned int channel;
unsigned char cmd_len;
unsigned char old_cmd_len;
unsigned char sc_data_direction;
static unsigned char cmd[6] =
{TEST_UNIT_READY, 0, 0, 0, 0, 0};
- Scsi_Cmnd * scp;
+ Scsi_Request * scp;
Scsi_Device * sdev;
printk("Allocating host dev\n");
sdev = scsi_get_host_dev(shpnt);
printk("Got %p. Allocating command block\n", sdev);
- scp = scsi_allocate_device(sdev, 1, FALSE);
+ scp = scsi_allocate_request(sdev);
printk("Got %p\n", scp);
- scp->cmd_len = 6;
- scp->use_sg = 0;
+ scp->sr_cmd_len = 6;
+ scp->sr_use_sg = 0;
printk("Sending command\n");
- scsi_wait_cmd (scp, (void *) cmd, (void *) NULL,
+ scsi_wait_req (scp, (void *) cmd, (void *) NULL,
0, 100, 3);
printk("Releasing command\n");
- scsi_release_command(scp);
+ scsi_release_request(scp);
printk("Freeing device\n");
scsi_free_host_dev(sdev);
}
int timeout, int retries)
{
int result;
- Scsi_Cmnd *SCpnt;
+ Scsi_Request *SRpnt;
Scsi_Device *SDpnt;
SCSI_LOG_IOCTL(1, printk("Trying ioctl with scsi command %d\n", cmd[0]));
- SCpnt = scsi_allocate_device(dev, TRUE, TRUE);
- if( SCpnt == NULL )
- {
- return -EINTR;
- }
+ SRpnt = scsi_allocate_request(dev);
- SCpnt->sc_data_direction = SCSI_DATA_NONE;
- scsi_wait_cmd(SCpnt, cmd, NULL, 0, timeout, retries);
+ SRpnt->sr_data_direction = SCSI_DATA_NONE;
+ scsi_wait_req(SRpnt, cmd, NULL, 0, timeout, retries);
- SCSI_LOG_IOCTL(2, printk("Ioctl returned 0x%x\n", SCpnt->result));
+ SCSI_LOG_IOCTL(2, printk("Ioctl returned 0x%x\n", SRpnt->sr_result));
- if (driver_byte(SCpnt->result) != 0)
- switch (SCpnt->sense_buffer[2] & 0xf) {
+ if (driver_byte(SRpnt->sr_result) != 0)
+ switch (SRpnt->sr_sense_buffer[2] & 0xf) {
case ILLEGAL_REQUEST:
if (cmd[0] == ALLOW_MEDIUM_REMOVAL)
dev->lockable = 0;
case UNIT_ATTENTION:
if (dev->removable) {
dev->changed = 1;
- SCpnt->result = 0; /* This is no longer considered an error */
+ SRpnt->sr_result = 0; /* This is no longer considered an error */
/* gag this error, VFS will log it anyway /axboe */
/* printk(KERN_INFO "Disc change detected.\n"); */
break;
dev->host->host_no,
dev->id,
dev->lun,
- SCpnt->result);
+ SRpnt->sr_result);
printk("\tSense class %x, sense error %x, extended sense %x\n",
- sense_class(SCpnt->sense_buffer[0]),
- sense_error(SCpnt->sense_buffer[0]),
- SCpnt->sense_buffer[2] & 0xf);
+ sense_class(SRpnt->sr_sense_buffer[0]),
+ sense_error(SRpnt->sr_sense_buffer[0]),
+ SRpnt->sr_sense_buffer[2] & 0xf);
};
- result = SCpnt->result;
+ result = SRpnt->sr_result;
SCSI_LOG_IOCTL(2, printk("IOCTL Releasing command\n"));
- SDpnt = SCpnt->device;
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ SDpnt = SRpnt->sr_device;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
return result;
}
char *buf;
unsigned char cmd[MAX_COMMAND_SIZE];
char *cmd_in;
- Scsi_Cmnd *SCpnt;
+ Scsi_Request *SRpnt;
Scsi_Device *SDpnt;
unsigned char opcode;
int inlen, outlen, cmdlen;
return -ENOMEM;
memset(buf, 0, buf_needed);
if( inlen == 0 ) {
- data_direction = SCSI_DATA_WRITE;
- } else if (outlen == 0 ) {
data_direction = SCSI_DATA_READ;
+ } else if (outlen == 0 ) {
+ data_direction = SCSI_DATA_WRITE;
} else {
/*
* Can this ever happen?
#ifndef DEBUG_NO_CMD
- SCpnt = scsi_allocate_device(dev, TRUE, TRUE);
- if( SCpnt == NULL )
+ SRpnt = scsi_allocate_request(dev);
+ if( SRpnt == NULL )
{
return -EINTR;
}
- SCpnt->sc_data_direction = data_direction;
- scsi_wait_cmd(SCpnt, cmd, buf, needed, timeout, retries);
+ SRpnt->sr_data_direction = data_direction;
+ scsi_wait_req(SRpnt, cmd, buf, needed, timeout, retries);
/*
* If there was an error condition, pass the info back to the user.
*/
- if (SCpnt->result) {
- int sb_len = sizeof(SCpnt->sense_buffer);
+ if (SRpnt->sr_result) {
+ int sb_len = sizeof(SRpnt->sr_sense_buffer);
sb_len = (sb_len > OMAX_SB_LEN) ? OMAX_SB_LEN : sb_len;
result = verify_area(VERIFY_WRITE, cmd_in, sb_len);
if (result)
return result;
- copy_to_user(cmd_in, SCpnt->sense_buffer, sb_len);
+ copy_to_user(cmd_in, SRpnt->sr_sense_buffer, sb_len);
} else {
result = verify_area(VERIFY_WRITE, cmd_in, outlen);
if (result)
return result;
copy_to_user(cmd_in, buf, outlen);
}
- result = SCpnt->result;
+ result = SRpnt->sr_result;
- SDpnt = SCpnt->device;
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ SDpnt = SRpnt->sr_device;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
if (buf)
scsi_free(buf, buf_needed);
* be in an interrupt handler. Only do this
* from user space, since we do not want to
* sleep from an interrupt.
+ *
+ * FIXME(eric) - have the error handler thread do
+ * this work.
*/
SDpnt->was_reset = 0;
if (SDpnt->removable && !in_interrupt()) {
if( SRpnt->sr_magic == SCSI_REQ_MAGIC ) {
SCpnt = scsi_allocate_device(SRpnt->sr_device,
FALSE, FALSE);
+ if( !SCpnt ) {
+ break;
+ }
scsi_init_cmd_from_req(SCpnt, SRpnt);
}
* devices to the disk driver.
*/
void scan_scsis(struct Scsi_Host *shpnt,
- unchar hardcoded,
- unchar hchannel,
- unchar hid,
- unchar hlun)
+ uint hardcoded,
+ uint hchannel,
+ uint hid,
+ uint hlun)
{
- int channel;
+ uint channel;
int dev;
int lun;
int max_dev_lun;
SDpnt->host = shpnt;
SDpnt->online = TRUE;
- scsi_build_commandblocks(SDpnt);
-
initialize_merge_fn(SDpnt);
/*
leave:
- { /* Unchain SCpnt from host_queue */
+ { /* Unchain SRpnt from host_queue */
Scsi_Device *prev, *next;
Scsi_Device *dqptr;
}
}
- scsi_release_commandblocks(SDpnt);
-
/* Last device block does not exist. Free memory. */
if (SDpnt != NULL)
kfree((char *) SDpnt);
unsigned char scsi_cmd[MAX_COMMAND_SIZE];
struct Scsi_Device_Template *sdtpnt;
Scsi_Device *SDtail, *SDpnt = *SDpnt2;
- Scsi_Cmnd * SCpnt;
+ Scsi_Request * SRpnt;
int bflags, type = -1;
static int ghost_channel=-1, ghost_dev=-1;
int org_lun = lun;
SDpnt->channel = channel;
SDpnt->online = TRUE;
+ scsi_build_commandblocks(SDpnt);
if ((channel == ghost_channel) && (dev == ghost_dev) && (lun == 1)) {
SDpnt->lun = 0;
scsi_cmd[1] = lun << 5;
scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[4] = scsi_cmd[5] = 0;
- SCpnt = scsi_allocate_device(SDpnt, 0, 0);
+ SRpnt = scsi_allocate_request(SDpnt);
- SCpnt->host = SDpnt->host;
- SCpnt->device = SDpnt;
- SCpnt->target = SDpnt->id;
- SCpnt->lun = SDpnt->lun;
- SCpnt->channel = SDpnt->channel;
- SCpnt->sc_data_direction = SCSI_DATA_NONE;
+ SRpnt->sr_data_direction = SCSI_DATA_NONE;
- scsi_wait_cmd (SCpnt, (void *) scsi_cmd,
+ scsi_wait_req (SRpnt, (void *) scsi_cmd,
(void *) NULL,
0, SCSI_TIMEOUT + 4 * HZ, 5);
SCSI_LOG_SCAN_BUS(3, printk("scsi: scan_scsis_single id %d lun %d. Return code 0x%08x\n",
- dev, lun, SCpnt->result));
- SCSI_LOG_SCAN_BUS(3, print_driverbyte(SCpnt->result));
- SCSI_LOG_SCAN_BUS(3, print_hostbyte(SCpnt->result));
+ dev, lun, SRpnt->sr_result));
+ SCSI_LOG_SCAN_BUS(3, print_driverbyte(SRpnt->sr_result));
+ SCSI_LOG_SCAN_BUS(3, print_hostbyte(SRpnt->sr_result));
SCSI_LOG_SCAN_BUS(3, printk("\n"));
- if (SCpnt->result) {
- if (((driver_byte(SCpnt->result) & DRIVER_SENSE) ||
- (status_byte(SCpnt->result) & CHECK_CONDITION)) &&
- ((SCpnt->sense_buffer[0] & 0x70) >> 4) == 7) {
- if (((SCpnt->sense_buffer[2] & 0xf) != NOT_READY) &&
- ((SCpnt->sense_buffer[2] & 0xf) != UNIT_ATTENTION) &&
- ((SCpnt->sense_buffer[2] & 0xf) != ILLEGAL_REQUEST || lun > 0)) {
- scsi_release_command(SCpnt);
+ if (SRpnt->sr_result) {
+ if (((driver_byte(SRpnt->sr_result) & DRIVER_SENSE) ||
+ (status_byte(SRpnt->sr_result) & CHECK_CONDITION)) &&
+ ((SRpnt->sr_sense_buffer[0] & 0x70) >> 4) == 7) {
+ if (((SRpnt->sr_sense_buffer[2] & 0xf) != NOT_READY) &&
+ ((SRpnt->sr_sense_buffer[2] & 0xf) != UNIT_ATTENTION) &&
+ ((SRpnt->sr_sense_buffer[2] & 0xf) != ILLEGAL_REQUEST || lun > 0)) {
+ scsi_release_request(SRpnt);
return 1;
}
} else {
- scsi_release_command(SCpnt);
+ scsi_release_request(SRpnt);
return 0;
}
}
scsi_cmd[3] = 0;
scsi_cmd[4] = 255;
scsi_cmd[5] = 0;
- SCpnt->cmd_len = 0;
- SCpnt->sc_data_direction = SCSI_DATA_READ;
+ SRpnt->sr_cmd_len = 0;
+ SRpnt->sr_data_direction = SCSI_DATA_READ;
- scsi_wait_cmd (SCpnt, (void *) scsi_cmd,
+ scsi_wait_req (SRpnt, (void *) scsi_cmd,
(void *) scsi_result,
256, SCSI_TIMEOUT, 3);
SCSI_LOG_SCAN_BUS(3, printk("scsi: INQUIRY %s with code 0x%x\n",
- SCpnt->result ? "failed" : "successful", SCpnt->result));
+ SRpnt->sr_result ? "failed" : "successful", SRpnt->sr_result));
- if (SCpnt->result) {
- scsi_release_command(SCpnt);
+ if (SRpnt->sr_result) {
+ scsi_release_request(SRpnt);
return 0; /* assume no peripheral if any sort of error */
}
* are supported here or not.
*/
if ((scsi_result[0] >> 5) == 3) {
- scsi_release_command(SCpnt);
+ scsi_release_request(SRpnt);
return 0; /* assume no peripheral if any sort of error */
}
scsi_cmd[3] = 0;
scsi_cmd[4] = 0x2a;
scsi_cmd[5] = 0;
- SCpnt->cmd_len = 0;
- SCpnt->sc_data_direction = SCSI_DATA_READ;
- scsi_wait_cmd (SCpnt, (void *) scsi_cmd,
+ SRpnt->sr_cmd_len = 0;
+ SRpnt->sr_data_direction = SCSI_DATA_READ;
+ scsi_wait_req (SRpnt, (void *) scsi_cmd,
(void *) scsi_result, 0x2a,
SCSI_TIMEOUT, 3);
}
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
scsi_release_commandblocks(SDpnt);
SDpnt->host = shpnt;
SDpnt->online = TRUE;
- scsi_build_commandblocks(SDpnt);
-
/*
* Register the queue for the device. All I/O requests will come
* in through here. We also need to register a pointer to
EXPORT_SYMBOL(scsi_partsize);
EXPORT_SYMBOL(scsi_allocate_device);
EXPORT_SYMBOL(scsi_do_cmd);
-EXPORT_SYMBOL(scsi_wait_cmd);
EXPORT_SYMBOL(scsi_command_size);
EXPORT_SYMBOL(scsi_ioctl);
EXPORT_SYMBOL(print_command);
unsigned long spintime_value = 0;
int the_result, retries, spintime;
int sector_size;
- Scsi_Cmnd *SCpnt;
+ Scsi_Request *SRpnt;
/*
* Get the name of the disk, in case we need to log it somewhere.
* just after a scsi bus reset.
*/
- SCpnt = scsi_allocate_device(rscsi_disks[i].device, 1, FALSE);
+ SRpnt = scsi_allocate_request(rscsi_disks[i].device);
buffer = (unsigned char *) scsi_malloc(512);
cmd[0] = TEST_UNIT_READY;
cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
memset((void *) &cmd[2], 0, 8);
- SCpnt->cmd_len = 0;
- SCpnt->sense_buffer[0] = 0;
- SCpnt->sense_buffer[2] = 0;
- SCpnt->sc_data_direction = SCSI_DATA_READ;
+ SRpnt->sr_cmd_len = 0;
+ SRpnt->sr_sense_buffer[0] = 0;
+ SRpnt->sr_sense_buffer[2] = 0;
+ SRpnt->sr_data_direction = SCSI_DATA_READ;
- scsi_wait_cmd (SCpnt, (void *) cmd, (void *) buffer,
+ scsi_wait_req (SRpnt, (void *) cmd, (void *) buffer,
0/*512*/, SD_TIMEOUT, MAX_RETRIES);
- the_result = SCpnt->result;
+ the_result = SRpnt->sr_result;
retries++;
if (the_result == 0
- || SCpnt->sense_buffer[2] != UNIT_ATTENTION)
+ || SRpnt->sr_sense_buffer[2] != UNIT_ATTENTION)
break;
}
*/
if( the_result != 0
&& ((driver_byte(the_result) & DRIVER_SENSE) != 0)
- && SCpnt->sense_buffer[2] == UNIT_ATTENTION
- && SCpnt->sense_buffer[12] == 0x3A ) {
+ && SRpnt->sr_sense_buffer[2] == UNIT_ATTENTION
+ && SRpnt->sr_sense_buffer[12] == 0x3A ) {
rscsi_disks[i].capacity = 0x1fffff;
sector_size = 512;
rscsi_disks[i].device->changed = 1;
/* Look for non-removable devices that return NOT_READY.
* Issue command to spin up drive for these cases. */
if (the_result && !rscsi_disks[i].device->removable &&
- SCpnt->sense_buffer[2] == NOT_READY) {
+ SRpnt->sr_sense_buffer[2] == NOT_READY) {
unsigned long time1;
if (!spintime) {
printk("%s: Spinning up disk...", nbuff);
cmd[1] |= 1; /* Return immediately */
memset((void *) &cmd[2], 0, 8);
cmd[4] = 1; /* Start spin cycle */
- SCpnt->cmd_len = 0;
- SCpnt->sense_buffer[0] = 0;
- SCpnt->sense_buffer[2] = 0;
+ SRpnt->sr_cmd_len = 0;
+ SRpnt->sr_sense_buffer[0] = 0;
+ SRpnt->sr_sense_buffer[2] = 0;
- SCpnt->sc_data_direction = SCSI_DATA_READ;
- scsi_wait_cmd(SCpnt, (void *) cmd, (void *) buffer,
+ SRpnt->sr_data_direction = SCSI_DATA_READ;
+ scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer,
0/*512*/, SD_TIMEOUT, MAX_RETRIES);
}
spintime = 1;
cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
memset((void *) &cmd[2], 0, 8);
memset((void *) buffer, 0, 8);
- SCpnt->cmd_len = 0;
- SCpnt->sense_buffer[0] = 0;
- SCpnt->sense_buffer[2] = 0;
+ SRpnt->sr_cmd_len = 0;
+ SRpnt->sr_sense_buffer[0] = 0;
+ SRpnt->sr_sense_buffer[2] = 0;
- SCpnt->sc_data_direction = SCSI_DATA_READ;
- scsi_wait_cmd(SCpnt, (void *) cmd, (void *) buffer,
+ SRpnt->sr_data_direction = SCSI_DATA_READ;
+ scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer,
8, SD_TIMEOUT, MAX_RETRIES);
- the_result = SCpnt->result;
+ the_result = SRpnt->sr_result;
retries--;
} while (the_result && retries);
);
if (driver_byte(the_result) & DRIVER_SENSE)
printk("%s : extended sense code = %1x \n",
- nbuff, SCpnt->sense_buffer[2] & 0xf);
+ nbuff, SRpnt->sr_sense_buffer[2] & 0xf);
else
printk("%s : sense not available. \n", nbuff);
/* Set dirty bit for removable devices if not ready - sometimes drives
* will not report this properly. */
if (rscsi_disks[i].device->removable &&
- SCpnt->sense_buffer[2] == NOT_READY)
+ SRpnt->sr_sense_buffer[2] == NOT_READY)
rscsi_disks[i].device->changed = 1;
} else {
cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
cmd[2] = 1; /* page code 1 ?? */
cmd[4] = 12;
- SCpnt->cmd_len = 0;
- SCpnt->sense_buffer[0] = 0;
- SCpnt->sense_buffer[2] = 0;
+ SRpnt->sr_cmd_len = 0;
+ SRpnt->sr_sense_buffer[0] = 0;
+ SRpnt->sr_sense_buffer[2] = 0;
/* same code as READCAPA !! */
- SCpnt->sc_data_direction = SCSI_DATA_READ;
- scsi_wait_cmd(SCpnt, (void *) cmd, (void *) buffer,
+ SRpnt->sr_data_direction = SCSI_DATA_READ;
+ scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer,
512, SD_TIMEOUT, MAX_RETRIES);
- the_result = SCpnt->result;
+ the_result = SRpnt->sr_result;
if (the_result) {
printk("%s: test WP failed, assume Write Protected\n", nbuff);
}
} /* check for write protect */
- SCpnt->device->ten = 1;
- SCpnt->device->remap = 1;
- SCpnt->device->sector_size = sector_size;
+ SRpnt->sr_device->ten = 1;
+ SRpnt->sr_device->remap = 1;
+ SRpnt->sr_device->sector_size = sector_size;
/* Wake up a process waiting for device */
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
scsi_free(buffer, 512);
return i;
SRpnt->sr_data_direction = SCSI_DATA_READ;
scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer,
- 512, SR_TIMEOUT, MAX_RETRIES);
+ 8, SR_TIMEOUT, MAX_RETRIES);
the_result = SRpnt->sr_result;
retries--;
/* do the locking and issue the command */
SRpnt->sr_request.rq_dev = cdi->dev;
- /* scsi_wait_cmd sets the command length */
+ /* scsi_wait_req sets the command length */
SRpnt->sr_cmd_len = 0;
SRpnt->sr_data_direction = cgc->data_direction;
static struct sound_unit *chains[16];
+/**
+ * register_sound_special
+ * @fops: File operations for the driver
+ * @unit: Unit number to allocate
+ *
+ * Allocate a special sound device by minor number from the sound
+ * subsystem. The allocated number is returned on succes. On failure
+ * a negative error code is returned.
+ */
+
int register_sound_special(struct file_operations *fops, int unit)
{
char *name;
EXPORT_SYMBOL(register_sound_special);
+/**
+ * register_sound_mixer
+ * @fops: File operations for the driver
+ * @dev: Unit number to allocate
+ *
+ * Allocate a mixer device. Unit is the number of the mixer requested.
+ * Pass -1 to request the next free mixer unit. On success the allocated
+ * number is returned, on failure a negative error code is returned.
+ */
+
int register_sound_mixer(struct file_operations *fops, int dev)
{
return sound_insert_unit(&chains[0], fops, dev, 0, 128,
EXPORT_SYMBOL(register_sound_mixer);
+/**
+ * register_sound_midi
+ * @fops: File operations for the driver
+ * @dev: Unit number to allocate
+ *
+ * Allocate a midi device. Unit is the number of the midi device requested.
+ * Pass -1 to request the next free midi unit. On success the allocated
+ * number is returned, on failure a negative error code is returned.
+ */
+
int register_sound_midi(struct file_operations *fops, int dev)
{
return sound_insert_unit(&chains[2], fops, dev, 2, 130,
* in open - see below.
*/
+/**
+ * register_sound_dsp
+ * @fops: File operations for the driver
+ * @dev: Unit number to allocate
+ *
+ * Allocate a DSP device. Unit is the number of the DSP requested.
+ * Pass -1 to request the next free DSP unit. On success the allocated
+ * number is returned, on failure a negative error code is returned.
+ *
+ * This function allocates both the audio and dsp device entries together
+ * and will always allocate them as a matching pair - eg dsp3/audio3
+ */
+
int register_sound_dsp(struct file_operations *fops, int dev)
{
return sound_insert_unit(&chains[3], fops, dev, 3, 131,
EXPORT_SYMBOL(register_sound_dsp);
+/**
+ * register_sound_synth
+ * @fops: File operations for the driver
+ * @dev: Unit number to allocate
+ *
+ * Allocate a synth device. Unit is the number of the synth device requested.
+ * Pass -1 to request the next free synth unit. On success the allocated
+ * number is returned, on failure a negative error code is returned.
+ */
+
+
int register_sound_synth(struct file_operations *fops, int dev)
{
return sound_insert_unit(&chains[9], fops, dev, 9, 137,
EXPORT_SYMBOL(register_sound_synth);
+/**
+ * unregister_sound_special
+ * @unit: Unit number to allocate
+ *
+ * Release a sound device that was allocated with register_sound_special.
+ * The unit passed is the return value from the register function.
+ */
+
+
void unregister_sound_special(int unit)
{
sound_remove_unit(&chains[unit&15], unit);
EXPORT_SYMBOL(unregister_sound_special);
+/**
+ * unregister_sound_mixer
+ * @unit: Unit number to allocate
+ *
+ * Release a sound device that was allocated with register_sound_mixer.
+ * The unit passed is the return value from the register function.
+ */
+
void unregister_sound_mixer(int unit)
{
sound_remove_unit(&chains[0], unit);
EXPORT_SYMBOL(unregister_sound_mixer);
+/**
+ * unregister_sound_midi
+ * @unit: Unit number to allocate
+ *
+ * Release a sound device that was allocated with register_sound_midi.
+ * The unit passed is the return value from the register function.
+ */
+
void unregister_sound_midi(int unit)
{
return sound_remove_unit(&chains[2], unit);
EXPORT_SYMBOL(unregister_sound_midi);
+/**
+ * unregister_sound_dsp
+ * @unit: Unit number to allocate
+ *
+ * Release a sound device that was allocated with register_sound_dsp.
+ * The unit passed is the return value from the register function.
+ *
+ * Both of the allocated units are released together automatically.
+ */
+
void unregister_sound_dsp(int unit)
{
return sound_remove_unit(&chains[3], unit);
}
+
EXPORT_SYMBOL(unregister_sound_dsp);
+/**
+ * unregister_sound_synth
+ * @unit: Unit number to allocate
+ *
+ * Release a sound device that was allocated with register_sound_synth.
+ * The unit passed is the return value from the register function.
+ */
+
void unregister_sound_synth(int unit)
{
return sound_remove_unit(&chains[9], unit);
return (int) l;
}
+/**
+ * mod_firmware_load - load sound driver firmware
+ * @fn: filename
+ * @fp: return for the buffer.
+ *
+ * Load the firmware for a sound module (up to 128K) into a buffer.
+ * The buffer is returned in *fp. It is allocated with vmalloc so is
+ * virtually linear and not DMAable. The caller should free it with
+ * vfree when finished.
+ *
+ * The length of the buffer is returned on a successful load, the
+ * value zero on a failure.
+ *
+ * Caution: This API is not recommended. Firmware should be loaded via
+ * an ioctl call and a setup application. This function may disappear
+ * in future.
+ */
+
int mod_firmware_load(const char *fn, char **fp)
{
int r;
printk(",%d", hw_config->dma2);
}
printk("\n");
-}
#endif
+}
void conf_printf2(char *name, int base, int irq, int dma, int dma2)
{
printk(",%d", dma2);
}
printk("\n");
-}
#endif
+}
/*
* Module and lock management
comment 'USB Controllers'
dep_tristate ' UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB
- if [ "$CONFIG_USB_UHCI" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool ' USB-UHCI High Bandwidth (EXPERIMENTAL)' CONFIG_USB_UHCI_HIGH_BANDWIDTH
- fi
if [ "$CONFIG_USB_UHCI" != "y" ]; then
dep_tristate ' UHCI Alternate Driver (JE) support' CONFIG_USB_UHCI_ALT $CONFIG_USB
if [ "$CONFIG_USB_UHCI_ALT" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
device, and I couldn't figure out their meaning. My suspicion
is that they don't have any:-)
+ You might find some interesting stuff about this module at
+ http://unimut.fsk.uni-heidelberg.de/unimut/demi/dsbr
+
Copyright (c) 2000 Markus Demleitner
This program is free software; you can redistribute it and/or modify
History:
- Version 0.3:
- Brad Hards <bradh@dynamite.com.au>: Trivial fix for usb.h, more GPL
-
+ Version 0.21:
+ Markus Demleitner <msdemlei@tucana.harvard.edu>:
+ Minor cleanup, warnings if something goes wrong, lame attempt
+ to adhere to Documentation/CodingStyle
+
Version 0.2:
Brad Hards <bradh@dynamite.com.au>: Fixes to make it work as non-module
- Markus Demleitner <msdemlei@tucana.harvard.edu>: Copyright clarification
+ Markus: Copyright clarification
- Version 0.01: Markus Demleitner <msdemlei@tucana.harvard.edu>
- initial release
+ Version 0.01: Markus: initial release
*/
#include <linux/kernel.h>
+
+#if CONFIG_MODVERSIONS==1
+#define MODVERSIONS
+#include <linux/modversions.h>
+#endif
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/malloc.h>
minor: 0
};
+
static int dsbr100_start(usb_dsbr100 *radio)
{
- usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
- 0x00, 0xC0, 0x00, 0xC7, radio->transfer_buffer, 8, 300);
- usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
- 0x02, 0xC0, 0x01, 0x00, radio->transfer_buffer, 8, 300);
+ if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+ 0x00, 0xC0, 0x00, 0xC7, radio->transfer_buffer, 8, 300)<0 ||
+ usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+ 0x02, 0xC0, 0x01, 0x00, radio->transfer_buffer, 8, 300)<0)
+ return -1;
return (radio->transfer_buffer)[0];
}
static int dsbr100_stop(usb_dsbr100 *radio)
{
- usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
- 0x00, 0xC0, 0x16, 0x1C, radio->transfer_buffer, 8, 300);
- usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
- 0x02, 0xC0, 0x00, 0x00, radio->transfer_buffer, 8, 300);
+ if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+ 0x00, 0xC0, 0x16, 0x1C, radio->transfer_buffer, 8, 300)<0 ||
+ usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+ 0x02, 0xC0, 0x00, 0x00, radio->transfer_buffer, 8, 300)<0)
+ return -1;
return (radio->transfer_buffer)[0];
}
static int dsbr100_setfreq(usb_dsbr100 *radio, int freq)
{
freq = (freq*80)/16+856;
- usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
- 0x01, 0xC0, (freq&0xff00)>>8, freq&0xff, radio->transfer_buffer, 8, 300);
- usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
- 0x00, 0xC0, 0x96, 0xB7, radio->transfer_buffer, 8, 300);
- usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
- 0x00, 0xC0, 0x00, 0x24, radio->transfer_buffer, 8, 300);
+ if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+ 0x01, 0xC0, (freq&0xff00)>>8, freq&0xff,
+ radio->transfer_buffer, 8, 300)<0
+ || usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+ 0x00, 0xC0, 0x96, 0xB7, radio->transfer_buffer, 8, 300)<0 ||
+ usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+ 0x00, 0xC0, 0x00, 0x24, radio->transfer_buffer, 8, 300)<0) {
+ radio->stereo = -1;
+ return -1;
+ }
radio->stereo = ! ((radio->transfer_buffer)[0]&0x01);
return (radio->transfer_buffer)[0];
}
static void dsbr100_getstat(usb_dsbr100 *radio)
{
- usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
- 0x00, 0xC0, 0x00 , 0x24, radio->transfer_buffer, 8, 300);
- radio->stereo = ! (radio->transfer_buffer[0]&0x01);
+ if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+ 0x00, 0xC0, 0x00 , 0x24, radio->transfer_buffer, 8, 300)<0)
+ radio->stereo = -1;
+ else
+ radio->stereo = ! (radio->transfer_buffer[0]&0x01);
}
switch(cmd)
{
- case VIDIOCGCAP:
- {
+ case VIDIOCGCAP: {
struct video_capability v;
v.type=VID_TYPE_TUNER;
v.channels=1;
return -EFAULT;
return 0;
}
- case VIDIOCGTUNER:
- {
+ case VIDIOCGTUNER: {
struct video_tuner v;
dsbr100_getstat(radio);
if(copy_from_user(&v, arg,sizeof(v))!=0)
return -EFAULT;
return 0;
}
- case VIDIOCSTUNER:
- {
+ case VIDIOCSTUNER: {
struct video_tuner v;
if(copy_from_user(&v, arg, sizeof(v)))
return -EFAULT;
/* Only 1 tuner so no setting needed ! */
return 0;
}
- case VIDIOCGFREQ:
- if(copy_to_user(arg, &(radio->curfreq), sizeof(radio->curfreq)))
+ case VIDIOCGFREQ:
+ if (radio->curfreq==-1)
+ return -EINVAL;
+ if(copy_to_user(arg, &(radio->curfreq),
+ sizeof(radio->curfreq)))
return -EFAULT;
return 0;
+
case VIDIOCSFREQ:
- if(copy_from_user(&(radio->curfreq), arg,sizeof(radio->curfreq)))
+ if(copy_from_user(&(radio->curfreq), arg,
+ sizeof(radio->curfreq)))
return -EFAULT;
- dsbr100_setfreq(radio, radio->curfreq);
+ if (dsbr100_setfreq(radio, radio->curfreq)==-1)
+ warn("set frequency failed");
return 0;
- case VIDIOCGAUDIO:
- {
+
+ case VIDIOCGAUDIO: {
struct video_audio v;
memset(&v,0, sizeof(v));
v.flags|=VIDEO_AUDIO_MUTABLE;
return -EFAULT;
return 0;
}
- case VIDIOCSAUDIO:
- {
+ case VIDIOCSAUDIO: {
struct video_audio v;
if(copy_from_user(&v, arg, sizeof(v)))
return -EFAULT;
if(v.audio)
return -EINVAL;
- if(v.flags&VIDEO_AUDIO_MUTE)
- dsbr100_stop(radio);
+ if(v.flags&VIDEO_AUDIO_MUTE) {
+ if (dsbr100_stop(radio)==-1)
+ warn("radio did not respond properly");
+ }
else
- dsbr100_start(radio);
+ if (dsbr100_start(radio)==-1)
+ warn("radio did not respond properly");
return 0;
}
default:
{
usb_dsbr100 *radio=dev->priv;
- if (! radio)
- return -EINVAL;
+ if (! radio) {
+ warn("radio not initialised");
+ return -EAGAIN;
+ }
if(users)
+ {
+ warn("radio in use");
return -EBUSY;
+ }
users++;
MOD_INC_USE_COUNT;
- dsbr100_start(radio);
+ if (dsbr100_start(radio)<0)
+ warn("radio did not start up properly");
dsbr100_setfreq(radio,radio->curfreq);
return 0;
}
usb_dsbr100_radio.priv = NULL;
usb_register(&usb_dsbr100_driver);
if (video_register_device(&usb_dsbr100_radio,VFL_TYPE_RADIO)==-1) {
+ warn("couldn't register video device");
return -EINVAL;
}
return 0;
int __init init_module(void)
{
- return dsbr100_init();
+ return dsbr100_init();
}
void cleanup_module(void)
{
+ usb_dsbr100 *radio=usb_dsbr100_radio.priv;
+
+ if (radio)
+ dsbr100_stop(radio);
video_unregister_device(&usb_dsbr100_radio);
usb_deregister(&usb_dsbr100_driver);
}
+
+/*
+vi: ts=8
+Sigh. Of course, I am one of the ts=2 heretics, but Linus' wish is
+my command.
+*/
/*
-**
** Pegasus: USB 10/100Mbps/HomePNA (1Mbps) Controller
**
-** Copyleft (L) 1999 Petko Manolov - Petkan (petkan@spct.net)
-**
+** Copyright (R) 1999,2000 Petko Manolov - Petkan (petkan@spct.net)
+**
** Distribute under GPL version 2 or later.
*/
-
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/malloc.h>
#include <linux/init.h>
#include <linux/delay.h>
-#include <linux/usb.h>
-
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
+#include <linux/usb.h>
-#if LINUX_VERSION_CODE<0x2032d || !defined(__KERNEL__) || !defined(__OPTIMIZE__)
-#error You can not compile this driver on this kernel with this C options!
-#endif
-
-
-#define ADMTEK_VENDOR_ID 0x07a6
-#define ADMTEK_HPNA_PEGASUS 0x0986
-
-#define HPNA_MTU 1500
-#define MAX_MTU 1536
-#define TX_TIMEOUT (HZ*5)
-#define SOMETHING (jiffies + TX_TIMEOUT)
+static const char *version = __FILE__ ": v0.3.3 2000/03/13 Written by Petko Manolov (petkan@spct.net)\n";
-static const char version[] = "pegasus.c: v0.2.27 2000/02/29 Written by Petko Manolov (petkan@spct.net)\n";
+#define ADMTEK_VENDOR_ID 0x07a6
+#define ADMTEK_DEVICE_ID_PEGASUS 0x0986
+#define PEGASUS_MTU 1500
+#define PEGASUS_MAX_MTU 1536
+#define PEGASUS_TX_TIMEOUT (HZ*5)
+#define ALIGN(x) x __attribute__((aligned(16)))
-typedef struct usb_hpna
-{
- struct usb_device *usb_dev;
- struct net_device *net_dev;
- int present;
- int active;
- void *irq_handler;
- struct list_head list;
+struct pegasus {
+ struct usb_device *usb;
+ struct net_device *net;
struct net_device_stats stats;
- spinlock_t hpna_lock;
- struct timer_list timer;
-
- unsigned int rx_pipe;
- unsigned char * rx_buff;
- urb_t rx_urb;
-
- unsigned int tx_pipe;
- unsigned char * tx_buff;
- urb_t tx_urb;
- struct sk_buff * tx_skbuff;
-
- __u8 intr_ival;
- unsigned int intr_pipe;
- unsigned char intr_buff[8];
- urb_t intr_urb;
-} usb_hpna_t;
-
-
-usb_hpna_t usb_dev_hpna;
-static int loopback = 0;
-int multicast_filter_limit = 32;
-static LIST_HEAD(hpna_list);
+ spinlock_t pegasus_lock;
+ struct urb rx_urb, tx_urb, intr_urb;
+ unsigned char ALIGN(rx_buff[PEGASUS_MAX_MTU]);
+ unsigned char ALIGN(tx_buff[PEGASUS_MAX_MTU]);
+ unsigned char ALIGN(intr_buff[8]);
+};
+static int loopback = 0;
+static int multicast_filter_limit = 32;
MODULE_AUTHOR("Petko Manolov <petkan@spct.net>");
-MODULE_DESCRIPTION("ADMtek \"Pegasus\" USB Ethernet driver");
+MODULE_DESCRIPTION("ADMtek AN986 Pegasus USB Ethernet driver");
MODULE_PARM(loopback, "i");
-
-/*** vendor specific commands ***/
-static __inline__ int hpna_get_registers( struct usb_device *dev, __u16 indx, __u16 size, void *data )
-{
- return usb_control_msg(dev, usb_rcvctrlpipe(dev,0), 0xf0, 0xc0, 0,
- indx, data, size, HZ);
-}
-
-
-static __inline__ int hpna_set_register( struct usb_device *dev, __u16 indx, __u8 value )
-{
- __u8 data = value;
- return usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40,
- data, indx, &data, 1, HZ);
-}
+#define pegasus_get_registers(dev, indx, size, data)\
+ usb_control_msg(dev, usb_rcvctrlpipe(dev,0), 0xf0, 0xc0, 0, indx, data, size, HZ);
+#define pegasus_set_registers(dev, indx, size, data)\
+ usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, 0, indx, data, size, HZ);
+#define pegasus_set_register(dev, indx, value) \
+ { __u8 data = value; \
+ usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, data, indx, &data, 1, HZ);}
-static __inline__ int hpna_set_registers( struct usb_device *dev, __u16 indx, __u16 size, void *data )
+static int pegasus_read_phy_word(struct usb_device *dev, __u8 index, __u16 *regdata)
{
- return usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, 0,
- indx, data, size, HZ);
-}
-
-
-static int read_phy_word( struct usb_device *dev, __u8 index, __u16 *regdata )
-{
- int i;
- __u8 data[4];
-
- data[0] = 1;
- data[1] = 0;
- data[2] = 0;
- data[3] = 0x40 + index;
- hpna_set_registers( dev, 0x25, 4, data );
- for ( i=0; i<100; i++ ) {
- hpna_get_registers( dev, 0x25, 4, data );
- if ( data[3] & 0x80 ) {
- *regdata = *(__u16 *)(data+1);
- return 0;
+ int i;
+ __u8 data[4] = { 1, 0, 0, 0x40 + index };
+
+ pegasus_set_registers(dev, 0x25, 4, data);
+ for (i = 0; i < 100; i++) {
+ pegasus_get_registers(dev, 0x26, 3, data);
+ if (data[2] & 0x80) {
+ *regdata = *(__u16 *)(data);
+ return 0;
}
udelay(100);
}
+
warn("read_phy_word() failed");
- return 1;
+ return 1;
}
-
-static int write_phy_word( struct usb_device *dev, __u8 index, __u16 regdata )
+static int pegasus_write_phy_word(struct usb_device *dev, __u8 index, __u16 regdata)
{
- int i;
- __u8 data[4];
-
- data[0] = 1;
- data[1] = regdata;
- data[2] = regdata >> 8;
- data[3] = 0x20 + index;
- hpna_set_registers( dev, 0x25, 4, data );
- for ( i=0; i<100; i++ ) {
- hpna_get_registers( dev, 0x28, 1, data );
- if ( data[0] & 0x80 ) {
- return 0;
- }
+ int i;
+ __u8 data[4] = { 1, regdata, regdata >> 8, 0x20 + index };
+
+ pegasus_set_registers(dev, 0x25, 4, data);
+ for (i = 0; i < 100; i++) {
+ pegasus_get_registers(dev, 0x28, 1, data);
+ if (data[0] & 0x80)
+ return 0;
udelay(100);
}
+
warn("write_phy_word() failed");
- return 1;
+ return 1;
}
-
-int read_srom_word( struct usb_device *dev, __u8 index, __u16 *retdata)
+static int pegasus_read_srom_word(struct usb_device *dev, __u8 index, __u16 *retdata)
{
- int i;
- __u8 data[4];
-
- data[0] = index;
- data[1] = data[2] = 0;
- data[3] = 0x02;
- hpna_set_registers(dev, 0x20, 4, data);
- for ( i=0; i<100; i++ ) {
- hpna_get_registers(dev, 0x23, 1, data);
- if ( data[0] & 4 ) {
- hpna_get_registers(dev, 0x21, 2, data);
+ int i;
+ __u8 data[4] = { index, 0, 0, 0x02 };
+
+ pegasus_set_registers(dev, 0x20, 4, data);
+ for (i = 0; i < 100; i++) {
+ pegasus_get_registers(dev, 0x23, 1, data);
+ if (data[0] & 4) {
+ pegasus_get_registers(dev, 0x21, 2, data);
*retdata = *(__u16 *)data;
- return 0;
+ return 0;
}
}
+
warn("read_srom_word() failed");
- return 1;
+ return 1;
}
-/*** end ***/
-
-
-
-int get_node_id( struct usb_device *dev, __u8 *id )
+static int pegasus_get_node_id(struct usb_device *dev, __u8 *id)
{
- int i;
-
- for ( i=0; i<3; i++ ) {
- if ( read_srom_word(dev, i, (__u16 *)&id[i*2] ) )
- return 1;
- }
- return 0;
+ int i;
+ for (i = 0; i < 3; i++)
+ if (pegasus_read_srom_word(dev, i, (__u16 *)&id[i * 2]))
+ return 1;
+ return 0;
}
-
-static int reset_mac( struct usb_device *dev )
+static int pegasus_reset_mac(struct usb_device *dev)
{
- __u8 data = 0x8;
- int i;
-
- hpna_set_register( dev, 1, 0x08 );
- for ( i=0; i<100; i++ ) {
- hpna_get_registers( dev, 1, 1, &data);
- if ( !(data & 0x08) ) {
- if ( loopback & 1 )
- return 0;
- else if ( loopback & 2 ) {
- write_phy_word( dev, 0, 0x4000 );
- /*return 0;*/
- }
- hpna_set_register( dev, 0x7e, 0x24 );
- hpna_set_register( dev, 0x7e, 0x27 );
- return 0;
+ __u8 data = 0x8;
+ int i;
+
+ pegasus_set_register(dev, 1, data);
+ for (i = 0; i < 100; i++) {
+ pegasus_get_registers(dev, 1, 1, &data);
+ if (~data & 0x08) {
+ if (loopback & 1)
+ return 0;
+ if (loopback & 2)
+ pegasus_write_phy_word(dev, 0, 0x4000);
+ pegasus_set_register(dev, 0x7e, 0x24);
+ pegasus_set_register(dev, 0x7e, 0x27);
+ return 0;
}
}
+
return 1;
}
-
-int start_net( struct net_device *dev, struct usb_device *usb_dev )
+static int pegasus_start_net(struct net_device *dev, struct usb_device *usb)
{
- __u16 partmedia, temp;
- __u8 node_id[6];
- __u8 data[4];
-
- if ( get_node_id(usb_dev, node_id) )
- return 1;
- hpna_set_registers(usb_dev, 0x10, 6, node_id);
+ __u16 partmedia, temp;
+ __u8 node_id[6];
+ __u8 data[4];
+
+ if (pegasus_get_node_id(usb, node_id))
+ return 1;
+
+ pegasus_set_registers(usb, 0x10, 6, node_id);
memcpy(dev->dev_addr, node_id, 6);
- if ( read_phy_word(usb_dev, 1, &temp) )
- return 2;
- if ( !(temp & 4) ) {
- if ( loopback )
- goto ok;
+ if (pegasus_read_phy_word(usb, 1, &temp))
+ return 2;
+
+ if ((~temp & 4) && !loopback) {
err("link NOT established - %x", temp);
- return 3;
- }
-ok:
- if ( read_phy_word(usb_dev, 5, &partmedia) )
- return 4;
- temp = partmedia;
- partmedia &= 0x1f;
- if ( partmedia != 1 ) {
- err("party FAIL %x", temp);
- return 5;
+ return 3;
}
- partmedia = temp;
- if ( partmedia & 0x100 )
- data[1] = 0x30;
- else {
- if ( partmedia & 0x80 )
- data[1] = 0x10;
- else
- data[1] = 0;
+
+ if (pegasus_read_phy_word(usb, 5, &partmedia))
+ return 4;
+
+ if ((partmedia & 0x1f) != 1) {
+ err("party FAIL %x", partmedia);
+ return 5;
}
-
- data[0] = 0xc9;
- data[2] = (loopback & 1) ? 0x08 : 0x00;
-
- hpna_set_registers(usb_dev, 0, 3, data);
-
- return 0;
-}
+ data[0] = 0xc9;
+ data[1] = (partmedia & 0x100) ? 0x30 : ((partmedia & 0x80) ? 0x10 : 0);
+ data[2] = (loopback & 1) ? 0x08 : 0x00;
-static void hpna_read_irq( purb_t urb )
-{
- struct net_device *net_dev = urb->context;
- usb_hpna_t *hpna = net_dev->priv;
- int count = urb->actual_length, res;
- int rx_status = *(int *)(hpna->rx_buff + count - 4);
+ pegasus_set_registers(usb, 0, 3, data);
+ return 0;
+}
- if ( urb->status ) {
- info( "%s: RX status %d\n", net_dev->name, urb->status );
+static void pegasus_read_bulk(struct urb *urb)
+{
+ struct pegasus *pegasus = urb->context;
+ struct net_device *net = pegasus->net;
+ int count = urb->actual_length, res;
+ int rx_status = *(int *)(pegasus->rx_buff + count - 4);
+ struct sk_buff *skb;
+ __u16 pkt_len;
+
+ if (urb->status) {
+ info("%s: RX status %d", net->name, urb->status);
goto goon;
}
- if ( !count )
+ if (!count)
goto goon;
-/* if ( rx_status & 0x00010000 )
+#if 0
+ if (rx_status & 0x00010000)
+ goto goon;
+#endif
+ if (rx_status & 0x000e0000) {
+
+ dbg("%s: error receiving packet %x", net->name, rx_status & 0xe0000);
+ pegasus->stats.rx_errors++;
+ if(rx_status & 0x060000) pegasus->stats.rx_length_errors++;
+ if(rx_status & 0x080000) pegasus->stats.rx_crc_errors++;
+ if(rx_status & 0x100000) pegasus->stats.rx_frame_errors++;
+
goto goon;
-*/
- if ( rx_status & 0x000e0000 ) {
- dbg("%s: error receiving packet %x",
- net_dev->name, rx_status & 0xe0000);
- hpna->stats.rx_errors++;
- if(rx_status & 0x060000) hpna->stats.rx_length_errors++;
- if(rx_status & 0x080000) hpna->stats.rx_crc_errors++;
- if(rx_status & 0x100000) hpna->stats.rx_frame_errors++;
- } else {
- struct sk_buff *skb;
- __u16 pkt_len = (rx_status & 0xfff) - 8;
-
-
- if((skb = dev_alloc_skb(pkt_len+2)) != NULL ) {
- skb->dev = net_dev;
- skb_reserve(skb, 2);
- eth_copy_and_sum(skb, hpna->rx_buff, pkt_len, 0);
- skb_put(skb, pkt_len);
- } else
- goto goon;
- skb->protocol = eth_type_trans(skb, net_dev);
- netif_rx(skb);
- hpna->stats.rx_packets++;
- hpna->stats.rx_bytes += pkt_len;
}
+
+ pkt_len = (rx_status & 0xfff) - 8;
+
+ if(!(skb = dev_alloc_skb(pkt_len+2)))
+ goto goon;
+
+ skb->dev = net;
+ skb_reserve(skb, 2);
+ eth_copy_and_sum(skb, pegasus->rx_buff, pkt_len, 0);
+ skb_put(skb, pkt_len);
+
+ skb->protocol = eth_type_trans(skb, net);
+ netif_rx(skb);
+ pegasus->stats.rx_packets++;
+ pegasus->stats.rx_bytes += pkt_len;
+
goon:
- if ( (res = usb_submit_urb( &hpna->rx_urb )) )
- warn("failed rx_urb %d", res);
+ if ((res = usb_submit_urb(&pegasus->rx_urb)))
+ warn("(prb)failed rx_urb %d", res);
}
-
-static void hpna_irq( urb_t *urb)
+static void pegasus_irq(urb_t *urb)
{
- if( urb->status ) {
+ if(urb->status) {
__u8 *d = urb->transfer_buffer;
printk("txst0 %x, txst1 %x, rxst %x, rxlst0 %x, rxlst1 %x, wakest %x",
- d[0], d[1], d[2], d[3], d[4], d[5] );
+ d[0], d[1], d[2], d[3], d[4], d[5]);
}
}
-
-static void hpna_write_irq( purb_t urb )
+static void pegasus_write_bulk(struct urb *urb)
{
- struct net_device *net_dev = urb->context;
- usb_hpna_t *hpna = net_dev->priv;
+ struct pegasus *pegasus = urb->context;
+ spin_lock(&pegasus->pegasus_lock);
- spin_lock( &hpna->hpna_lock );
-
- if ( urb->status )
- info("%s: TX status %d\n", net_dev->name, urb->status);
- netif_wake_queue( net_dev );
+ if (urb->status)
+ info("%s: TX status %d", pegasus->net->name, urb->status);
+ netif_wake_queue(pegasus->net);
- spin_unlock( &hpna->hpna_lock );
+ spin_unlock(&pegasus->pegasus_lock);
}
-
-static void tx_timeout( struct net_device *dev )
+static void pegasus_tx_timeout(struct net_device *net)
{
- usb_hpna_t *hpna = dev->priv;
+ struct pegasus *pegasus = net->priv;
- warn( "%s: Tx timed out. Reseting...", dev->name );
- hpna->stats.tx_errors++;
- dev->trans_start = jiffies;
- netif_wake_queue( dev );
-}
+ warn("%s: Tx timed out. Reseting...", net->name);
+ pegasus->stats.tx_errors++;
+ net->trans_start = jiffies;
+ netif_wake_queue(net);
+}
-static int hpna_start_xmit( struct sk_buff *skb, struct net_device *net_dev )
+static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net)
{
- usb_hpna_t *hpna = (usb_hpna_t *)net_dev->priv;
- int count = skb->len+2 % 64 ? skb->len+2 : skb->len+3;
- int res;
+ struct pegasus *pegasus = net->priv;
+ int count = ((skb->len+2) & 0x3f) ? skb->len+2 : skb->len+3;
+ int res;
- spin_lock( &hpna->hpna_lock );
+ spin_lock(&pegasus->pegasus_lock);
- netif_stop_queue( net_dev );
- ((__u16 *)hpna->tx_buff)[0] = skb->len;
- memcpy(hpna->tx_buff+2, skb->data, skb->len);
- (&hpna->tx_urb)->transfer_buffer_length = count;
- if ( (res = usb_submit_urb( &hpna->tx_urb )) ) {
+ netif_stop_queue(net);
+
+ ((__u16 *)pegasus->tx_buff)[0] = skb->len;
+ memcpy(pegasus->tx_buff+2, skb->data, skb->len);
+ (&pegasus->tx_urb)->transfer_buffer_length = count;
+
+ if ((res = usb_submit_urb(&pegasus->tx_urb))) {
warn("failed tx_urb %d", res);
- hpna->stats.tx_errors++;
- netif_start_queue( net_dev );
+ pegasus->stats.tx_errors++;
+ netif_start_queue(net);
} else {
- hpna->stats.tx_packets++;
- hpna->stats.tx_bytes += skb->len;
- net_dev->trans_start = jiffies;
+ pegasus->stats.tx_packets++;
+ pegasus->stats.tx_bytes += skb->len;
+ net->trans_start = jiffies;
}
- dev_kfree_skb( skb );
- spin_unlock( &hpna->hpna_lock );
- return 0;
-}
+ dev_kfree_skb(skb);
-static struct net_device_stats *hpna_netdev_stats( struct net_device *dev )
-{
- return &((usb_hpna_t *)dev->priv)->stats;
+ spin_unlock(&pegasus->pegasus_lock);
+
+ return 0;
}
-static int hpna_open( struct net_device *net_dev )
+static struct net_device_stats *pegasus_netdev_stats(struct net_device *dev)
{
- usb_hpna_t *hpna = (usb_hpna_t *)net_dev->priv;
- int res;
+ return &((struct pegasus *)dev->priv)->stats;
+}
- if ( hpna->active )
- return -EBUSY;
- else
- hpna->active = 1;
+static int pegasus_open(struct net_device *net)
+{
+ struct pegasus *pegasus = (struct pegasus *)net->priv;
+ int res;
- if ( start_net(net_dev, hpna->usb_dev) ) {
- err("can't start_net()");
- return -EIO;
+ if ((res = pegasus_start_net(net, pegasus->usb))) {
+ err("can't start_net() - %d", res);
+ return -EIO;
}
- if ( (res = usb_submit_urb( &hpna->rx_urb )) )
- warn("failed rx_urb %d", res);
+ if ((res = usb_submit_urb(&pegasus->rx_urb)))
+ warn("(open)failed rx_urb %d", res);
-/* usb_submit_urb( &hpna->intr_urb );*/
- netif_start_queue( net_dev );
+/* usb_submit_urb(&pegasus->intr_urb);*/
+ netif_start_queue(net);
MOD_INC_USE_COUNT;
return 0;
}
-
-static int hpna_close( struct net_device *net_dev )
+static int pegasus_close(struct net_device *net)
{
- usb_hpna_t *hpna = net_dev->priv;
-
-
- netif_stop_queue( net_dev );
+ struct pegasus *pegasus = net->priv;
- usb_unlink_urb( &hpna->rx_urb );
- usb_unlink_urb( &hpna->tx_urb );
-/* usb_unlink_urb( hpna->intr_urb );*/
+ netif_stop_queue(net);
- hpna->active = 0;
+ usb_unlink_urb(&pegasus->rx_urb);
+ usb_unlink_urb(&pegasus->tx_urb);
+/* usb_unlink_urb(&pegasus->intr_urb); */
MOD_DEC_USE_COUNT;
return 0;
}
-
-static int hpna_ioctl( struct net_device *dev, struct ifreq *rq, int cmd )
+static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
{
- __u16 *data = (__u16 *)&rq->ifr_data;
- usb_hpna_t *hpna = dev->priv;
+ __u16 *data = (__u16 *)&rq->ifr_data;
+ struct pegasus *pegasus = net->priv;
- switch( cmd ) {
- case SIOCDEVPRIVATE:
+ switch(cmd) {
+ case SIOCDEVPRIVATE:
data[0] = 1;
case SIOCDEVPRIVATE+1:
- read_phy_word(hpna->usb_dev, data[1] & 0x1f, &data[3]);
- return 0;
+ pegasus_read_phy_word(pegasus->usb, data[1] & 0x1f, &data[3]);
+ return 0;
case SIOCDEVPRIVATE+2:
- if ( !capable(CAP_NET_ADMIN) )
- return -EPERM;
- write_phy_word(hpna->usb_dev, data[1] & 0x1f, data[2]);
- return 0;
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ pegasus_write_phy_word(pegasus->usb, data[1] & 0x1f, data[2]);
+ return 0;
default:
- return -EOPNOTSUPP;
+ return -EOPNOTSUPP;
}
}
-
-static void set_rx_mode( struct net_device *net_dev )
+static void pegasus_set_rx_mode(struct net_device *net)
{
- usb_hpna_t *hpna=net_dev->priv;
+ struct pegasus *pegasus = net->priv;
- netif_stop_queue( net_dev );
-
- if ( net_dev->flags & IFF_PROMISC ) {
- info("%s: Promiscuous mode enabled", net_dev->name);
- hpna_set_register( hpna->usb_dev, 2, 0x04 );
- } else if ((net_dev->mc_count > multicast_filter_limit) ||
- (net_dev->flags & IFF_ALLMULTI)) {
- hpna_set_register(hpna->usb_dev, 0, 0xfa);
- hpna_set_register(hpna->usb_dev, 2, 0);
+ netif_stop_queue(net);
+
+ if (net->flags & IFF_PROMISC) {
+ info("%s: Promiscuous mode enabled", net->name);
+ pegasus_set_register(pegasus->usb, 2, 0x04);
+ } else if ((net->mc_count > multicast_filter_limit) ||
+ (net->flags & IFF_ALLMULTI)) {
+ pegasus_set_register(pegasus->usb, 0, 0xfa);
+ pegasus_set_register(pegasus->usb, 2, 0);
} else {
- dbg("%s: set Rx mode", net_dev->name);
+ dbg("%s: set Rx mode", net->name);
}
- netif_wake_queue( net_dev );
+ netif_wake_queue(net);
}
-
-static void * usb_hpna_probe( struct usb_device *dev, unsigned int ifnum )
+static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum)
{
- struct net_device *net_dev;
- usb_hpna_t *hpna = &usb_dev_hpna;
-
+ struct net_device *net;
+ struct pegasus *pegasus;
-
- if ( dev->descriptor.idVendor != ADMTEK_VENDOR_ID ||
- dev->descriptor.idProduct != ADMTEK_HPNA_PEGASUS ) {
- return NULL;
+ if (dev->descriptor.idVendor != ADMTEK_VENDOR_ID ||
+ dev->descriptor.idProduct != ADMTEK_DEVICE_ID_PEGASUS) {
+ return NULL;
}
- printk("USB HPNA Pegasus found\n");
-
- if ( usb_set_configuration(dev, dev->config[0].bConfigurationValue)) {
+ if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) {
err("usb_set_configuration() failed");
return NULL;
}
- hpna->usb_dev = dev;
-
- hpna->rx_pipe = usb_rcvbulkpipe(hpna->usb_dev, 1);
- hpna->tx_pipe = usb_sndbulkpipe(hpna->usb_dev, 2);
- hpna->intr_pipe = usb_rcvintpipe(hpna->usb_dev, 0);
-
- if ( reset_mac(dev) ) {
- err("can't reset MAC");
+ if(!(pegasus = kmalloc(sizeof(struct pegasus), GFP_KERNEL))) {
+ err("out of memory allocating device structure");
+ return NULL;
}
+ memset(pegasus, 0, sizeof(struct pegasus));
- hpna->present = 1;
-
- if(!(hpna->rx_buff=kmalloc(MAX_MTU, GFP_KERNEL))) {
- err("not enough mem for out buff");
- return NULL;
- }
- if(!(hpna->tx_buff=kmalloc(MAX_MTU, GFP_KERNEL))) {
- kfree_s(hpna->rx_buff, MAX_MTU);
- err("not enough mem for out buff");
- return NULL;
+ if (pegasus_reset_mac(dev)) {
+ err("can't reset MAC");
+ kfree(pegasus);
+ return NULL;
}
-
- net_dev = init_etherdev( 0, 0 );
- hpna->net_dev = net_dev;
- net_dev->priv = hpna;
- net_dev->open = hpna_open;
- net_dev->stop = hpna_close;
- net_dev->watchdog_timeo = TX_TIMEOUT;
- net_dev->tx_timeout = tx_timeout;
- net_dev->do_ioctl = hpna_ioctl;
- net_dev->hard_start_xmit = hpna_start_xmit;
- net_dev->set_multicast_list = set_rx_mode;
- net_dev->get_stats = hpna_netdev_stats;
- net_dev->mtu = HPNA_MTU;
- hpna->hpna_lock = SPIN_LOCK_UNLOCKED;
-
- FILL_BULK_URB( &hpna->rx_urb, hpna->usb_dev, hpna->rx_pipe,
- hpna->rx_buff, MAX_MTU, hpna_read_irq, net_dev );
- FILL_BULK_URB( &hpna->tx_urb, hpna->usb_dev, hpna->tx_pipe,
- hpna->tx_buff, MAX_MTU, hpna_write_irq, net_dev );
- FILL_INT_URB( &hpna->intr_urb, hpna->usb_dev, hpna->intr_pipe,
- hpna->intr_buff, 8, hpna_irq, net_dev, 250 );
-/* list_add( &hpna->list, &hpna_list );*/
-
- return net_dev;
+ net = init_etherdev(0, 0);
+ net->priv = pegasus;
+ net->open = pegasus_open;
+ net->stop = pegasus_close;
+ net->watchdog_timeo = PEGASUS_TX_TIMEOUT;
+ net->tx_timeout = pegasus_tx_timeout;
+ net->do_ioctl = pegasus_ioctl;
+ net->hard_start_xmit = pegasus_start_xmit;
+ net->set_multicast_list = pegasus_set_rx_mode;
+ net->get_stats = pegasus_netdev_stats;
+ net->mtu = PEGASUS_MTU;
+
+ pegasus->usb = dev;
+ pegasus->net = net;
+ pegasus->pegasus_lock = SPIN_LOCK_UNLOCKED;
+
+ FILL_BULK_URB(&pegasus->rx_urb, dev, usb_rcvbulkpipe(dev, 1),
+ pegasus->rx_buff, PEGASUS_MAX_MTU, pegasus_read_bulk,
+ pegasus);
+ FILL_BULK_URB(&pegasus->tx_urb, dev, usb_sndbulkpipe(dev, 2),
+ pegasus->tx_buff, PEGASUS_MAX_MTU, pegasus_write_bulk,
+ pegasus);
+ FILL_INT_URB(&pegasus->intr_urb, dev, usb_rcvintpipe(dev, 0),
+ pegasus->intr_buff, 8, pegasus_irq, pegasus, 250);
+
+
+ printk(KERN_INFO "%s: ADMtek AN986 Pegasus usb device\n", net->name);
+
+ return pegasus;
}
-
-static void usb_hpna_disconnect( struct usb_device *dev, void *ptr )
+static void pegasus_disconnect(struct usb_device *dev, void *ptr)
{
- struct net_device *net_dev = ptr;
- struct usb_hpna *hpna = net_dev->priv;
+ struct pegasus *pegasus = ptr;
+ if (!pegasus) {
+ warn("unregistering non-existant device");
+ return;
+ }
- if ( net_dev->flags & IFF_UP )
- dev_close(net_dev);
-
- unregister_netdev( net_dev );
+ if (pegasus->net->flags & IFF_UP)
+ dev_close(pegasus->net);
- if ( !hpna ) /* should never happen */
- return;
-
- usb_unlink_urb( &hpna->rx_urb );
- usb_unlink_urb( &hpna->tx_urb );
-/* usb_unlink_urb( &hpna->intr_urb );*/
- kfree_s(hpna->rx_buff, MAX_MTU);
- kfree_s(hpna->tx_buff, MAX_MTU);
+ unregister_netdev(pegasus->net);
- hpna->usb_dev = NULL;
- hpna->present = 0;
+ usb_unlink_urb(&pegasus->rx_urb);
+ usb_unlink_urb(&pegasus->tx_urb);
+/* usb_unlink_urb(&pegasus->intr_urb);*/
- printk("USB HPNA disconnected\n");
+ kfree(pegasus);
}
-
-static struct usb_driver usb_hpna_driver = {
- "ADMtek \"Pegasus\" USB Ethernet",
- usb_hpna_probe,
- usb_hpna_disconnect,
- {NULL, NULL}
+static struct usb_driver pegasus_driver = {
+ name: "pegasus",
+ probe: pegasus_probe,
+ disconnect: pegasus_disconnect,
};
-
-
-static int __init start_hpna( void )
+int __init pegasus_init(void)
{
printk( version );
- return usb_register( &usb_hpna_driver );
+ return usb_register(&pegasus_driver);
}
-
-static void __exit stop_hpna( void )
+void __exit pegasus_exit(void)
{
- usb_deregister( &usb_hpna_driver );
+ usb_deregister(&pegasus_driver);
}
-
-module_init( start_hpna );
-module_exit( stop_hpna );
+module_init(pegasus_init);
+module_exit(pegasus_exit);
static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, int breadth)
{
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- struct uhci_td *td, *prevtd = NULL;
+ struct uhci_td *td, *prevtd;
if (!urbp)
return;
return -EINPROGRESS;
}
+static int usb_control_retrigger_status(urb_t *urb);
+
static int uhci_result_control(urb_t *urb)
{
struct urb_priv *urbp = urb->hcpriv;
if (!td)
return -EINVAL;
+ if (urbp->short_control_packet)
+ goto status_phase;
+
/* The first TD is the SETUP phase, check the status, but skip */
/* the count */
status = uhci_status_bits(td->status);
/* If SPD is set then we received a short packet */
/* There will be no status phase at the end */
- /* FIXME: Re-setup the queue to run the STATUS phase? */
if ((td->status & TD_CTRL_SPD) &&
(uhci_actual_length(td->status) < uhci_expected_length(td->info)))
- return 0;
+ return usb_control_retrigger_status(urb);
if (status)
goto td_error;
td = td->list.next;
}
+status_phase:
/* Control status phase */
status = uhci_status_bits(td->status);
/* APC BackUPS Pro kludge */
- /* It tries to send all of the descriptor instead of */
- /* the amount we requested */
+ /* It tries to send all of the descriptor instead of the amount */
+ /* we requested */
if (td->status & TD_CTRL_IOC &&
status & TD_CTRL_ACTIVE &&
status & TD_CTRL_NAK)
return uhci_map_status(status, uhci_packetout(td->info));
}
+static int usb_control_retrigger_status(urb_t *urb)
+{
+ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+ struct uhci *uhci = urb->dev->bus->hcpriv;
+ struct uhci_td *td, *nexttd;
+
+ urbp->short_control_packet = 1;
+
+ /* Delete all of the TD's except for the status TD at the end */
+ td = urbp->list.begin;
+ while (td && td->list.next) {
+ nexttd = td->list.next;
+
+ uhci_remove_td_from_urb(urb, td);
+
+ uhci_remove_td(uhci, td);
+
+ uhci_free_td(td);
+
+ td = nexttd;
+ }
+
+ /* Create a new QH to avoid pointer overwriting problems */
+ uhci_remove_qh(uhci, urbp->qh);
+
+ urbp->qh = uhci_alloc_qh(urb->dev);
+ if (!urbp->qh)
+ return -ENOMEM;
+
+ /* One TD, who cares about Breadth first? */
+ uhci_insert_tds_in_qh(urbp->qh, urb, 0);
+
+ /* Low speed or small transfers gets a different queue and treatment */
+ if (urb->pipe & TD_CTRL_LS)
+ uhci_insert_qh(uhci, &uhci->skel_ls_control_qh, urbp->qh);
+ else
+ uhci_insert_qh(uhci, &uhci->skel_hs_control_qh, urbp->qh);
+
+ return -EINPROGRESS;
+}
+
/*
* Interrupt transfers
*/
struct urb_priv {
struct uhci_qh *qh; /* QH for this URB */
- int fsbr;
+ int fsbr; /* Did this URB turn on FSBR? */
+
+ char short_control_packet; /* If we get a short packet during */
+ /* a control transfer, retrigger */
+ /* the status phase */
unsigned long inserttime; /* In jiffies */
* Further reference:
* This driver is based on the 'USB Mass Storage Class' document. This
* describes in detail the protocol used to communicate with such
- * devices. Clearly, the designers had SCSI commands in mind when they
- * created this document. The commands are all similar to commands
- * in the SCSI-II specification.
+ * devices. Clearly, the designers had SCSI and ATAPI commands in mind
+ * when they created this document. The commands are all very similar
+ * to commands in the SCSI-II and ATAPI specifications.
*
* It is important to note that in a number of cases this class exhibits
* class-specific exemptions from the USB specification. Notably the
static int my_host_number;
-int usb_stor_debug = 1;
-
struct us_data;
typedef int (*trans_cmnd)(Scsi_Cmnd*, struct us_data*);
typedef void (*proto_cmnd)(Scsi_Cmnd*, struct us_data*);
struct us_data {
- struct us_data *next; /* next device */
+ struct us_data *next; /* next device */
struct usb_device *pusb_dev; /* this usb_device */
unsigned int flags; /* from filter initially */
__u8 ifnum; /* interface number */
int host_number; /* to find us */
int host_no; /* allocated by scsi */
Scsi_Cmnd *srb; /* current srb */
+ Scsi_Cmnd *queue_srb; /* the single queue slot */
int action; /* what to do */
- wait_queue_head_t waitq; /* thread waits */
- wait_queue_head_t ip_waitq; /* for CBI interrupts */
+ struct semaphore ip_waitq; /* for CBI interrupts */
__u16 ip_data; /* interrupt data */
int ip_wanted; /* needed */
int pid; /* control thread */
struct semaphore *notify; /* wait for thread to begin */
void *irq_handle; /* for USB int requests */
unsigned int irqpipe; /* pipe for release_irq */
+ struct semaphore sleeper; /* to sleep on */
+ struct semaphore queue_exclusion; /* to protect data structs */
};
/*
* Data transfer routines
***********************************************************************/
-/* Transfer one buffer (breaking into packets if necessary)
- * Note that this function is necessary because if the device NAKs, we
- * need to know that information directly
+/* FIXME: the names of these functions are poorly choosen. */
+
+/*
+ * Transfer one SCSI scatter-gather buffer via bulk transfer
+ *
+ * Note that this function is necessary because we want the ability to
+ * use scatter-gather memory. Good performance is achived by a combination
+ * of scatter-gather and clustering (which makes each chunk bigger).
*
- * FIXME: is the above true? Or will the URB status show ETIMEDOUT after
- * retrying several times allready? Perhaps this is the way we should
- * be going anyway?
+ * Note that the lower layer will always retry when a NAK occurs, up to the
+ * timeout limit. Thus we don't have to worry about it for individual
+ * packets.
*/
-static int us_one_transfer(struct us_data *us, int pipe, char *buf, int length)
+static int us_bulk_transfer(struct us_data *us, int pipe,
+ char *buf, int length)
{
- int max_size;
- int this_xfer;
int result;
int partial;
- int maxtry;
-
- /* determine the maximum packet size for these transfers */
- max_size = usb_maxpacket(us->pusb_dev,
- pipe, usb_pipeout(pipe)) * 16;
-
- /* while we have data left to transfer */
- while (length) {
-
- /* calculate how long this will be -- maximum or a remainder */
- this_xfer = length > max_size ? max_size : length;
- length -= this_xfer;
-
- /* FIXME: this number is totally outrageous. We need to pick
- * a better (smaller) number).
- */
-
- /* setup the retry counter */
- maxtry = 100;
-
- /* set up the transfer loop */
- do {
- /* transfer the data */
- US_DEBUGP("Bulk xfer 0x%x(%d) try #%d\n",
- (unsigned int)buf, this_xfer, 101 - maxtry);
- result = usb_bulk_msg(us->pusb_dev, pipe, buf,
- this_xfer, &partial, HZ*5);
- US_DEBUGP("bulk_msg returned %d xferred %d/%d\n",
- result, partial, this_xfer);
-
- /* if we stall, we need to clear it before we go on */
- if (result == -EPIPE) {
- US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
- usb_clear_halt(us->pusb_dev, pipe);
- }
-
- /* update to show what data was transferred */
- this_xfer -= partial;
- buf += partial;
-
- /* NAK - we retry a few times */
- if (result == -ETIMEDOUT) {
- US_DEBUGP("us_one_transfer: device NAKed\n");
-
- /* if our try counter reaches 0, bail out */
- if (!maxtry--)
- return -ETIMEDOUT;
+ /* transfer the data */
+ US_DEBUGP("Bulk xfer 0x%x(%d)\n", (unsigned int)buf, length);
+ result = usb_bulk_msg(us->pusb_dev, pipe, buf, length, &partial, HZ*5);
+ US_DEBUGP("bulk_msg returned %d xferred %d/%d\n",
+ result, partial, length);
+
+ /* if we stall, we need to clear it before we go on */
+ if (result == -EPIPE) {
+ US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
+ usb_clear_halt(us->pusb_dev, pipe);
+ }
- /* just continue the while loop */
- continue;
- }
-
- /* other errors (besides NAK) -- we just bail out*/
- if (result != 0) {
- US_DEBUGP("us_one_transfer: device returned error %d\n", result);
- return result;
- }
+ /* did we send all the data? */
+ if (partial == length) {
+ return US_BULK_TRANSFER_GOOD;
+ }
- /* continue until this transfer is done */
- } while ( this_xfer );
+ /* uh oh... we have an error code, so something went wrong. */
+ if (result) {
+ /* NAK - that means we've retried a few times allready */
+ if (result == -ETIMEDOUT) {
+ US_DEBUGP("us_bulk_transfer: device NAKed\n");
+ }
+ return US_BULK_TRANSFER_FAILED;
}
- /* if we get here, we're done and successful */
- return 0;
+ /* no error code, so we must have transferred some data,
+ * just not all of it */
+ return US_BULK_TRANSFER_SHORT;
}
-static unsigned int us_transfer_length(Scsi_Cmnd *srb);
-
-/* transfer one SCSI command, using scatter-gather if requested */
-/* FIXME: what do the return codes here mean? */
-static int us_transfer(Scsi_Cmnd *srb, int dir_in)
+/*
+ * Transfer an entire SCSI command's worth of data payload over the bulk
+ * pipe.
+ *
+ * Note that this uses us_bulk_transfer to achive it's goals -- this
+ * function simply determines if we're going to use scatter-gather or not,
+ * and acts appropriately. For now, it also re-interprets the error codes.
+ */
+static void us_transfer(Scsi_Cmnd *srb, int dir_in)
{
- struct us_data *us = (struct us_data *)srb->host_scribble;
+ struct us_data *us;
int i;
int result = -1;
- unsigned int pipe = dir_in ? usb_rcvbulkpipe(us->pusb_dev, us->ep_in) :
- usb_sndbulkpipe(us->pusb_dev, us->ep_out);
+ unsigned int pipe;
+ struct scatterlist *sg;
- /* FIXME: stop transferring data at us_transfer_length(), not
- * bufflen */
+ /* calculate the appropriate pipe information */
+ us = (struct us_data*) srb->host_scribble;
+ if (dir_in)
+ pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
+ else
+ pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
+
+ /* are we scatter-gathering? */
if (srb->use_sg) {
- struct scatterlist *sg = (struct scatterlist *) srb->request_buffer;
+ /* loop over all the scatter gather structures and
+ * make the appropriate requests for each, until done
+ */
+ sg = (struct scatterlist *) srb->request_buffer;
for (i = 0; i < srb->use_sg; i++) {
- result = us_one_transfer(us, pipe, sg[i].address, sg[i].length);
+ result = us_bulk_transfer(us, pipe, sg[i].address,
+ sg[i].length);
if (result)
break;
}
}
else
- result = us_one_transfer(us, pipe, srb->request_buffer,
- us_transfer_length(srb));
+ /* no scatter-gather, just make the request */
+ result = us_bulk_transfer(us, pipe, srb->request_buffer,
+ srb->request_bufflen);
- if (result < 0)
- US_DEBUGP("us_transfer returning error %d\n", result);
- return result;
+ /* return the result in the data structure itself */
+ srb->result = result;
}
/* calculate the length of the data transfer (not the command) for any
case MODE_SENSE:
return srb->cmnd[4];
+ case READ_CAPACITY:
+ return 8;
+
case LOG_SENSE:
case MODE_SENSE_10:
return (srb->cmnd[7] << 8) + srb->cmnd[8];
}
if (srb->use_sg) {
- struct scatterlist *sg = (struct scatterlist *) srb->request_buffer;
+ struct scatterlist *sg;
+ sg = (struct scatterlist *) srb->request_buffer;
for (i = 0; i < srb->use_sg; i++) {
total += sg[i].length;
}
* Protocol routines
***********************************************************************/
-static int CB_transport(Scsi_Cmnd *srb, struct us_data *us);
-static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us);
+static void ATAPI_command(Scsi_Cmnd *srb, struct us_data *us)
+{
+ int old_cmnd = 0;
+ int result;
+
+ /* Fix some commands -- this is a form of mode translation
+ * ATAPI devices only accept 12 byte long commands
+ *
+ * NOTE: This only works because a Scsi_Cmnd struct field contains
+ * a unsigned char cmnd[12], so we know we have storage available
+ */
+
+ /* set command length to 12 bytes */
+ srb->cmd_len = 12;
+
+ /* determine the correct (or minimum) data length for these commands */
+ switch (us->srb->cmnd[0]) {
+
+ /* change MODE_SENSE/MODE_SELECT from 6 to 10 byte commands */
+ case MODE_SENSE:
+ case MODE_SELECT:
+ /* save the command so we can tell what it was */
+ old_cmnd = srb->cmnd[0];
+
+ srb->cmnd[11] = 0;
+ srb->cmnd[10] = 0;
+ srb->cmnd[9] = 0;
+ srb->cmnd[8] = srb->cmnd[4];
+ srb->cmnd[7] = 0;
+ srb->cmnd[6] = 0;
+ srb->cmnd[5] = 0;
+ srb->cmnd[4] = 0;
+ srb->cmnd[3] = 0;
+ srb->cmnd[2] = srb->cmnd[2];
+ srb->cmnd[1] = srb->cmnd[1];
+ srb->cmnd[0] = srb->cmnd[0] | 0x40;
+ break;
+
+ /* change READ_6/WRITE_6 to READ_10/WRITE_10, which
+ * are ATAPI commands */
+ case WRITE_6:
+ case READ_6:
+ srb->cmnd[11] = 0;
+ srb->cmnd[10] = 0;
+ srb->cmnd[9] = 0;
+ srb->cmnd[8] = srb->cmnd[4];
+ srb->cmnd[7] = 0;
+ srb->cmnd[6] = 0;
+ srb->cmnd[5] = srb->cmnd[3];
+ srb->cmnd[4] = srb->cmnd[2];
+ srb->cmnd[3] = srb->cmnd[1] & 0x1F;
+ srb->cmnd[2] = 0;
+ srb->cmnd[1] = srb->cmnd[1] & 0xE0;
+ srb->cmnd[0] = srb->cmnd[0] | 0x20;
+ break;
+ } /* end switch on cmnd[0] */
+
+ /* send the command to the transport layer */
+ result = us->transport(srb, us);
+
+ /* If we got a short transfer, but it was for a command that
+ * can have short transfers, we're actually okay
+ */
+ if ((us->srb->result == US_BULK_TRANSFER_SHORT) &&
+ ((us->srb->cmnd[0] == REQUEST_SENSE) ||
+ (us->srb->cmnd[0] == INQUIRY) ||
+ (us->srb->cmnd[0] == MODE_SENSE) ||
+ (us->srb->cmnd[0] == LOG_SENSE) ||
+ (us->srb->cmnd[0] == MODE_SENSE_10))) {
+ us->srb->result = DID_OK;
+ }
+
+ /*
+ * If we have an error, we're going to do a
+ * REQUEST_SENSE automatically
+ */
+ if (result != USB_STOR_TRANSPORT_GOOD) {
+ int temp_result;
+ void* old_request_buffer;
+ int old_sg;
+
+ US_DEBUGP("Command FAILED: Issuing auto-REQUEST_SENSE\n");
+
+ us->srb->cmnd[0] = REQUEST_SENSE;
+ us->srb->cmnd[1] = 0;
+ us->srb->cmnd[2] = 0;
+ us->srb->cmnd[3] = 0;
+ us->srb->cmnd[4] = 18;
+ us->srb->cmnd[5] = 0;
+
+ /* set the buffer length for transfer */
+ old_request_buffer = us->srb->request_buffer;
+ old_sg = us->srb->use_sg;
+ us->srb->request_bufflen = 18;
+ us->srb->request_buffer = us->srb->sense_buffer;
+
+ /* FIXME: what if this command fails? */
+ temp_result = us->transport(us->srb, us);
+ US_DEBUGP("-- Result from auto-sense is %d\n", temp_result);
+ US_DEBUGP("-- sense key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n",
+ us->srb->sense_buffer[2] & 0xf,
+ us->srb->sense_buffer[12],
+ us->srb->sense_buffer[13]);
+
+ /* set the result so the higher layers expect this data */
+ us->srb->result = CHECK_CONDITION;
+
+ /* we're done here */
+ us->srb->request_buffer = old_request_buffer;
+ us->srb->use_sg = old_sg;
+ return;
+ }
+
+ /* Fix the MODE_SENSE data if we translated the command
+ */
+ if (old_cmnd == MODE_SENSE) {
+ unsigned char *dta = (unsigned char *)us->srb->request_buffer;
+
+ /* FIXME: we need to compress the entire data structure here
+ */
+ dta[0] = dta[1]; /* data len */
+ dta[1] = dta[2]; /* med type */
+ dta[2] = dta[3]; /* dev-spec prm */
+ dta[3] = dta[7]; /* block desc len */
+ printk (KERN_DEBUG USB_STORAGE
+ "new MODE_SENSE_6 data = %.2X %.2X %.2X %.2X\n",
+ dta[0], dta[1], dta[2], dta[3]);
+ }
+
+ /* Fix-up the return data from an INQUIRY command to show
+ * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us
+ */
+ if (us->srb->cmnd[0] == INQUIRY) {
+ ((unsigned char *)us->srb->request_buffer)[2] |= 0x2;
+ }
+}
+
static void ufi_command(Scsi_Cmnd *srb, struct us_data *us)
{
int old_cmnd = 0;
+ int result;
/* fix some commands -- this is a form of mode translation
* UFI devices only accept 12 byte long commands
} /* end switch on cmnd[0] */
/* send the command to the transport layer */
- us->srb->result = us->transport(srb, us);
+ result = us->transport(srb, us);
- /* if we have an error, we're going to do a
- * REQUEST_SENSE automatically */
+ /* If we got a short transfer, but it was for a command that
+ * can have short transfers, we're actually okay
+ */
+ if ((us->srb->result == US_BULK_TRANSFER_SHORT) &&
+ ((us->srb->cmnd[0] == REQUEST_SENSE) ||
+ (us->srb->cmnd[0] == INQUIRY) ||
+ (us->srb->cmnd[0] == MODE_SENSE) ||
+ (us->srb->cmnd[0] == LOG_SENSE) ||
+ (us->srb->cmnd[0] == MODE_SENSE_10))) {
+ us->srb->result = DID_OK;
+ }
- /* FIXME: we should only do this for device
- * errors, not system errors */
- if (us->srb->result) {
+ /*
+ * If we have an error, we're going to do a
+ * REQUEST_SENSE automatically
+ */
+ if (result != USB_STOR_TRANSPORT_GOOD) {
int temp_result;
- int count;
void* old_request_buffer;
+ int old_sg;
US_DEBUGP("Command FAILED: Issuing auto-REQUEST_SENSE\n");
- /* set the result so the higher layers expect this data */
- us->srb->result = CHECK_CONDITION;
-
us->srb->cmnd[0] = REQUEST_SENSE;
us->srb->cmnd[1] = 0;
us->srb->cmnd[2] = 0;
/* set the buffer length for transfer */
old_request_buffer = us->srb->request_buffer;
+ old_sg = us->srb->use_sg;
us->srb->request_bufflen = 18;
- us->srb->request_buffer = kmalloc(18, GFP_KERNEL);
+ us->srb->request_buffer = us->srb->sense_buffer;
/* FIXME: what if this command fails? */
temp_result = us->transport(us->srb, us);
US_DEBUGP("-- Result from auto-sense is %d\n", temp_result);
-
- /* copy the data from the request buffer to the sense buffer */
- for(count = 0; count < 18; count++)
- us->srb->sense_buffer[count] =
- ((unsigned char *)(us->srb->request_buffer))[count];
-
US_DEBUGP("-- sense key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n",
us->srb->sense_buffer[2] & 0xf,
- us->srb->sense_buffer[12], us->srb->sense_buffer[13]);
+ us->srb->sense_buffer[12],
+ us->srb->sense_buffer[13]);
+
+ /* set the result so the higher layers expect this data */
+ us->srb->result = CHECK_CONDITION;
/* we're done here */
- kfree(us->srb->request_buffer);
us->srb->request_buffer = old_request_buffer;
+ us->srb->use_sg = old_sg;
return;
}
- /* FIXME: if we need to send more data, or recieve data, we should
- * do it here. Then, we can do status handling here also.
- *
- * This includes MODE_SENSE from above
+ /* Fix the MODE_SENSE data here if we had to translate the command
*/
if (old_cmnd == MODE_SENSE) {
unsigned char *dta = (unsigned char *)us->srb->request_buffer;
- /* calculate the new length */
- int length = (dta[0] << 8) + dta[1] + 2;
-
- /* copy the available data length into the structure */
- us->srb->cmnd[7] = length >> 8;
- us->srb->cmnd[8] = length & 0xFF;
-
- /* send the command to the transport layer */
- us->srb->result = us->transport(srb, us);
-
- /* FIXME: this assumes that the 2nd attempt is always
- * successful convert MODE_SENSE_10 return data format
- * to MODE_SENSE_6 format */
+ /* FIXME: we need to compress the entire data structure here
+ */
dta[0] = dta[1]; /* data len */
dta[1] = dta[2]; /* med type */
dta[2] = dta[3]; /* dev-spec prm */
dta[0], dta[1], dta[2], dta[3]);
}
- /* FIXME: if this was a TEST_UNIT_READY, and we get a NOT READY/
- * LOGICAL DRIVE NOT READY then we do a START_STOP, and retry
- */
-
- /* FIXME: here is where we need to fix-up the return data from
- * an INQUIRY command to show ANSI SCSI rev 2
- */
-
- /* FIXME: The rest of this is bogus. usb_control_msg() will only
- * return an error if we've really honked things up. If it just
- * needs a START_STOP, then we'll get some data back via
- * REQUEST_SENSE -- either way, this belongs at a higher level
+ /* Fix-up the return data from an INQUIRY command to show
+ * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us
*/
-
-#if 0
- /* For UFI, if this is the first time we've sent this TEST_UNIT_READY
- * command, we can try again
- */
- if (!done_start && (us->subclass == US_SC_UFI)
- && (cmd[0] == TEST_UNIT_READY) && (result < 0)) {
-
- /* as per spec try a start command, wait and retry */
- wait_ms(100);
-
- done_start++;
- memset(cmd, 0, sizeof(cmd));
- cmd[0] = START_STOP;
- cmd[4] = 1; /* start */
-
- result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
- US_CBI_ADSC,
- USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- 0, us->ifnum,
- cmd, 12, HZ*5);
- US_DEBUGP("Next usb_control_msg returns %d\n", result);
-
- /* allow another retry */
- retry++;
- continue;
+ if (us->srb->cmnd[0] == INQUIRY) {
+ ((unsigned char *)us->srb->request_buffer)[2] |= 0x2;
}
-#endif
}
static void transparent_scsi_command(Scsi_Cmnd *srb, struct us_data *us)
{
- unsigned int savelen = us->srb->request_bufflen;
- unsigned int saveallocation = 0;
-
-#if 0
- /* force attention on first command */
- if (!us->attention_done) {
- if (us->srb->cmnd[0] == REQUEST_SENSE) {
- US_DEBUGP("forcing unit attention\n");
- us->attention_done = 1;
-
- if (us->srb->result == USB_STOR_TRANSPORT_GOOD) {
- unsigned char *p = (unsigned char *)us->srb->request_buffer;
-
- if ((p[2] & 0x0f) != UNIT_ATTENTION) {
- p[2] = UNIT_ATTENTION;
- p[12] = 0x29; /* power on, reset or bus-reset */
- p[13] = 0;
- } /* if ((p[2] & 0x0f) != UNIT_ATTENTION) */
- } /* if (us->srb->result == USB_STORE_TRANSPORT_GOOD) */
- }
- } /* if (!us->attention_done) */
-#endif
-
- /* If the command has a variable-length payload, then we do them
- * in two steps -- first we do the minimum, then we recalculate
- * then length, and re-issue the command
- *
- * we use savelen to remember how much buffer we really have
- * we use savealloction to remember how much was really requested
- */
+ unsigned int result = 0;
- /* FIXME: remove savelen based on mods to us_transfer_length() */
- switch (us->srb->cmnd[0]) {
- case REQUEST_SENSE:
- if (us->srb->request_bufflen > 18)
- us->srb->request_bufflen = 18;
- else
- break;
- saveallocation = us->srb->cmnd[4];
- us->srb->cmnd[4] = 18;
- break;
-
- case INQUIRY:
- if (us->srb->request_bufflen > 36)
- us->srb->request_bufflen = 36;
- else
- break;
- saveallocation = us->srb->cmnd[4];
- us->srb->cmnd[4] = 36;
- break;
-
- case MODE_SENSE:
- if (us->srb->request_bufflen > 4)
- us->srb->request_bufflen = 4;
- else
- break;
- saveallocation = us->srb->cmnd[4];
- us->srb->cmnd[4] = 4;
- break;
-
- case LOG_SENSE:
- case MODE_SENSE_10:
- if (us->srb->request_bufflen > 8)
- us->srb->request_bufflen = 8;
- else
- break;
- saveallocation = (us->srb->cmnd[7] << 8) | us->srb->cmnd[8];
- us->srb->cmnd[7] = 0;
- us->srb->cmnd[8] = 8;
- break;
-
- default:
- break;
- } /* end switch on cmnd[0] */
-
/* This code supports devices which do not support {READ|WRITE}_6
* Apparently, neither Windows or MacOS will use these commands,
* so some devices do not support them
US_DEBUGP("Changing WRITE_6 to WRITE_10\n");
US_DEBUG(us_show_command(us->srb));
}
- } /* end if (us->flags & US_FL_MODE_XLATE) */
+ } /* if (us->flags & US_FL_MODE_XLATE) */
/* send the command to the transport layer */
- us->srb->result = us->transport(us->srb, us);
+ result = us->transport(us->srb, us);
+
+ /* If we got a short transfer, but it was for a command that
+ * can have short transfers, we're actually okay
+ */
+ if ((us->srb->result == US_BULK_TRANSFER_SHORT) &&
+ ((us->srb->cmnd[0] == REQUEST_SENSE) ||
+ (us->srb->cmnd[0] == INQUIRY) ||
+ (us->srb->cmnd[0] == MODE_SENSE) ||
+ (us->srb->cmnd[0] == LOG_SENSE) ||
+ (us->srb->cmnd[0] == MODE_SENSE_10))) {
+ us->srb->result = DID_OK;
+ }
/* if we have an error, we're going to do a REQUEST_SENSE
* automatically */
- /* FIXME: we should only do this for device errors, not
- * system errors */
- if (us->srb->result) {
+ if (result != USB_STOR_TRANSPORT_GOOD) {
int temp_result;
- int count;
+ int old_sg;
void* old_request_buffer;
US_DEBUGP("Command FAILED: Issuing auto-REQUEST_SENSE\n");
- /* set the result so the higher layers expect this data */
- us->srb->result = CHECK_CONDITION;
-
+ /* set up the REQUEST_SENSE command and parameters */
us->srb->cmnd[0] = REQUEST_SENSE;
us->srb->cmnd[1] = 0;
us->srb->cmnd[2] = 0;
/* set the buffer length for transfer */
old_request_buffer = us->srb->request_buffer;
+ old_sg = us->srb->use_sg;
us->srb->request_bufflen = 18;
- us->srb->request_buffer = kmalloc(18, GFP_KERNEL);
+ us->srb->request_buffer = us->srb->sense_buffer;
/* FIXME: what if this command fails? */
temp_result = us->transport(us->srb, us);
US_DEBUGP("-- Result from auto-sense is %d\n", temp_result);
-
- /* copy the data from the request buffer to the sense buffer */
- for(count = 0; count < 18; count++)
- us->srb->sense_buffer[count] =
- ((unsigned char *)(us->srb->request_buffer))[count];
-
US_DEBUGP("-- sense key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n",
us->srb->sense_buffer[2] & 0xf,
- us->srb->sense_buffer[12], us->srb->sense_buffer[13]);
+ us->srb->sense_buffer[12],
+ us->srb->sense_buffer[13]);
+
+ /* set the result so the higher layers expect this data */
+ us->srb->result = CHECK_CONDITION;
/* we're done here */
- kfree(us->srb->request_buffer);
+ us->srb->use_sg = old_sg;
us->srb->request_buffer = old_request_buffer;
return;
}
- if (savelen != us->srb->request_bufflen) {
- unsigned char *p = (unsigned char *)us->srb->request_buffer;
- unsigned int length = 0;
-
- /* set correct length and retry */
- switch (us->srb->cmnd[0]) {
-
- /* FIXME: we should try to get all the sense data */
- case REQUEST_SENSE:
- /* simply return 18 bytes */
- p[7] = 10;
- length = us->srb->request_bufflen;
- break;
-
- case INQUIRY:
- length = p[4] + 5 > savelen ? savelen : p[4] + 5;
- us->srb->cmnd[4] = length;
- break;
-
- case MODE_SENSE:
- US_DEBUGP("MODE_SENSE Mode data length is %d\n", p[0]);
- length = p[0] + 1 > savelen ? savelen : p[0] + 1;
- us->srb->cmnd[4] = length;
- break;
-
- case LOG_SENSE:
- length = ((p[2] << 8) + p[3]) + 4 > savelen ? savelen : ((p[2] << 8) + p[3]) + 4;
- us->srb->cmnd[7] = length >> 8;
- us->srb->cmnd[8] = length;
- break;
-
- case MODE_SENSE_10:
- US_DEBUGP("MODE_SENSE_10 Mode data length is %d\n",
- (p[0] << 8) + p[1]);
- length = ((p[0] << 8) + p[1]) + 6 > savelen ? savelen : ((p[0] << 8) + p[1]) + 6;
- us->srb->cmnd[7] = length >> 8;
- us->srb->cmnd[8] = length;
- break;
- } /* end switch on cmnd[0] */
-
- US_DEBUGP("Old/New length = %d/%d\n",
- savelen, length);
-
- /* issue the new command */
- /* FIXME: this assumes that the second attempt is
- * always successful */
- if (us->srb->request_bufflen != length) {
- US_DEBUGP("redoing cmd with len=%d\n", length);
- us->srb->request_bufflen = length;
- us->srb->result = us->transport(us->srb, us);
- }
-
- /* reset back to original values */
- us->srb->request_bufflen = savelen;
-
- /* fix data as necessary */
- switch (us->srb->cmnd[0]) {
- case INQUIRY:
- if ((((unsigned char*)us->srb->request_buffer)[2] & 0x7) == 0) {
- US_DEBUGP("Fixing INQUIRY data, setting SCSI rev to 2\n");
- ((unsigned char*)us->srb->request_buffer)[2] |= 2;
- }
- /* FALL THROUGH */
- case REQUEST_SENSE:
- case MODE_SENSE:
- if (us->srb->use_sg == 0 && length > 0) {
- int i;
- printk(KERN_DEBUG "Data is");
- for (i = 0; i < 32 && i < length; ++i)
- printk(" %.2x", ((unsigned char *)us->srb->request_buffer)[i]);
- if (i < length)
- printk(" ...");
- printk("\n");
- }
-
- /* FIXME: is this really necessary? */
- us->srb->cmnd[4] = saveallocation;
- break;
-
- case LOG_SENSE:
- case MODE_SENSE_10:
- /* FIXME: is this really necessary? */
- us->srb->cmnd[7] = saveallocation >> 8;
- us->srb->cmnd[8] = saveallocation;
- break;
- } /* end switch on cmnd[0] */
- } /* if good command */
+ /* fix the results of an INQUIRY */
+ if (us->srb->cmnd[0] == INQUIRY) {
+ US_DEBUGP("Fixing INQUIRY data, setting SCSI rev to 2\n");
+ ((unsigned char*)us->srb->request_buffer)[2] |= 2;
+ }
}
/***********************************************************************
/* was this a wanted interrupt? */
if (us->ip_wanted) {
us->ip_wanted = 0;
- wake_up(&us->ip_waitq);
+ up(&(us->ip_waitq));
} else {
US_DEBUGP("ERROR: Unwanted interrupt received!\n");
}
return 0;
}
-/* FIXME: this reset function doesn't really reset the port, and it
- * should. Actually it should probably do what it's doing here, and
- * reset the port physically
+/* This issues a CB[I] Reset to the device in question
*/
static int CB_reset(struct us_data *us)
{
cmd[0] = SEND_DIAGNOSTIC;
cmd[1] = 4;
result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
- US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ US_CBI_ADSC,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE,
0, us->ifnum, cmd, sizeof(cmd), HZ*5);
/* long wait for reset */
schedule_timeout(HZ*6);
US_DEBUGP("CB_reset: clearing endpoint halt\n");
- usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
- usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_out));
+ usb_clear_halt(us->pusb_dev,
+ usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
+ usb_clear_halt(us->pusb_dev,
+ usb_rcvbulkpipe(us->pusb_dev, us->ep_out));
US_DEBUGP("CB_reset done\n");
return 0;
}
-static int pop_CB_status(Scsi_Cmnd *srb);
-
-/* FIXME: we also need a CBI_command which sets up the completion
- * interrupt, and waits for it
+/*
+ * Control/Bulk/Interrupt transport
*/
-static int CB_transport(Scsi_Cmnd *srb, struct us_data *us)
+static int CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
{
int result;
US_DEBUGP("CBI gets a command:\n");
US_DEBUG(us_show_command(srb));
- /* FIXME: we aren't setting the ip_wanted indicator early enough, which
- * causes some commands to never complete. This hangs the driver.
- */
-
+ /* COMMAND STAGE */
/* let's send the command via the control pipe */
- result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
- US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- 0, us->ifnum,
- srb->cmnd, srb->cmd_len, HZ*5);
+ result = usb_control_msg(us->pusb_dev,
+ usb_sndctrlpipe(us->pusb_dev,0), US_CBI_ADSC,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0,
+ us->ifnum, srb->cmnd, srb->cmd_len, HZ*5);
/* check the return code for the command */
if (result < 0) {
/* a stall is a fatal condition from the device */
if (result == -EPIPE) {
- US_DEBUGP("-- Stall on control pipe detected. Clearing\n");
-
+ US_DEBUGP("-- Stall on control pipe. Clearing\n");
US_DEBUGP("-- Return from usb_clear_halt() is %d\n",
usb_clear_halt(us->pusb_dev,
- usb_sndctrlpipe(us->pusb_dev, 0)));
+ usb_sndctrlpipe(us->pusb_dev,
+ 0)));
return USB_STOR_TRANSPORT_ERROR;
}
- /* FIXME: we need to handle NAKs here */
+ /* FIXME: we need to handle NAKs here */
return USB_STOR_TRANSPORT_ERROR;
}
+ /* Set up for status notification */
+ us->ip_wanted = 1;
+
+ /* DATA STAGE */
/* transfer the data payload for this command, if one exists*/
if (us_transfer_length(srb)) {
- result = us_transfer(srb, US_DIRECTION(srb->cmnd[0]));
- US_DEBUGP("CBI attempted to transfer data, result is 0x%x\n", result);
-
- /* FIXME: what do the return codes from us_transfer mean? */
- if ((result < 0) &&
- (result != USB_ST_DATAUNDERRUN) &&
- (result != USB_ST_STALL)) {
- return DID_ERROR << 16;
- }
- } /* if (us_transfer_length(srb)) */
+ us_transfer(srb, US_DIRECTION(srb->cmnd[0]));
+ US_DEBUGP("CBI data stage result is 0x%x\n", result);
+ }
- /* get status and return it */
- return pop_CB_status(srb);
+ /* STATUS STAGE */
+
+ /* go to sleep until we get this interrup */
+ /* FIXME: this should be changed to use a timeout */
+ down(&(us->ip_waitq));
+
+ /* FIXME: currently this code is unreachable, but the idea is
+ * necessary. See above comment.
+ */
+ if (us->ip_wanted) {
+ US_DEBUGP("Did not get interrupt on CBI\n");
+ us->ip_wanted = 0;
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ US_DEBUGP("Got interrupt data 0x%x\n", us->ip_data);
+
+ /* UFI gives us ASC and ASCQ, like a request sense */
+ /* FIXME: is this right? Do REQUEST_SENSE and INQUIRY need special
+ * case handling?
+ */
+ if (us->subclass == US_SC_UFI) {
+ if (srb->cmnd[0] == REQUEST_SENSE ||
+ srb->cmnd[0] == INQUIRY)
+ return USB_STOR_TRANSPORT_GOOD;
+ else
+ if (us->ip_data)
+ return USB_STOR_TRANSPORT_FAILED;
+ else
+ return USB_STOR_TRANSPORT_GOOD;
+ }
+
+ /* otherwise, we interpret the data normally */
+ switch (us->ip_data) {
+ case 0x0001:
+ return USB_STOR_TRANSPORT_GOOD;
+ case 0x0002:
+ return USB_STOR_TRANSPORT_FAILED;
+ default:
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ US_DEBUGP("CBI_transport() reached end of function\n");
+ return USB_STOR_TRANSPORT_ERROR;
}
/*
- * Control/Bulk status handler
+ * Control/Bulk transport
*/
-
-static int pop_CB_status(Scsi_Cmnd *srb)
+static int CB_transport(Scsi_Cmnd *srb, struct us_data *us)
{
- struct us_data *us = (struct us_data *)srb->host_scribble;
- int result = 0;
+ int result;
__u8 status[2];
- int retry = 5;
- US_DEBUGP("pop_CB_status, proto=0x%x\n", us->protocol);
- switch (us->protocol) {
- case US_PR_CB:
- /* get from control */
-
- while (retry--) {
- result = usb_control_msg(us->pusb_dev, usb_rcvctrlpipe(us->pusb_dev,0),
- USB_REQ_GET_STATUS, USB_DIR_IN |
- USB_TYPE_STANDARD | USB_RECIP_DEVICE,
- 0, us->ifnum, status, sizeof(status), HZ*5);
- if (result != USB_ST_TIMEOUT)
- break;
- }
- if (result) {
- US_DEBUGP("Bad AP status request %d\n", result);
- return DID_ABORT << 16;
- }
- US_DEBUGP("Got AP status 0x%x 0x%x\n", status[0], status[1]);
- if (srb->cmnd[0] != REQUEST_SENSE && srb->cmnd[0] != INQUIRY &&
- ( (status[0] & ~3) || status[1]))
- return (DID_OK << 16) | 2;
- else
- return USB_STOR_TRANSPORT_GOOD;
- break;
+ US_DEBUGP("CBC gets a command:\n");
+ US_DEBUG(us_show_command(srb));
- /* FIXME: this should be in a separate function */
- case US_PR_CBI:
- /* get from interrupt pipe */
+ /* COMMAND STAGE */
+ /* let's send the command via the control pipe */
+ result = usb_control_msg(us->pusb_dev,
+ usb_sndctrlpipe(us->pusb_dev,0), US_CBI_ADSC,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0,
+ us->ifnum, srb->cmnd, srb->cmd_len, HZ*5);
- /* add interrupt transfer, marked for removal */
- us->ip_wanted = 1;
+ /* check the return code for the command */
+ if (result < 0) {
+ US_DEBUGP("Call to usb_control_msg() returned %d\n", result);
- /* go to sleep until we get this interrup */
- /* FIXME: this should be changed to use a timeout */
- sleep_on(&us->ip_waitq);
-
- if (us->ip_wanted) {
- US_DEBUGP("Did not get interrupt on CBI\n");
- us->ip_wanted = 0;
+ /* a stall is a fatal condition from the device */
+ if (result == -EPIPE) {
+ US_DEBUGP("-- Stall on control pipe. Clearing\n");
+ US_DEBUGP("-- Return from usb_clear_halt() is %d\n",
+ usb_clear_halt(us->pusb_dev,
+ usb_sndctrlpipe(us->pusb_dev,
+ 0)));
return USB_STOR_TRANSPORT_ERROR;
}
-
- US_DEBUGP("Got interrupt data 0x%x\n", us->ip_data);
- /* UFI gives us ASC and ASCQ, like a request sense */
- /* FIXME: is this right? do REQUEST_SENSE and INQUIRY need special
- * case handling?
- */
- if (us->subclass == US_SC_UFI) {
- if (srb->cmnd[0] == REQUEST_SENSE ||
- srb->cmnd[0] == INQUIRY)
- return USB_STOR_TRANSPORT_GOOD;
- else
- if (us->ip_data)
- return USB_STOR_TRANSPORT_FAILED;
- else
- return USB_STOR_TRANSPORT_GOOD;
- }
+ /* FIXME: we need to handle NAKs here */
+ return USB_STOR_TRANSPORT_ERROR;
+ }
- /* otherwise, we interpret the data normally */
- switch (us->ip_data) {
- case 0x0001:
- return USB_STOR_TRANSPORT_GOOD;
- case 0x0002:
- return USB_STOR_TRANSPORT_FAILED;
- default:
- return USB_STOR_TRANSPORT_ERROR;
- }
+ /* DATA STAGE */
+ /* transfer the data payload for this command, if one exists*/
+ if (us_transfer_length(srb)) {
+ us_transfer(srb, US_DIRECTION(srb->cmnd[0]));
+ US_DEBUGP("CBC data stage result is 0x%x\n", result);
}
- US_DEBUGP("pop_CB_status, reached end of function\n");
+
+
+ /* STATUS STAGE */
+ /* FIXME: this is wrong */
+ result = usb_control_msg(us->pusb_dev,
+ usb_rcvctrlpipe(us->pusb_dev,0),
+ USB_REQ_GET_STATUS, USB_DIR_IN |
+ USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+ 0, us->ifnum, status, sizeof(status), HZ*5);
+
+ if (result < 0) {
+ US_DEBUGP("CBC Status stage returns %d\n", result);
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ US_DEBUGP("Got CB status 0x%x 0x%x\n", status[0], status[1]);
+ if (srb->cmnd[0] != REQUEST_SENSE && srb->cmnd[0] != INQUIRY &&
+ ( (status[0] & ~3) || status[1]))
+ return USB_STOR_TRANSPORT_FAILED;
+ else
+ return USB_STOR_TRANSPORT_GOOD;
+
+ US_DEBUGP("CB_transport() reached end of function\n");
return USB_STOR_TRANSPORT_ERROR;
}
+/* FIXME: Does this work? */
static int Bulk_reset(struct us_data *us)
{
int result;
- result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
- US_BULK_RESET, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- US_BULK_RESET_HARD, us->ifnum,
- NULL, 0, HZ*5);
- if (result)
+ result = usb_control_msg(us->pusb_dev,
+ usb_sndctrlpipe(us->pusb_dev,0),
+ US_BULK_RESET,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ US_BULK_RESET_HARD, us->ifnum, NULL, 0, HZ*5);
+
+ if (result < 0)
US_DEBUGP("Bulk hard reset failed %d\n", result);
- usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
- usb_clear_halt(us->pusb_dev, usb_sndbulkpipe(us->pusb_dev, us->ep_out));
+
+ usb_clear_halt(us->pusb_dev,
+ usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
+ usb_clear_halt(us->pusb_dev,
+ usb_sndbulkpipe(us->pusb_dev, us->ep_out));
/* long wait for reset */
schedule_timeout(HZ*6);
}
/*
- * The bulk only protocol handler.
- * Uses the in and out endpoints to transfer commands and data
+ * Bulk only transport
*/
static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
{
int result;
int pipe;
int partial;
-
+
/* set up the command wrapper */
bcb.Signature = US_BULK_CB_SIGN;
bcb.DataTransferLength = us_transfer_length(srb);
bcb.Tag = srb->serial_number;
bcb.Lun = 0;
bcb.Length = srb->cmd_len;
-
+
/* construct the pipe handle */
pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
-
+
/* copy the command payload */
memset(bcb.CDB, 0, sizeof(bcb.CDB));
memcpy(bcb.CDB, srb->cmnd, bcb.Length);
-
+
/* send it to out endpoint */
US_DEBUGP("Bulk command S 0x%x T 0x%x L %d F %d CL %d\n",
bcb.Signature, bcb.Tag, bcb.DataTransferLength,
result = usb_bulk_msg(us->pusb_dev, pipe, &bcb,
US_BULK_CB_WRAP_LEN, &partial, HZ*5);
US_DEBUGP("Bulk command transfer result=%d\n", result);
-
+
/* if we stall, we need to clear it before we go on */
if (result == -EPIPE) {
US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
usb_clear_halt(us->pusb_dev, pipe);
}
-
+
/* if the command transfered well, then we go to the data stage */
- /* FIXME: Regardless of the status of the data stage, we go on to the
- * status stage. Note that this implies that if a command is
- * partially successful, we rely on the device reporting an error
- * the CSW. The spec says that the device may just decide to short us.
- */
if (result == 0) {
/* send/receive data payload, if there is any */
if (bcb.DataTransferLength) {
- result = us_transfer(srb, bcb.Flags);
- US_DEBUGP("Bulk data transfer result 0x%x\n", result);
-#if 0
- if ((result < 0) && (result != USB_ST_DATAUNDERRUN)
- && (result != USB_ST_STALL)) {
- US_DEBUGP("Bulk data transfer result 0x%x\n", result);
- return DID_ABORT << 16;
- }
-#endif
+ us_transfer(srb, bcb.Flags);
+ US_DEBUGP("Bulk data transfer result 0x%x\n",
+ srb->result);
}
}
-
+
/* See flow chart on pg 15 of the Bulk Only Transport spec for
* an explanation of how this code works.
*/
-
+
/* construct the pipe handle */
pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
-
+
/* get CSW for device status */
result = usb_bulk_msg(us->pusb_dev, pipe, &bcs,
US_BULK_CS_WRAP_LEN, &partial, HZ*5);
-
+
/* did the attempt to read the CSW fail? */
if (result == -EPIPE) {
US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
usb_clear_halt(us->pusb_dev, pipe);
-
+
/* get the status again */
result = usb_bulk_msg(us->pusb_dev, pipe, &bcs,
US_BULK_CS_WRAP_LEN, &partial, HZ*5);
-
+
/* if it fails again, we need a reset and return an error*/
if (result == -EPIPE) {
Bulk_reset(us);
- return (DID_ABORT << 16);
+ return USB_STOR_TRANSPORT_ERROR;
}
}
-
+
/* if we still have a failure at this point, we're in trouble */
if (result) {
- US_DEBUGP("Bulk status result = 0x%x\n", result);
- return DID_ABORT << 16;
+ US_DEBUGP("Bulk status result = %d\n", result);
+ return USB_STOR_TRANSPORT_ERROR;
}
-
+
/* check bulk status */
US_DEBUGP("Bulk status S 0x%x T 0x%x R %d V 0x%x\n",
bcs.Signature, bcs.Tag, bcs.Residue, bcs.Status);
if (bcs.Signature != US_BULK_CS_SIGN || bcs.Tag != bcb.Tag ||
bcs.Status > US_BULK_STAT_PHASE || partial != 13) {
US_DEBUGP("Bulk logical error\n");
- return DID_ABORT << 16;
+ return USB_STOR_TRANSPORT_ERROR;
}
-
+
/* based on the status code, we report good or bad */
switch (bcs.Status) {
case US_BULK_STAT_OK:
- /* if there is residue, we really didn't finish the command */
- if (bcs.Residue)
- return DID_ERROR << 16;
- else
- return DID_OK << 16;
+ /* command good -- note that we could be short on data */
+ return USB_STOR_TRANSPORT_GOOD;
case US_BULK_STAT_FAIL:
- return DID_ERROR << 16;
-
+ /* command failed */
+ return USB_STOR_TRANSPORT_FAILED;
+
case US_BULK_STAT_PHASE:
+ /* phase error */
Bulk_reset(us);
- return DID_ERROR << 16;
+ return USB_STOR_TRANSPORT_ERROR;
}
-
- return DID_OK << 16; /* check sense required */
+
+ /* we should never get here, but if we do, we're in trouble */
+ return USB_STOR_TRANSPORT_ERROR;
}
/***********************************************************************
usb_release_irq(us->pusb_dev, us->irq_handle, us->irqpipe);
us->irq_handle = NULL;
}
- if (us->pusb_dev)
- usb_deregister(&storage_driver);
+
+ /* FIXME: release the interface claim here? */
+ // if (us->pusb_dev)
+ // usb_deregister(&storage_driver);
/* FIXME - leaves hanging host template copy */
/* (because scsi layer uses it after removal !!!) */
- while (prev->next != us)
- prev = prev->next;
- prev->next = us->next;
+ if (us_list == us)
+ us_list = us->next;
+ else {
+ while (prev->next != us)
+ prev = prev->next;
+ prev->next = us->next;
+ }
return 0;
}
struct us_data *us = (struct us_data *)srb->host->hostdata[0];
US_DEBUGP("Command wakeup\n");
- if (us->srb) {
- /* busy */
- }
srb->host_scribble = (unsigned char *)us;
- us->srb = srb;
+
+ /* get exclusive access to the structures we want */
+ down(&(us->queue_exclusion));
+
+ /* enqueue the command */
+ us->queue_srb = srb;
srb->scsi_done = done;
us->action = US_ACT_COMMAND;
/* wake up the process task */
-
- wake_up_interruptible(&us->waitq);
+ up(&(us->queue_exclusion));
+ up(&(us->sleeper));
return 0;
}
return 0;
}
+/* FIXME: this doesn't do anything right now */
static int us_bus_reset( Scsi_Cmnd *srb )
{
// struct us_data *us = (struct us_data *)srb->host->hostdata[0];
NULL, /* select_queue_depths */
1, /* can_queue */
-1, /* this_id */
- SG_ALL, /* sg_tablesize */
+ SG_ALL, /* sg_tablesize */
1, /* cmd_per_lun */
0, /* present */
- FALSE, /* unchecked_isa_dma */
- FALSE, /* use_clustering */
+ FALSE, /* unchecked_isa_dma */
+ TRUE, /* use_clustering */
TRUE, /* use_new_eh_code */
TRUE /* emulated */
};
siginfo_t info;
int unsigned long signr;
- interruptible_sleep_on(&us->waitq);
+ US_DEBUGP("*** thread sleeping.\n");
+ down(&(us->sleeper));
+ down(&(us->queue_exclusion));
+ US_DEBUGP("*** thread awakened.\n");
+ /* take the command off the queue */
action = us->action;
us->action = 0;
+ us->srb = us-> queue_srb;
+
+ /* release the queue lock as fast as possible */
+ up(&(us->queue_exclusion));
/* FIXME: we need to examine placment of break; and
* scsi_done() calls */
break;
} /* end switch on action */
-
+
+ /* FIXME: we ignore TERM and KILL... is this right? */
if (signal_pending(current)) {
/* sending SIGUSR1 makes us print out some info */
spin_lock_irq(¤t->sigmask_lock);
signr = dequeue_signal(¤t->blocked, &info);
spin_unlock_irq(¤t->sigmask_lock);
-
- if (signr == SIGUSR2) {
- usb_stor_debug = !usb_stor_debug;
- printk(USB_STORAGE "debug toggle = %d\n", usb_stor_debug);
- } else {
- break; /* exit the loop on any other signal */
- }
- }
- }
+ } /* if (singal_pending(current)) */
+ } /* for (;;) */
// MOD_DEC_USE_COUNT;
printk("usb_stor_control_thread exiting\n");
- /* FIXME: this is a hack to allow for debugging */
- // scsi_unregister_module(MODULE_SCSI_HA, us->htmplt);
-
return 0;
}
unsigned int flags = 0;
GUID(guid); /* Global Unique Identifier */
struct us_data *prev;
- Scsi_Host_Template *htmplt;
int protocol = 0;
int subclass = 0;
struct usb_interface_descriptor *altsetting =
return NULL;
}
memset(ss, 0, sizeof(struct us_data));
+
+ /* Initialize the mutexes only when the struct is new */
+ init_MUTEX_LOCKED(&(ss->sleeper));
+ init_MUTEX(&(ss->queue_exclusion));
}
- /* Initialize the us_data structure with some useful info */
+ /* establish the connection to the new device */
interface = altsetting;
ss->flags = flags;
ss->ifnum = ifnum;
- ss->pusb_dev = dev;
ss->attention_done = 0;
+ ss->pusb_dev = dev;
/* If the device has subclass and protocol, then use that. Otherwise,
* take data from the specific interface.
case US_PR_CBI:
US_DEBUGPX("Control/Bulk/Interrupt\n");
- ss->transport = CB_transport;
+ ss->transport = CBI_transport;
ss->transport_reset = CB_reset;
break;
*/
for (i = 0; i < interface->bNumEndpoints; i++) {
/* is it an BULK endpoint? */
- if ((interface->endpoint[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ if ((interface->endpoint[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
== USB_ENDPOINT_XFER_BULK) {
if (interface->endpoint[i].bEndpointAddress & USB_DIR_IN)
ss->ep_in = interface->endpoint[i].bEndpointAddress &
(ss->protocol == US_PR_CBI && ss->ep_int == 0)) {
US_DEBUGP("Problems with device\n");
if (ss->host) {
- scsi_unregister_module(MODULE_SCSI_HA, ss->htmplt);
kfree(ss->htmplt->name);
kfree(ss->htmplt);
}
US_DEBUGP("Protocol: ");
switch (ss->subclass) {
case US_SC_RBC:
- US_DEBUGPX("Reduced Block Commands\n");
+ US_DEBUGPX("Reduced Block Commands (RBC)\n");
+ ss->proto_handler = transparent_scsi_command;
break;
case US_SC_8020:
- US_DEBUGPX("8020\n");
+ US_DEBUGPX("8020i\n");
+ ss->proto_handler = ATAPI_command;
break;
case US_SC_QIC:
break;
case US_SC_8070:
- US_DEBUGPX("8070\n");
+ US_DEBUGPX("8070i\n");
+ ss->proto_handler = ATAPI_command;
break;
case US_SC_SCSI:
break;
}
- /* We only handle certain protocols. Currently, these are
- *the only ones that devices use.
- */
- if ((ss->subclass != US_SC_SCSI) && (ss->subclass != US_SC_UFI)) {
- US_DEBUGP("Sorry, we do not support that protocol yet.\n");
- US_DEBUGP("If you have a device which uses one of the unsupported\n");
- US_DEBUGP("protocols, please contact mdharm-usb@one-eyed-alien.net\n");
-
- kfree(ss);
- return NULL;
- }
-
/* Allocate memory for the SCSI Host Template */
- if ((htmplt = (Scsi_Host_Template *)
- kmalloc(sizeof(*ss->htmplt), GFP_KERNEL)) == NULL ) {
-
+ if ((ss->htmplt = (Scsi_Host_Template *)
+ kmalloc(sizeof(Scsi_Host_Template),GFP_KERNEL))==NULL ) {
printk(KERN_WARNING USB_STORAGE "Out of memory\n");
kfree(ss);
}
/* Initialize the host template based on the default one */
- memcpy(htmplt, &my_host_template, sizeof(my_host_template));
+ memcpy(ss->htmplt, &my_host_template, sizeof(my_host_template));
/* Grab the next host number */
ss->host_number = my_host_number++;
* can pass the ss pointer to the host controler thread
* in us_detect
*/
- (struct us_data *)htmplt->proc_dir = ss;
+ (struct us_data *)ss->htmplt->proc_dir = ss;
/* shuttle E-USB */
if (dev->descriptor.idVendor == 0x04e6 &&
dev->descriptor.idProduct == 0x0001) {
__u8 qstat[2];
int result;
-
- result = usb_control_msg(ss->pusb_dev, usb_rcvctrlpipe(dev,0),
+
+ result = usb_control_msg(ss->pusb_dev,
+ usb_rcvctrlpipe(dev,0),
1, 0xC0,
0, ss->ifnum,
qstat, 2, HZ*5);
US_DEBUGP("C0 status 0x%x 0x%x\n", qstat[0], qstat[1]);
- init_waitqueue_head(&ss->ip_waitq);
+ init_MUTEX_LOCKED(&(ss->ip_waitq));
ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
- result = usb_request_irq(ss->pusb_dev, ss->irqpipe, CBI_irq,
- 255, (void *)ss, &ss->irq_handle);
- if (result)
+ result = usb_request_irq(ss->pusb_dev, ss->irqpipe,
+ CBI_irq, 255, (void *)ss,
+ &ss->irq_handle);
+ if (result < 0)
return NULL;
-
- interruptible_sleep_on_timeout(&ss->ip_waitq, HZ*6);
- } else if (ss->protocol == US_PR_CBI)
- {
+ /* FIXME: what is this?? */
+ down(&(ss->ip_waitq));
+ } else if (ss->protocol == US_PR_CBI) {
int result;
-
- init_waitqueue_head(&ss->ip_waitq);
+
+ /* set up so we'll wait for notification */
+ init_MUTEX_LOCKED(&(ss->ip_waitq));
/* set up the IRQ pipe and handler */
/* FIXME: This needs to get the period from the device */
}
- /* start up our thread */
+ /* start up our thread */
{
DECLARE_MUTEX_LOCKED(sem);
- init_waitqueue_head(&ss->waitq);
-
ss->notify = &sem;
ss->pid = kernel_thread(usb_stor_control_thread, ss,
CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
if (ss->pid < 0) {
printk(KERN_WARNING USB_STORAGE "Unable to start control thread\n");
- kfree(htmplt);
+ kfree(ss->htmplt);
kfree(ss);
return NULL;
}
/* now register - our detect function will be called */
- scsi_register_module(MODULE_SCSI_HA, htmplt);
+ ss->htmplt->module = &__this_module;
+ scsi_register_module(MODULE_SCSI_HA, ss->htmplt);
/* put us in the list */
- prev = (struct us_data *)&us_list;
- while (prev->next)
- prev = prev->next;
- prev->next = ss;
+ ss->next = us_list;
+ us_list = ss;
}
- printk(KERN_INFO "WARNING: USB Mass Storage data integrity not assured\n");
- printk(KERN_INFO "USB Mass Storage device found at %d\n", dev->devnum);
+ printk(KERN_DEBUG "WARNING: USB Mass Storage data integrity not assured\n");
+ printk(KERN_DEBUG "USB Mass Storage device found at %d\n", dev->devnum);
return ss;
}
return;
ss->pusb_dev = NULL;
- // MOD_DEC_USE_COUNT;
}
int __init usb_stor_init(void)
{
- // MOD_INC_USE_COUNT;
-
if (sizeof(my_host_template) != SCSI_HOST_TEMPLATE_SIZE) {
printk(KERN_ERR "usb-storage: SCSI_HOST_TEMPLATE_SIZE does not match\n") ;
printk(KERN_ERR "usb-storage: expected %d bytes, got %d bytes\n",
void __exit usb_stor_exit(void)
{
+ static struct us_data *ptr;
+
+ // FIXME: this needs to be put back to free _all_ the hosts
+ // for (ptr = us_list; ptr != NULL; ptr = ptr->next)
+ // scsi_unregister_module(MODULE_SCSI_HA, ptr->htmplt);
+ printk("MDD: us_list->htmplt is 0x%x\n", (unsigned int)(us_list->htmplt));
+ scsi_unregister_module(MODULE_SCSI_HA, us_list->htmplt);
+
usb_deregister(&storage_driver) ;
}
#define USB_STORAGE "usb-storage: "
-extern int usb_stor_debug;
-
#ifdef CONFIG_USB_STORAGE_DEBUG
void us_show_command(Scsi_Cmnd *srb);
-#define US_DEBUGP(x...) { if(usb_stor_debug) printk( KERN_DEBUG USB_STORAGE ## x ); }
-#define US_DEBUGPX(x...) { if(usb_stor_debug) printk( ## x ); }
-#define US_DEBUG(x) { if(usb_stor_debug) x; }
+#define US_DEBUGP(x...) printk( KERN_DEBUG USB_STORAGE ## x )
+#define US_DEBUGPX(x...) printk( ## x )
+#define US_DEBUG(x) x
#else
#define US_DEBUGP(x...)
#define US_DEBUGPX(x...)
#define US_BULK_RESET_SOFT 1
#define US_BULK_RESET_HARD 0
+/*
+ * us_bulk_transfer() return codes
+ */
+#define US_BULK_TRANSFER_GOOD 0
+#define US_BULK_TRANSFER_SHORT 1
+#define US_BULK_TRANSFER_FAILED 2
+
/*
* Transport return codes
*/
-#define USB_STOR_TRANSPORT_GOOD 0 /* Transport good, command good */
-#define USB_STOR_TRANSPORT_FAILED 1 /* Transport good, command failed */
-#define USB_STOR_TRANSPORT_ERROR 2 /* Transport bad (i.e. device dead */
+#define USB_STOR_TRANSPORT_GOOD 0 /* Transport good, command good */
+#define USB_STOR_TRANSPORT_FAILED 1 /* Transport good, command failed */
+#define USB_STOR_TRANSPORT_ERROR 2 /* Transport bad (i.e. device dead) */
/*
- * CBI style
+ * CBI accept device specific command
*/
#define US_CBI_ADSC 0
#define US_FL_FIXED_COMMAND 0x00000002 /* expand commands to fixed size */
#define US_FL_MODE_XLATE 0x00000004 /* translate _6 to _10 comands for
Win/MacOS compatibility */
+
#ifdef DEBUG
-
static void uhci_show_qh (puhci_desc_t qh)
{
if (qh->type != QH_TYPE) {
dbg("qh has not QH_TYPE");
return;
}
- dbg("uhci_show_qh %p (%08lX):", qh, virt_to_bus (qh));
+ dbg("QH @ %p/%08lX:", qh, virt_to_bus (qh));
if (qh->hw.qh.head & UHCI_PTR_TERM)
- dbg("Head Terminate");
- else {
- if (qh->hw.qh.head & UHCI_PTR_QH)
- dbg("Head points to QH");
- else
- dbg("Head points to TD");
-
- dbg("head: %08X", qh->hw.qh.head & ~UHCI_PTR_BITS);
- }
+ dbg(" Head Terminate");
+ else
+ dbg(" Head: %s @ %08X",
+ (qh->hw.qh.head & UHCI_PTR_QH?"QH":"TD"),
+ qh->hw.qh.head & ~UHCI_PTR_BITS);
+
if (qh->hw.qh.element & UHCI_PTR_TERM)
- dbg("Element Terminate");
- else {
-
- if (qh->hw.qh.element & UHCI_PTR_QH)
- dbg("Element points to QH");
- else
- dbg("Element points to TD");
- dbg("element: %08X", qh->hw.qh.element & ~UHCI_PTR_BITS);
- }
+ dbg(" Element Terminate");
+ else
+ dbg(" Element: %s @ %08X",
+ (qh->hw.qh.element & UHCI_PTR_QH?"QH":"TD"),
+ qh->hw.qh.element & ~UHCI_PTR_BITS);
}
#endif
static void uhci_show_td (puhci_desc_t td)
{
char *spid;
- warn("uhci_show_td %p (%08lX) ", td, virt_to_bus (td));
-
+
switch (td->hw.td.info & 0xff) {
case USB_PID_SETUP:
spid = "SETUP";
break;
}
- warn("MaxLen=%02x DT%d EndPt=%x Dev=%x, PID=%x(%s) (buf=%08x)",
+ warn(" TD @ %p/%08lX, MaxLen=%02x DT%d EP=%x Dev=%x PID=(%s) buf=%08x",
+ td, virt_to_bus (td),
td->hw.td.info >> 21,
((td->hw.td.info >> 19) & 1),
(td->hw.td.info >> 15) & 15,
(td->hw.td.info >> 8) & 127,
- (td->hw.td.info & 0xff),
spid,
td->hw.td.buffer);
- warn("Len=%02x e%d %s%s%s%s%s%s%s%s%s%s",
+ warn(" Len=%02x e%d %s%s%s%s%s%s%s%s%s%s",
td->hw.td.status & 0x7ff,
((td->hw.td.status >> 27) & 3),
(td->hw.td.status & TD_CTRL_SPD) ? "SPD " : "",
(td->hw.td.status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "",
(td->hw.td.status & TD_CTRL_BITSTUFF) ? "BitStuff " : ""
);
-#if 1
+
if (td->hw.td.link & UHCI_PTR_TERM)
- warn("Link Terminate");
- else {
- if (td->hw.td.link & UHCI_PTR_QH)
- warn("%s, link points to QH @ %08x",
- (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : " Breadth first"),
- td->hw.td.link & ~UHCI_PTR_BITS);
- else
- warn("%s, link points to TD @ %08x",
- (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : " Breadth first"),
- td->hw.td.link & ~UHCI_PTR_BITS);
- }
-#endif
+ warn(" TD Link Terminate");
+ else
+ warn(" Link points to %s @ %08x, %s",
+ (td->hw.td.link & UHCI_PTR_QH?"QH":"TD"),
+ td->hw.td.link & ~UHCI_PTR_BITS,
+ (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : "Breadth first"));
}
#ifdef DEBUG
static void uhci_show_td_queue (puhci_desc_t td)
{
- dbg("uhci_show_td_queue %p (%08lX):", td, virt_to_bus (td));
+ //dbg("uhci_show_td_queue %p (%08lX):", td, virt_to_bus (td));
while (1) {
uhci_show_td (td);
if (td->hw.td.link & UHCI_PTR_TERM)
break;
- //if(!(td->hw.td.link&UHCI_PTR_DEPTH))
- // break;
if (td != bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS))
td = bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS);
else {
dbg("td points to itself!");
break;
}
-// schedule();
}
}
static void uhci_show_queue (puhci_desc_t qh)
{
+ uhci_desc_t *start_qh=qh;
+
dbg("uhci_show_queue %p:", qh);
while (1) {
uhci_show_qh (qh);
- if (qh->hw.qh.element & UHCI_PTR_QH)
- dbg("Warning: qh->element points to qh!");
- else if (!(qh->hw.qh.element & UHCI_PTR_TERM))
+ if (!(qh->hw.qh.element & UHCI_PTR_TERM))
uhci_show_td_queue (bus_to_virt (qh->hw.qh.element & ~UHCI_PTR_BITS));
if (qh->hw.qh.head & UHCI_PTR_TERM)
dbg("qh points to itself!");
break;
}
- }
+
+ if (qh==start_qh) { // avoid loop
+ dbg("Loop detect");
+ break;
+ }
+ }
}
static void uhci_show_sc (int port, unsigned short status)
* (C) Copyright 1999 Johannes Erdfelt
* (C) Copyright 1999 Randy Dunlap
*
- * $Id: usb-uhci.c,v 1.197 2000/02/15 17:44:22 acher Exp $
+ * $Id: usb-uhci.c,v 1.222 2000/03/13 21:18:02 fliegl Exp $
*/
#include <linux/config.h>
#include <linux/unistd.h>
#include <linux/interrupt.h> /* for in_interrupt() */
#include <linux/init.h>
-/* This enables debug printks */
-#define DEBUG
-#include <linux/usb.h>
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,44)
+#include <linux/pm.h>
+#endif
#include <asm/uaccess.h>
#include <asm/io.h>
/* This enables more detailed sanity checks in submit_iso */
//#define ISO_SANITY_CHECK
+/* This enables debug printks */
+#define DEBUG
+
/* This enables all symbols to be exported, to ease debugging oopses */
//#define DEBUG_SYMBOLS
/* This enables an extra UHCI slab for memory debugging */
#define DEBUG_SLAB
+#include <linux/usb.h>
#include "usb-uhci.h"
#include "usb-uhci-debug.h"
#undef DEBUG
#undef dbg
#define dbg(format, arg...) do {} while (0)
-
-#include <linux/pm.h>
-
+#define DEBUG_SYMBOLS
#ifdef DEBUG_SYMBOLS
#define _static
#ifndef EXPORT_SYMTAB
- #define EXPORT_SYMTAB
+ #define EXPORT_SYMTAB
#endif
#else
#define _static static
#endif
+#define queue_dbg dbg //err
+#define async_dbg dbg //err
+
#ifdef DEBUG_SLAB
static kmem_cache_t *uhci_desc_kmem;
static kmem_cache_t *urb_priv_kmem;
#endif
+#define SLAB_FLAG (in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL)
+#define KMALLOC_FLAG (in_interrupt ()? GFP_ATOMIC : GFP_KERNEL)
+
+#define CONFIG_USB_UHCI_HIGH_BANDWIDTH
#define USE_CTRL_DEPTH_FIRST 0 // 0: Breadth first, 1: Depth first
#define USE_BULK_DEPTH_FIRST 0 // 0: Breadth first, 1: Depth first
-#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
-#define USE_RECLAMATION_LOOP
-#else
-//#define USE_RECLAMATION_LOOP
-#endif
-
-// stop bandwidth reclamation after (roughly) 50ms (depends also on
-// hub polling interval)
+// stop bandwidth reclamation after (roughly) 50ms
#define IDLE_TIMEOUT (HZ/20)
_static int rh_submit_urb (urb_t *urb);
_static int rh_unlink_urb (urb_t *urb);
_static int delete_qh (uhci_t *s, uhci_desc_t *qh);
+_static int process_transfer (uhci_t *s, urb_t *urb, int mode);
+_static int process_interrupt (uhci_t *s, urb_t *urb);
+_static int process_iso (uhci_t *s, urb_t *urb, int force);
static uhci_t *devs = NULL;
qh = list_entry (q, uhci_desc_t, horizontal);
if ((qh->last_used!=now) || force)
delete_qh(s,qh);
+
q=qh->horizontal.prev;
}
}
/*-------------------------------------------------------------------*/
-#ifdef USE_RECLAMATION_LOOP
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
_static void enable_desc_loop(uhci_t *s, urb_t *urb)
{
int flags;
-
- dbg("enable_desc_loop: enter");
-
+
spin_lock_irqsave (&s->qh_lock, flags);
- s->chain_end->hw.qh.head=virt_to_bus(s->control_chain)|UHCI_PTR_QH;
+ s->chain_end->hw.qh.head&=~UHCI_PTR_TERM;
+ mb();
s->loop_usage++;
((urb_priv_t*)urb->hcpriv)->use_loop=1;
spin_unlock_irqrestore (&s->qh_lock, flags);
-
- dbg("enable_desc_loop: finished");
}
/*-------------------------------------------------------------------*/
_static void disable_desc_loop(uhci_t *s, urb_t *urb)
{
int flags;
-
- dbg("disable_desc_loop: enter\n");
-
+
spin_lock_irqsave (&s->qh_lock, flags);
if (((urb_priv_t*)urb->hcpriv)->use_loop) {
s->loop_usage--;
- if (!s->loop_usage)
- s->chain_end->hw.qh.head=UHCI_PTR_TERM;
-
+ if (!s->loop_usage) {
+ s->chain_end->hw.qh.head|=UHCI_PTR_TERM;
+ mb();
+ }
((urb_priv_t*)urb->hcpriv)->use_loop=0;
}
spin_unlock_irqrestore (&s->qh_lock, flags);
-
- dbg("disable_desc_loop: finished");
-
}
#endif
/*-------------------------------------------------------------------*/
-_static void queue_urb (uhci_t *s, urb_t *urb)
+_static void queue_urb_unlocked (uhci_t *s, urb_t *urb)
{
- unsigned long flags=0;
struct list_head *p=&urb->urb_list;
-
-
- spin_lock_irqsave (&s->urb_list_lock, flags);
-
-#ifdef USE_RECLAMATION_LOOP
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
{
int type;
type=usb_pipetype (urb->pipe);
}
#endif
((urb_priv_t*)urb->hcpriv)->started=jiffies;
- list_add_tail (p, &s->urb_list);
-
- spin_unlock_irqrestore (&s->urb_list_lock, flags);
+ list_add (p, &s->urb_list);
}
+/*-------------------------------------------------------------------*/
+_static void queue_urb (uhci_t *s, urb_t *urb)
+{
+ unsigned long flags=0;
+ spin_lock_irqsave (&s->urb_list_lock, flags);
+ queue_urb_unlocked(s,urb);
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
+}
/*-------------------------------------------------------------------*/
_static void dequeue_urb (uhci_t *s, urb_t *urb)
{
-#ifdef USE_RECLAMATION_LOOP
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
int type;
type=usb_pipetype (urb->pipe);
_static int alloc_td (uhci_desc_t ** new, int flags)
{
#ifdef DEBUG_SLAB
- *new= kmem_cache_alloc(uhci_desc_kmem, in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL);
+ *new= kmem_cache_alloc(uhci_desc_kmem, SLAB_FLAG);
#else
- *new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL);
+ *new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), KMALLOC_FLAG);
#endif
if (!*new)
return -ENOMEM;
return 0;
}
/*-------------------------------------------------------------------*/
+// append a qh to td.link physically, the SW linkage is not affected
+_static void append_qh(uhci_t *s, uhci_desc_t *td, uhci_desc_t* qh, int flags)
+{
+ unsigned long xxx;
+
+ spin_lock_irqsave (&s->td_lock, xxx);
+
+ td->hw.td.link = virt_to_bus (qh) | (flags & UHCI_PTR_DEPTH) | UHCI_PTR_QH;
+
+ mb();
+ spin_unlock_irqrestore (&s->td_lock, xxx);
+}
+/*-------------------------------------------------------------------*/
/* insert td at last position in td-list of qh (vertical) */
_static int insert_td (uhci_t *s, uhci_desc_t *qh, uhci_desc_t* new, int flags)
{
if (prev->type == TD_TYPE)
prev->hw.td.link = element->hw.td.link;
else
- prev->hw.qh.element = element->hw.td.link;
+ prev->hw.qh.element = element->hw.td.link;
}
- element->hw.td.link=UHCI_PTR_TERM;
mb ();
if (dir == 0)
_static int alloc_qh (uhci_desc_t ** new)
{
#ifdef DEBUG_SLAB
- *new= kmem_cache_alloc(uhci_desc_kmem, in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL);
+ *new= kmem_cache_alloc(uhci_desc_kmem, SLAB_FLAG);
#else
- *new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL);
+ *new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), KMALLOC_FLAG);
#endif
if (!*new)
return -ENOMEM;
mb ();
spin_unlock_irqrestore (&s->qh_lock, flags);
-
+
return 0;
}
+
/*-------------------------------------------------------------------*/
_static int unlink_qh (uhci_t *s, uhci_desc_t *element)
{
delete_desc (td);
}
+
+/*-------------------------------------------------------------------*/
+_static void fill_td (uhci_desc_t *td, int status, int info, __u32 buffer)
+{
+ td->hw.td.status = status;
+ td->hw.td.info = info;
+ td->hw.td.buffer = buffer;
+}
/*-------------------------------------------------------------------*/
// Removes ALL qhs in chain (paranoia!)
_static void cleanup_skel (uhci_t *s)
qh = s->control_chain;
while ((p = qh->horizontal.next) != &qh->horizontal) {
qh1 = list_entry (p, uhci_desc_t, horizontal);
- dbg("delete_qh @ %p",qh1);
delete_qh (s, qh1);
}
- dbg("delete_qh last @ %p",qh);
+
delete_qh (s, qh);
}
else {
+ if (s->ls_control_chain)
+ delete_desc (s->ls_control_chain);
if (s->control_chain)
- kfree (s->control_chain);
+ delete_desc(s->control_chain);
if (s->bulk_chain)
- kfree (s->bulk_chain);
+ delete_desc (s->bulk_chain);
if (s->chain_end)
- kfree (s->chain_end);
+ delete_desc (s->chain_end);
}
dbg("cleanup_skel finished");
}
if (!s->iso_td)
goto init_skel_cleanup;
+ s->ls_control_chain = NULL;
s->control_chain = NULL;
s->bulk_chain = NULL;
s->chain_end = NULL;
if (ret)
goto init_skel_cleanup;
-
+
s->chain_end = qh;
+ ret = alloc_td (&td, 0);
+
+ if (ret)
+ goto init_skel_cleanup;
+
+ fill_td (td, TD_CTRL_IOC, 0, 0); // generate 1ms interrupt
+ insert_td (s, qh, td, 0);
+
dbg("allocating qh: bulk_chain");
ret = alloc_qh (&qh);
-
if (ret)
goto init_skel_cleanup;
insert_qh (s, s->chain_end, qh, 0);
s->bulk_chain = qh;
+
dbg("allocating qh: control_chain");
ret = alloc_qh (&qh);
-
if (ret)
goto init_skel_cleanup;
insert_qh (s, s->bulk_chain, qh, 0);
s->control_chain = qh;
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+ // disabled reclamation loop
+ s->chain_end->hw.qh.head=virt_to_bus(s->control_chain) | UHCI_PTR_QH | UHCI_PTR_TERM;
+#endif
+
+ dbg("allocating qh: ls_control_chain");
+ ret = alloc_qh (&qh);
+ if (ret)
+ goto init_skel_cleanup;
+
+ insert_qh (s, s->control_chain, qh, 0);
+ s->ls_control_chain = qh;
+
for (n = 0; n < 8; n++)
s->int_chain[n] = 0;
goto init_skel_cleanup;
s->int_chain[n] = td;
if (n == 0) {
- s->int_chain[0]->hw.td.link = virt_to_bus (s->control_chain) | UHCI_PTR_QH;
+ s->int_chain[0]->hw.td.link = virt_to_bus (s->ls_control_chain) | UHCI_PTR_QH;
}
else {
s->int_chain[n]->hw.td.link = virt_to_bus (s->int_chain[0]);
dbg("framelist[%i]=%x",n,s->framelist[n]);
if ((n&127)==127)
((uhci_desc_t*) s->iso_td[n])->hw.td.link = virt_to_bus(s->int_chain[0]);
- else {
- for (o = 1, m = 2; m <= 128; o++, m += m) {
- // n&(m-1) = n%m
- if ((n & (m - 1)) == ((m - 1) / 2)) {
+ else
+ for (o = 1, m = 2; m <= 128; o++, m += m)
+ if ((n & (m - 1)) == ((m - 1) / 2))
((uhci_desc_t*) s->iso_td[n])->hw.td.link = virt_to_bus (s->int_chain[o]);
- }
- }
- }
}
mb();
//uhci_show_queue(s->control_chain);
dbg("init_skel exit");
- return 0; // OK
+ return 0;
init_skel_cleanup:
cleanup_skel (s);
return -ENOMEM;
}
-/*-------------------------------------------------------------------*/
-_static void fill_td (uhci_desc_t *td, int status, int info, __u32 buffer)
-{
- td->hw.td.status = status;
- td->hw.td.info = info;
- td->hw.td.buffer = buffer;
-}
-
/*-------------------------------------------------------------------*/
// LOW LEVEL STUFF
// assembles QHs und TDs for control, bulk and iso
urb_priv_t *urb_priv = urb->hcpriv;
unsigned long destination, status;
int maxsze = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe));
- unsigned long len, bytesrequested;
+ unsigned long len;
char *data;
int depth_first=USE_CTRL_DEPTH_FIRST; // UHCI descriptor chasing method
+ if (!maxsze) {
+ err("uhci_submit_control_urb: pipesize for pipe %x is zero", urb->pipe);
+ return -EINVAL;
+ }
+
dbg("uhci_submit_control start");
alloc_qh (&qh); // alloc qh for this request
insert_td (s, qh, td, 0); // queue 'setup stage'-td in qh
#if 0
- dbg("SETUP to pipe %x: %x %x %x %x %x %x %x %x", urb->pipe,
- urb->setup_packet[0], urb->setup_packet[1], urb->setup_packet[2], urb->setup_packet[3],
- urb->setup_packet[4], urb->setup_packet[5], urb->setup_packet[6], urb->setup_packet[7]);
+ {
+ char *sp=urb->setup_packet;
+ dbg("SETUP to pipe %x: %x %x %x %x %x %x %x %x", urb->pipe,
+ sp[0],sp[1],sp[2],sp[3],sp[4],sp[5],sp[6],sp[7]);
+ }
//uhci_show_td(td);
#endif
- /* Build the DATA TD's */
len = urb->transfer_buffer_length;
- bytesrequested = len;
data = urb->transfer_buffer;
/* If direction is "send", change the frame from SETUP (0x2D)
to OUT (0xE1). Else change it from SETUP to IN (0x69). */
- destination &= ~UHCI_PID;
-
- if (usb_pipeout (urb->pipe))
- destination |= USB_PID_OUT;
- else
- destination |= USB_PID_IN;
+ destination = (urb->pipe & PIPE_DEVEP_MASK) | (usb_pipeout (urb->pipe)?USB_PID_OUT:USB_PID_IN);
while (len > 0) {
int pktsze = len;
destination &= ~UHCI_PID;
- if (usb_pipeout (urb->pipe) || (bytesrequested == 0))
+ if (usb_pipeout (urb->pipe) || (urb->transfer_buffer_length == 0))
destination |= USB_PID_IN;
else
destination |= USB_PID_OUT;
urb->status = -EINPROGRESS;
queue_urb (s, urb); // queue before inserting in desc chain
- qh->hw.qh.element&=~UHCI_PTR_TERM;
+ qh->hw.qh.element &= ~UHCI_PTR_TERM;
//uhci_show_queue(qh);
/* Start it up... put low speed first */
if (urb->pipe & TD_CTRL_LS)
- insert_qh (s, s->control_chain, qh, 1); // insert after control chain
+ insert_qh (s, s->control_chain, qh, 0);
else
- insert_qh (s, s->bulk_chain, qh, 0); // insert before bulk chain
- //uhci_show_queue(qh);
+ insert_qh (s, s->bulk_chain, qh, 0);
dbg("uhci_submit_control end");
return 0;
}
/*-------------------------------------------------------------------*/
-_static int uhci_submit_bulk_urb (urb_t *urb)
+// For queued bulk transfers, two additional QH helpers are allocated (nqh, bqh)
+// Due to the linking with other bulk urbs, it has to be locked with urb_list_lock!
+
+_static int uhci_submit_bulk_urb (urb_t *urb, urb_t *bulk_urb)
{
uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
urb_priv_t *urb_priv = urb->hcpriv;
- uhci_desc_t *qh, *td;
+ uhci_desc_t *qh, *td, *nqh, *bqh;
unsigned long destination, status;
char *data;
unsigned int pipe = urb->pipe;
int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));
int info, len;
int depth_first=USE_BULK_DEPTH_FIRST; // UHCI descriptor chasing method
-
+ urb_priv_t *upriv, *bpriv;
if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)))
return -EPIPE;
if (!maxsze)
return -EMSGSIZE;
- /* FIXME: should tell the client that the endpoint is invalid, i.e. not in the descriptor */
+
+ queue_dbg("uhci_submit_bulk_urb: urb %p, old %p, pipe %08x, len %i",
+ urb,bulk_urb,urb->pipe,urb->transfer_buffer_length);
- alloc_qh (&qh); // get qh for this request
+ upriv=(urb_priv_t*)urb->hcpriv;
- if (!qh)
- return -ENOMEM;
+ if (!bulk_urb) {
+ alloc_qh (&qh); // get qh for this request
+
+ if (!qh)
+ return -ENOMEM;
+
+ if (urb->transfer_flags & USB_QUEUE_BULK) {
+ alloc_qh(&nqh); // placeholder for clean unlink
+ if (!nqh) {
+ delete_desc (qh);
+ return -ENOMEM;
+ }
+ upriv->next_qh = nqh;
+ queue_dbg("new next qh %p",nqh);
+ }
+ }
+ else {
+ bpriv = (urb_priv_t*)bulk_urb->hcpriv;
+ qh = bpriv->bottom_qh; // re-use bottom qh and next qh
+ nqh = bpriv->next_qh;
+ upriv->next_qh=nqh;
+ bpriv->next_queued_urb=urb;
+ upriv->prev_queued_urb=bulk_urb;
+ }
+
+ queue_dbg("uhci_submit_bulk: qh=%p, nqh=%p\n",bqh,nqh);
+
+ if (urb->transfer_flags & USB_QUEUE_BULK) {
+ alloc_qh (&bqh); // "bottom" QH,
+
+ if (!bqh) {
+ if (!bulk_urb) {
+ delete_desc(qh);
+ delete_desc(nqh);
+ }
+ return -ENOMEM;
+ }
+ bqh->hw.qh.element = UHCI_PTR_TERM;
+ bqh->hw.qh.element = virt_to_bus(nqh)|UHCI_PTR_QH;
+ upriv->bottom_qh = bqh;
+ queue_dbg("uhci_submit_bulk: new bqh %p\n",bqh);
+ }
+
/* The "pipe" thing contains the destination in bits 8--18. */
destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe);
/* Build the TDs for the bulk request */
len = urb->transfer_buffer_length;
data = urb->transfer_buffer;
- dbg("uhci_submit_bulk_urb: pipe %x, len %d", pipe, len);
do { // TBD: Really allow zero-length packets?
int pktsze = len;
if (!len)
td->hw.td.status |= TD_CTRL_IOC; // last one generates INT
- //dbg("insert td %p, len %i",td,pktsze);
insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first);
-
- /* Alternate Data0/1 (start with Data0) */
usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe));
+
} while (len > 0);
list_add (&qh->desc_list, &urb_priv->desc_list);
+ if (urb->transfer_flags & USB_QUEUE_BULK) {
+ qh->hw.qh.element&=~UHCI_PTR_TERM;
+ append_qh(s, td, bqh, UHCI_PTR_DEPTH * depth_first);
+ }
+
urb->status = -EINPROGRESS;
- queue_urb (s, urb);
+ queue_urb_unlocked (s, urb);
- qh->hw.qh.element&=~UHCI_PTR_TERM;
+ qh->hw.qh.element &= ~UHCI_PTR_TERM;
- insert_qh (s, s->chain_end, qh, 0); // insert before end marker
+ if (!bulk_urb) {
+ if (urb->transfer_flags & USB_QUEUE_BULK) {
+ spin_lock (&s->td_lock); // both QHs in one go
+ insert_qh (s, s->chain_end, qh, 0); // Main QH
+ insert_qh (s, s->chain_end, nqh, 0); // Helper QH
+ spin_unlock (&s->td_lock);
+ }
+ else
+ insert_qh (s, s->chain_end, qh, 0);
+ }
+
//uhci_show_queue(s->bulk_chain);
-
- dbg("uhci_submit_bulk_urb: exit");
+ //dbg("uhci_submit_bulk_urb: exit\n");
return 0;
}
-
/*-------------------------------------------------------------------*/
-// unlinks an urb by dequeuing its qh, waits some frames and forgets it
-// Problem: unlinking in interrupt requires waiting for one frame (udelay)
-// to allow the whole structures to be safely removed
-_static int uhci_unlink_urb (urb_t *urb)
+_static void uhci_clean_iso_step1(uhci_t *s, urb_priv_t *urb_priv)
{
- uhci_t *s;
- uhci_desc_t *qh;
+ struct list_head *p;
uhci_desc_t *td;
- urb_priv_t *urb_priv;
- unsigned long flags=0;
+ for (p = urb_priv->desc_list.next; p != &urb_priv->desc_list; p = p->next) {
+ td = list_entry (p, uhci_desc_t, desc_list);
+ unlink_td (s, td, 1);
+ }
+}
+/*-------------------------------------------------------------------*/
+_static void uhci_clean_iso_step2(uhci_t *s, urb_priv_t *urb_priv)
+{
struct list_head *p;
+ uhci_desc_t *td;
- if (!urb || !urb->dev) // you never know...
- return -EINVAL;
+ while ((p = urb_priv->desc_list.next) != &urb_priv->desc_list) {
+ td = list_entry (p, uhci_desc_t, desc_list);
+ list_del (p);
+ delete_desc (td);
+ }
+}
+/*-------------------------------------------------------------------*/
+// mode: 0: unlink + no deletion mark, 1: regular (unlink/delete-mark), 2: don't unlink
+// looks a bit complicated because of all the bulk queueing goodies
- s = (uhci_t*) urb->dev->bus->hcpriv; // get pointer to uhci struct
+_static void uhci_clean_transfer (uhci_t *s, urb_t *urb, uhci_desc_t *qh, int mode)
+{
+ uhci_desc_t *bqh, *nqh, *prevqh;
+ int now;
+ urb_priv_t *priv=(urb_priv_t*)urb->hcpriv;
- if (usb_pipedevice (urb->pipe) == s->rh.devnum)
- return rh_unlink_urb (urb);
+ now=UHCI_GET_CURRENT_FRAME(s);
- if (!urb->hcpriv) // you never know...
- return -EINVAL;
+ dbg("clean transfer urb %p, qh %p, mode %i",urb,qh,mode);
+ bqh=priv->bottom_qh;
- //dbg("unlink_urb called %p",urb);
+ if (!priv->next_queued_urb) { // no more appended bulk queues
+
+ if (mode != 2)
+ unlink_qh (s, qh);
+
+ if (priv->prev_queued_urb) {
+ urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv;
+
+ ppriv->bottom_qh = priv->bottom_qh;
+ ppriv->next_queued_urb = NULL;
+ }
+ else if (bqh) { // queue dead
+ nqh=priv->next_qh;
+
+ if (mode != 2)
+ unlink_qh(s, nqh);
+
+ if (mode) {
+ nqh->last_used = bqh->last_used = now;
+ list_add_tail (&nqh->horizontal, &s->free_desc);
+ list_add_tail (&bqh->horizontal, &s->free_desc);
+ }
+ }
+ }
+ else { // there are queued urbs following
+ urb_t *nurb;
+ unsigned long flags;
+
+ nurb=priv->next_queued_urb;
+ spin_lock_irqsave (&s->qh_lock, flags);
+
+ if (!priv->prev_queued_urb) { // top
+ if (mode !=2) {
+ prevqh = list_entry (qh->horizontal.prev, uhci_desc_t, horizontal);
+ prevqh->hw.qh.head = virt_to_bus(bqh) | UHCI_PTR_QH;
+ queue_dbg ("TOP relink of %p to %p-%p",qh,prevqh,bqh);
+
+ list_del (&qh->horizontal);
+ list_add (&bqh->horizontal, &prevqh->horizontal);
+ }
+ }
+ else { //intermediate
+ urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv;
+ uhci_desc_t * bnqh;
+
+ bnqh=list_entry (&((urb_priv_t*)(nurb->hcpriv))->desc_list.next, uhci_desc_t, desc_list);
+ ppriv->bottom_qh=bnqh;
+ ppriv->next_queued_urb=nurb;
+
+ if (mode!=2) {
+ prevqh = list_entry (ppriv->desc_list.next, uhci_desc_t, desc_list);
+ prevqh->hw.qh.head = virt_to_bus(bqh) | UHCI_PTR_QH;
+ queue_dbg ("IM relink of %p to %p-%p",qh,prevqh,bqh);
+ }
+ }
+ mb();
+ spin_unlock_irqrestore (&s->qh_lock, flags);
+ ((urb_priv_t*)nurb->hcpriv)->prev_queued_urb=priv->prev_queued_urb;
+ }
+
+ if (mode) {
+ qh->last_used = now;
+ list_add_tail (&qh->horizontal, &s->free_desc); // mark for later deletion
+ }
+}
+/*-------------------------------------------------------------------*/
+// unlinks an urb by dequeuing its qh, waits some frames and forgets it
+_static int uhci_unlink_urb_sync (uhci_t *s, urb_t *urb)
+{
+ uhci_desc_t *qh;
+ urb_priv_t *urb_priv;
+ unsigned long flags=0;
spin_lock_irqsave (&s->urb_list_lock, flags);
switch (usb_pipetype (urb->pipe)) {
case PIPE_ISOCHRONOUS:
case PIPE_INTERRUPT:
- for (p = urb_priv->desc_list.next; p != &urb_priv->desc_list; p = p->next) {
- td = list_entry (p, uhci_desc_t, desc_list);
- unlink_td (s, td, 1);
- }
- // wait at least 1 Frame
- uhci_wait_ms(1);
- while ((p = urb_priv->desc_list.next) != &urb_priv->desc_list) {
- td = list_entry (p, uhci_desc_t, desc_list);
- list_del (p);
- delete_desc (td);
- }
+ uhci_clean_iso_step1(s, urb_priv);
+ uhci_wait_ms(1);
+ uhci_clean_iso_step2(s, urb_priv);
break;
case PIPE_BULK:
case PIPE_CONTROL:
qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list);
-
- unlink_qh (s, qh); // remove this qh from qh-list
- qh->last_used=UHCI_GET_CURRENT_FRAME(s);
- list_add_tail (&qh->horizontal, &s->free_desc); // mark for later deletion
- // wait at least 1 Frame
+ spin_lock_irqsave (&s->urb_list_lock, flags);
+ uhci_clean_transfer(s, urb, qh, 1);
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
uhci_wait_ms(1);
}
#ifdef DEBUG_SLAB
- kmem_cache_free(urb_priv_kmem, urb->hcpriv);
+ kmem_cache_free (urb_priv_kmem, urb->hcpriv);
#else
kfree (urb->hcpriv);
#endif
return 0;
}
/*-------------------------------------------------------------------*/
+// async unlink_urb completion/cleanup work
+// has to be protected by urb_list_lock!
+// features: if set in transfer_flags, the resulting status of the killed
+// transaction is not overwritten
+
+_static void uhci_cleanup_unlink(uhci_t *s, int force)
+{
+ struct list_head *q;
+ urb_t *urb;
+ struct usb_device *dev;
+ int pipe,now;
+ urb_priv_t *urb_priv;
+
+ q=s->urb_unlinked.next;
+ now=UHCI_GET_CURRENT_FRAME(s);
+
+ while (q != &s->urb_unlinked) {
+
+ urb = list_entry (q, urb_t, urb_list);
+
+ urb_priv = (urb_priv_t*)urb->hcpriv;
+ q = urb->urb_list.next;
+
+ if (force ||
+ ((urb_priv->started != 0xffffffff) && (urb_priv->started != now))) {
+ async_dbg("async cleanup %p",urb);
+ switch (usb_pipetype (urb->pipe)) { // process descriptors
+ case PIPE_CONTROL:
+ process_transfer (s, urb, 2);
+ break;
+ case PIPE_BULK:
+ if (!s->avoid_bulk.counter)
+ process_transfer (s, urb, 2); // don't unlink (already done)
+ else
+ continue;
+ break;
+ case PIPE_ISOCHRONOUS:
+ process_iso (s, urb, 1); // force, don't unlink
+ break;
+ case PIPE_INTERRUPT:
+ process_interrupt (s, urb);
+ break;
+ }
+
+ if (!(urb->transfer_flags & USB_TIMEOUT_KILLED))
+ urb->status = -ECONNRESET; // mark as asynchronously killed
+
+ pipe = urb->pipe; // completion may destroy all...
+ dev = urb->dev;
+ urb_priv = urb->hcpriv;
+
+ if (urb->complete) {
+ spin_unlock(&s->urb_list_lock);
+ urb->complete ((struct urb *) urb);
+ spin_lock(&s->urb_list_lock);
+ }
+
+ if (!(urb->transfer_flags & USB_TIMEOUT_KILLED))
+ urb->status = -ENOENT; // now the urb is really dead
+
+ usb_dec_dev_use (dev);
+#ifdef DEBUG_SLAB
+ kmem_cache_free (urb_priv_kmem, urb_priv);
+#else
+ kfree (urb_priv);
+#endif
+ switch (usb_pipetype (pipe)) {
+ case PIPE_ISOCHRONOUS:
+ case PIPE_INTERRUPT:
+ uhci_clean_iso_step2(s, urb_priv);
+ break;
+ }
+ list_del (&urb->urb_list);
+ }
+ }
+}
+
+/*-------------------------------------------------------------------*/
+_static int uhci_unlink_urb_async (uhci_t *s,urb_t *urb)
+{
+ uhci_desc_t *qh;
+ urb_priv_t *urb_priv;
+
+ async_dbg("unlink_urb_async called %p",urb);
+
+ if (urb->status == -EINPROGRESS) {
+ ((urb_priv_t*)urb->hcpriv)->started = ~0;
+ dequeue_urb (s, urb);
+ list_add_tail (&urb->urb_list, &s->urb_unlinked); // store urb
+
+ s->unlink_urb_done = 1;
+
+ urb->status = -ECONNABORTED; // mark urb as "waiting to be killed"
+ urb_priv = (urb_priv_t*)urb->hcpriv;
+
+ switch (usb_pipetype (urb->pipe)) {
+ case PIPE_ISOCHRONOUS:
+ case PIPE_INTERRUPT:
+ uhci_clean_iso_step1 (s, urb_priv);
+ break;
+
+ case PIPE_BULK:
+ case PIPE_CONTROL:
+ qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list);
+ uhci_clean_transfer (s, urb, qh, 0);
+ break;
+ }
+ ((urb_priv_t*)urb->hcpriv)->started = UHCI_GET_CURRENT_FRAME(s);
+ }
+
+ return -EINPROGRESS;
+}
+/*-------------------------------------------------------------------*/
+_static int uhci_unlink_urb (urb_t *urb)
+{
+ uhci_t *s;
+ unsigned long flags=0;
+ dbg("uhci_unlink_urb called for %p",urb);
+ if (!urb || !urb->dev) // you never know...
+ return -EINVAL;
+
+ s = (uhci_t*) urb->dev->bus->hcpriv;
+
+ if (usb_pipedevice (urb->pipe) == s->rh.devnum)
+ return rh_unlink_urb (urb);
+
+ if (!urb->hcpriv)
+ return -EINVAL;
+
+ if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+ int ret;
+
+ spin_lock_irqsave (&s->urb_list_lock, flags);
+ ret = uhci_unlink_urb_async(s, urb);
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
+ return ret;
+ }
+ else
+ return uhci_unlink_urb_sync(s, urb);
+}
+/*-------------------------------------------------------------------*/
// In case of ASAP iso transfer, search the URB-list for already queued URBs
// for this EP and calculate the earliest start frame for the new
// URB (easy seamless URB continuation!)
unsigned long flags;
spin_lock_irqsave (&s->urb_list_lock, flags);
- p=s->urb_list.next;
+ p=s->urb_list.prev;
- for (; p != &s->urb_list; p = p->next) {
+ for (; p != &s->urb_list; p = p->prev) {
u = list_entry (p, urb_t, urb_list);
// look for pending URBs with identical pipe handle
// works only because iso doesn't toggle the data bit!
spin_unlock_irqrestore(&s->urb_list_lock, flags);
- return ret; // no previous urb found
-
+ return ret;
}
/*-------------------------------------------------------------------*/
// adjust start_frame according to scheduling constraints (ASAP etc)
info("iso_find_start: gap in seamless isochronous scheduling");
dbg("iso_find_start: now %u start_frame %u number_of_packets %u pipe 0x%08x",
now, urb->start_frame, urb->number_of_packets, urb->pipe);
-// The following code is only for debugging purposes...
-#if 0
- {
- uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
- struct list_head *p;
- urb_t *u;
- int a = -1, b = -1;
- unsigned long flags;
-
- spin_lock_irqsave (&s->urb_list_lock, flags);
- p=s->urb_list.next;
-
- for (; p != &s->urb_list; p = p->next) {
- u = list_entry (p, urb_t, urb_list);
- if (urb->dev != u->dev)
- continue;
- dbg("urb: pipe 0x%08x status %d start_frame %u number_of_packets %u",
- u->pipe, u->status, u->start_frame, u->number_of_packets);
- if (!usb_pipeisoc (u->pipe))
- continue;
- if (a == -1)
- a = u->start_frame;
- b = (u->start_frame + u->number_of_packets - 1) & 1023;
- }
- spin_unlock_irqrestore(&s->urb_list_lock, flags);
- }
-#endif
urb->start_frame = (now + 5) & 1023; // 5ms setup should be enough //FIXME!
- //return -EAGAIN; //FIXME
}
}
}
/*-------------------------------------------------------------------*/
// submits USB interrupt (ie. polling ;-)
// ASAP-flag set implicitely
-// if period==0, the the transfer is only done once (usb_scsi need this...)
+// if period==0, the the transfer is only done once
_static int uhci_submit_int_urb (urb_t *urb)
{
int nint, n, ret;
uhci_desc_t *td;
int status, destination;
- int now;
int info;
unsigned int pipe = urb->pipe;
- //dbg("SUBMIT INT");
-
if (urb->interval < 0 || urb->interval >= 256)
return -EINVAL;
dbg("Rounded interval to %i, chain %i", urb->interval, nint);
- now = UHCI_GET_CURRENT_FRAME (s) & 1023;
- urb->start_frame = now; // remember start frame, just in case...
+ urb->start_frame = UHCI_GET_CURRENT_FRAME (s) & 1023; // remember start frame, just in case...
urb->number_of_packets = 1;
usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe));
-#if 0
- td = tdm[urb->number_of_packets];
- fill_td (td, TD_CTRL_IOC, 0, 0);
- insert_td_horizontal (s, s->iso_td[(urb->start_frame + (urb->number_of_packets) * urb->interval + 1) & 1023], td);
- list_add_tail (&td->desc_list, &urb_priv->desc_list);
-#endif
-
return 0;
}
/*-------------------------------------------------------------------*/
__save_flags(flags);
__cli(); // Disable IRQs to schedule all ISO-TDs in time
ret = iso_find_start (urb); // adjusts urb->start_frame for later use
-
+
if (ret)
goto err;
- tdm = (uhci_desc_t **) kmalloc (urb->number_of_packets * sizeof (uhci_desc_t*), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL);
+ tdm = (uhci_desc_t **) kmalloc (urb->number_of_packets * sizeof (uhci_desc_t*), KMALLOC_FLAG);
if (!tdm) {
ret = -ENOMEM;
tdm[n] = 0;
continue;
}
- #ifdef ISO_SANITY_CHECK
+
if(urb->iso_frame_desc[n].length > maxsze) {
+#ifdef ISO_SANITY_CHECK
err("submit_iso: urb->iso_frame_desc[%d].length(%d)>%d",n , urb->iso_frame_desc[n].length, maxsze);
tdm[n] = 0;
ret=-EINVAL;
goto inval;
+#endif
}
- #endif
+
ret = alloc_td (&td, UHCI_PTR_DEPTH);
inval:
if (ret) {
for (i = 0; i < n; n++)
if (tdm[i])
- kfree (tdm[i]);
+ delete_desc(tdm[i]);
kfree (tdm);
goto err;
}
}
/*-------------------------------------------------------------------*/
-_static int search_dev_ep (uhci_t *s, urb_t *urb)
+// returns: 0 (no transfer queued), urb* (this urb already queued)
+
+_static urb_t* search_dev_ep (uhci_t *s, urb_t *urb)
{
- unsigned long flags;
struct list_head *p;
urb_t *tmp;
unsigned int mask = usb_pipecontrol(urb->pipe) ? (~USB_DIR_IN) : (~0);
dbg("search_dev_ep:");
- spin_lock_irqsave (&s->urb_list_lock, flags);
+
p=s->urb_list.next;
for (; p != &s->urb_list; p = p->next) {
dbg("urb: %p", tmp);
// we can accept this urb if it is not queued at this time
// or if non-iso transfer requests should be scheduled for the same device and pipe
- if ((!usb_pipeisoc(urb->pipe) && tmp->dev == urb->dev && !((tmp->pipe ^ urb->pipe) & mask)) ||
+ if ((!usb_pipeisoc(urb->pipe) && (tmp->dev == urb->dev) && !((tmp->pipe ^ urb->pipe) & mask)) ||
(urb == tmp)) {
- spin_unlock_irqrestore (&s->urb_list_lock, flags);
- return 1; // found another urb already queued for processing
+ return tmp; // found another urb already queued for processing
}
}
- spin_unlock_irqrestore (&s->urb_list_lock, flags);
+
return 0;
}
/*-------------------------------------------------------------------*/
uhci_t *s;
urb_priv_t *urb_priv;
int ret = 0;
-
+ unsigned long flags;
+ urb_t *bulk_urb=NULL;
+
if (!urb->dev || !urb->dev->bus)
return -ENODEV;
s = (uhci_t*) urb->dev->bus->hcpriv;
//dbg("submit_urb: %p type %d",urb,usb_pipetype(urb->pipe));
-
+
+ if (!s->running)
+ return -ENODEV;
+
if (usb_pipedevice (urb->pipe) == s->rh.devnum)
return rh_submit_urb (urb); /* virtual root hub */
usb_inc_dev_use (urb->dev);
- if (search_dev_ep (s, urb)) {
- usb_dec_dev_use (urb->dev);
- return -ENXIO; // urb already queued
+ spin_lock_irqsave (&s->urb_list_lock, flags);
+
+ bulk_urb = search_dev_ep (s, urb);
+ if (bulk_urb) {
+
+ queue_dbg("found bulk urb %p\n",bulk_urb);
+
+ if ((usb_pipetype (urb->pipe) != PIPE_BULK) ||
+ ((usb_pipetype (urb->pipe) == PIPE_BULK) &&
+ (!(urb->transfer_flags & USB_QUEUE_BULK) || !(bulk_urb->transfer_flags & USB_QUEUE_BULK)))) {
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
+ usb_dec_dev_use (urb->dev);
+ err("ENXIO1 %08x, flags %x, urb %p, burb %p",urb->pipe,urb->transfer_flags,urb,bulk_urb);
+ return -ENXIO; // urb already queued
+ }
}
#ifdef DEBUG_SLAB
- urb_priv = kmem_cache_alloc(urb_priv_kmem, in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL);
+ urb_priv = kmem_cache_alloc(urb_priv_kmem, SLAB_FLAG);
#else
- urb_priv = kmalloc (sizeof (urb_priv_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL);
+ urb_priv = kmalloc (sizeof (urb_priv_t), KMALLOC_FLAG);
#endif
if (!urb_priv) {
usb_dec_dev_use (urb->dev);
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
return -ENOMEM;
}
urb->hcpriv = urb_priv;
INIT_LIST_HEAD (&urb_priv->desc_list);
- urb_priv->short_control_packet=0;
+ urb_priv->short_control_packet = 0;
dbg("submit_urb: scheduling %p", urb);
-
- switch (usb_pipetype (urb->pipe)) {
- case PIPE_ISOCHRONOUS:
- ret = uhci_submit_iso_urb (urb);
- break;
- case PIPE_INTERRUPT:
- ret = uhci_submit_int_urb (urb);
- break;
- case PIPE_CONTROL:
- //dump_urb (urb);
- ret = uhci_submit_control_urb (urb);
- break;
- case PIPE_BULK:
- ret = uhci_submit_bulk_urb (urb);
- break;
- default:
- ret = -EINVAL;
+ urb_priv->next_queued_urb = NULL;
+ urb_priv->prev_queued_urb = NULL;
+ urb_priv->bottom_qh = NULL;
+ urb_priv->next_qh = NULL;
+
+ if (usb_pipetype (urb->pipe) == PIPE_BULK) {
+
+ if (bulk_urb) {
+ while (((urb_priv_t*)bulk_urb->hcpriv)->next_queued_urb) // find last queued bulk
+ bulk_urb=((urb_priv_t*)bulk_urb->hcpriv)->next_queued_urb;
+
+ ((urb_priv_t*)bulk_urb->hcpriv)->next_queued_urb=urb;
+ }
+ atomic_inc (&s->avoid_bulk);
+ ret = uhci_submit_bulk_urb (urb, bulk_urb);
+ atomic_dec (&s->avoid_bulk);
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
+ }
+ else {
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
+ switch (usb_pipetype (urb->pipe)) {
+ case PIPE_ISOCHRONOUS:
+ ret = uhci_submit_iso_urb (urb);
+ break;
+ case PIPE_INTERRUPT:
+ ret = uhci_submit_int_urb (urb);
+ break;
+ case PIPE_CONTROL:
+ ret = uhci_submit_control_urb (urb);
+ break;
+ default:
+ ret = -EINVAL;
+ }
}
dbg("submit_urb: scheduled with ret: %d", ret);
-
if (ret != 0) {
usb_dec_dev_use (urb->dev);
#ifdef DEBUG_SLAB
return 0;
}
-#ifdef USE_RECLAMATION_LOOP
-// Removes bandwidth reclamation if URB idles too long
-void check_idling_urbs(uhci_t *s)
+
+// Checks for URB timeout and removes bandwidth reclamation
+// if URB idles too long
+_static void uhci_check_timeouts(uhci_t *s)
{
struct list_head *p,*p2;
urb_t *urb;
int type;
- //dbg("check_idling_urbs: enter i:%d",in_interrupt());
-
- spin_lock (&s->urb_list_lock);
p = s->urb_list.prev;
while (p != &s->urb_list) {
+ urb_priv_t *hcpriv;
+
p2 = p;
p = p->prev;
- urb=list_entry (p2, urb_t, urb_list);
- type=usb_pipetype (urb->pipe);
-
-#if 0
- err("URB timers: %li now: %li %i\n",
- ((urb_priv_t*)urb->hcpriv)->started +IDLE_TIMEOUT, jiffies,
- type);
-#endif
- if (((type == PIPE_BULK) || (type == PIPE_CONTROL)) &&
- (((urb_priv_t*)urb->hcpriv)->use_loop) &&
- ((((urb_priv_t*)urb->hcpriv)->started +IDLE_TIMEOUT) < jiffies))
- disable_desc_loop(s,urb);
+ urb = list_entry (p2, urb_t, urb_list);
+ type = usb_pipetype (urb->pipe);
+
+ hcpriv = (urb_priv_t*)urb->hcpriv;
+
+ if ( urb->timeout &&
+ ((hcpriv->started + urb->timeout) < jiffies)) {
+ urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK;
+ async_dbg("uhci_check_timeout: timeout for %p",urb);
+ uhci_unlink_urb_async(s, urb);
+ }
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+ else if (((type == PIPE_BULK) || (type == PIPE_CONTROL)) &&
+ (hcpriv->use_loop) &&
+ ((hcpriv->started + IDLE_TIMEOUT) < jiffies))
+ disable_desc_loop(s, urb);
+#endif
}
- spin_unlock (&s->urb_list_lock);
-
- //dbg("check_idling_urbs: finished");
}
-#endif
+
/*-------------------------------------------------------------------
Virtual Root Hub
-------------------------------------------------------------------*/
dbg("Root-Hub INT complete: port1: %x port2: %x data: %x",
inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2), data);
urb->complete (urb);
-
}
return 0;
}
urb_t *urb = (urb_t*) ptr;
uhci_t *uhci = urb->dev->bus->hcpriv;
-#ifdef USE_RECLAMATION_LOOP
- check_idling_urbs(uhci);
-#endif
-
if (uhci->rh.send) {
len = rh_send_irq (urb);
if (len > 0) {
}
/*-------------------------------------------------------------------------*/
-/* Root Hub INTs are polled by this timer */
+/* Root Hub INTs are polled by this timer, polling interval 20ms */
+/* This time is also used for URB-timeout checking */
+
_static int rh_init_int_timer (urb_t *urb)
{
uhci_t *uhci = urb->dev->bus->hcpriv;
init_timer (&uhci->rh.rh_int_timer);
uhci->rh.rh_int_timer.function = rh_int_timer_do;
uhci->rh.rh_int_timer.data = (unsigned long) urb;
- uhci->rh.rh_int_timer.expires = jiffies + (HZ * (urb->interval < 30 ? 30 : urb->interval)) / 1000;
+ uhci->rh.rh_int_timer.expires = jiffies + (HZ * 20) / 1000;
add_timer (&uhci->rh.rh_int_timer);
return 0;
stat = -EPIPE;
}
-
dbg("Root-Hub stat port1: %x port2: %x",
inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2));
p = s->urb_list.prev;
while (p != &s->urb_list) {
p2 = p;
- p = p->prev;
+ p = p->prev ;
urb = list_entry (p2, urb_t, urb_list);
- dbg("urb: %p", urb);
+ dbg("urb: %p, dev %p, %p", urb, usb_dev,urb->dev);
+
+ //urb->transfer_flags |=USB_ASYNC_UNLINK;
+
if (remove_all || (usb_dev == urb->dev)) {
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
warn("forced removing of queued URB %p due to disconnect",urb);
uhci_unlink_urb(urb);
- urb->dev = NULL; // avoid further processing of this URB
+ urb->dev = NULL; // avoid further processing of this UR
+ spin_lock_irqsave (&s->urb_list_lock, flags);
+ p = s->urb_list.prev;
}
}
spin_unlock_irqrestore (&s->urb_list_lock, flags);
{
uhci_t *s;
- dbg("uhci_free_dev");
if(!usb_dev || !usb_dev->bus || !usb_dev->bus->hcpriv)
return -EINVAL;
- s=(uhci_t*) usb_dev->bus->hcpriv;
-
+ s=(uhci_t*) usb_dev->bus->hcpriv;
uhci_unlink_urbs(s, usb_dev, 0);
return 0;
* have announced. This leads to a queue abort due to the short packet,
* the status stage is not executed. If this happens, the status stage
* is manually re-executed.
- * FIXME: Stall-condition may override 'nearly' successful CTRL-IN-transfer
- * when the transfered length fits exactly in maxsze-packets. A bit
- * more intelligence is needed to detect this and finish without error.
+ * mode: 0: QHs already unlinked
*/
-_static int process_transfer (uhci_t *s, urb_t *urb)
+_static int process_transfer (uhci_t *s, urb_t *urb, int mode)
{
int ret = 0;
urb_priv_t *urb_priv = urb->hcpriv;
uhci_desc_t *desc= list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list);
uhci_desc_t *last_desc = list_entry (desc->vertical.prev, uhci_desc_t, vertical);
int data_toggle = usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); // save initial data_toggle
-
-
- // extracted and remapped info from TD
- int maxlength;
+ int maxlength; // extracted and remapped info from TD
int actual_length;
int status = 0;
- dbg("process_transfer: urb contains bulk/control request");
-
+ //dbg("process_transfer: urb contains bulk/control request");
/* if the status phase has been retriggered and the
queue is empty or the last status-TD is inactive, the retriggered
status stage is completed
*/
-#if 1
+
if (urb_priv->short_control_packet &&
((qh->hw.qh.element == UHCI_PTR_TERM) ||(!(last_desc->hw.td.status & TD_CTRL_ACTIVE))))
goto transfer_finished;
-#endif
+
urb->actual_length=0;
for (; p != &qh->vertical; p = p->next) {
if (desc->hw.td.status & TD_CTRL_ACTIVE) // do not process active TDs
return ret;
-
- // extract transfer parameters from TD
- actual_length = (desc->hw.td.status + 1) & 0x7ff;
+
+ actual_length = (desc->hw.td.status + 1) & 0x7ff; // extract transfer parameters from TD
maxlength = (((desc->hw.td.info >> 21) & 0x7ff) + 1) & 0x7ff;
status = uhci_map_status (uhci_status_bits (desc->hw.td.status), usb_pipeout (urb->pipe));
- // see if EP is stalled
- if (status == -EPIPE) {
+ if (status == -EPIPE) { // see if EP is stalled
// set up stalled condition
usb_endpoint_halt (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
}
- // if any error occured stop processing of further TDs
- if (status != 0) {
+ if (status != 0) { // if any error occured stop processing of further TDs
// only set ret if status returned an error
- uhci_show_td (desc);
+ if (status != -EPIPE)
+ uhci_show_td (desc);
ret = status;
urb->error_count++;
break;
else if ((desc->hw.td.info & 0xff) != USB_PID_SETUP)
urb->actual_length += actual_length;
-#if 0
- // if (i++==0)
- uhci_show_td (desc); // show first TD of each transfer
-#endif
-
// got less data than requested
if ( (actual_length < maxlength)) {
if (urb->transfer_flags & USB_DISABLE_SPD) {
qh->hw.qh.element = virt_to_bus (last_desc); // re-trigger status stage
dbg("short packet during control transfer, retrigger status stage @ %p",last_desc);
- uhci_show_td (desc);
- uhci_show_td (last_desc);
+ //uhci_show_td (desc);
+ //uhci_show_td (last_desc);
urb_priv->short_control_packet=1;
return 0;
}
}
data_toggle = uhci_toggle (desc->hw.td.info);
- //dbg("process_transfer: len:%d status:%x mapped:%x toggle:%d", actual_length, desc->hw.td.status,status, data_toggle);
+ queue_dbg("process_transfer: len:%d status:%x mapped:%x toggle:%d", actual_length, desc->hw.td.status,status, data_toggle);
}
+
usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe), !data_toggle);
- transfer_finished:
- unlink_qh (s, qh);
- //delete_qh (s, qh);
- qh->last_used=UHCI_GET_CURRENT_FRAME(s);
- list_add_tail (&qh->horizontal, &s->free_desc); // mark for later deletion
+ transfer_finished:
+
+ uhci_clean_transfer(s, urb, qh, (mode==0?2:1));
urb->status = status;
-#ifdef USE_RECLAMATION_LOOP
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
disable_desc_loop(s,urb);
#endif
- dbg("process_transfer: urb %p, wanted len %d, len %d status %x err %d",
+ queue_dbg("process_transfer: (end) urb %p, wanted len %d, len %d status %x err %d",
urb,urb->transfer_buffer_length,urb->actual_length, urb->status, urb->error_count);
- //dbg("process_transfer: exit");
-#if 0
- if (urb->actual_length){
- char *uu;
- uu=urb->transfer_buffer;
- dbg("%x %x %x %x %x %x %x %x",
- *uu,*(uu+1),*(uu+2),*(uu+3),*(uu+4),*(uu+5),*(uu+6),*(uu+7));
- }
-#endif
return ret;
}
// if any error occured: ignore this td, and continue
if (status != 0) {
- uhci_show_td (desc);
+ //uhci_show_td (desc);
urb->error_count++;
goto recycle;
}
else
urb->actual_length = actual_length;
- // FIXME: SPD?
-
recycle:
if (urb->complete) {
//dbg("process_interrupt: calling completion, status %i",status);
desc->hw.td.info &= ~(1 << TD_TOKEN_TOGGLE);
if (status==0) {
+ ((urb_priv_t*)urb->hcpriv)->started=jiffies;
desc->hw.td.info |= (usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe),
usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE);
usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
return ret;
}
-
-_static int process_iso (uhci_t *s, urb_t *urb)
+// mode: 1: force processing, don't unlink tds (already unlinked)
+_static int process_iso (uhci_t *s, urb_t *urb, int mode)
{
int i;
int ret = 0;
uhci_desc_t *desc = list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list);
dbg("urb contains iso request");
- if (desc->hw.td.status & TD_CTRL_ACTIVE)
+ if ((desc->hw.td.status & TD_CTRL_ACTIVE) && !mode)
return -EXDEV; // last TD not finished
urb->error_count = 0;
urb->actual_length = 0;
urb->status = 0;
+ dbg("process iso urb %p, %li, %i, %i, %i %08x",urb,jiffies,UHCI_GET_CURRENT_FRAME(s),
+ urb->number_of_packets,mode,desc->hw.td.status);
for (i = 0; p != &urb_priv->desc_list; p = p->next, i++) {
desc = list_entry (p, uhci_desc_t, desc_list);
-
+
//uhci_show_td(desc);
if (desc->hw.td.status & TD_CTRL_ACTIVE) {
// means we have completed the last TD, but not the TDs before
goto err;
}
- unlink_td (s, desc, 1);
+ if (!mode)
+ unlink_td (s, desc, 1);
if (urb->number_of_packets <= i) {
dbg("urb->number_of_packets (%d)<=(%d)", urb->number_of_packets, i);
urb->error_count++;
urb->status = urb->iso_frame_desc[i].status;
}
- dbg("process_iso: len:%d status:%x",
- urb->iso_frame_desc[i].length, urb->iso_frame_desc[i].status);
+ dbg("process_iso: %i: len:%d %08x status:%x",
+ i, urb->iso_frame_desc[i].actual_length, desc->hw.td.status,urb->iso_frame_desc[i].status);
delete_desc (desc);
list_del (p);
}
- dbg("process_iso: exit %i (%d)", i, ret);
+
+ dbg("process_iso: exit %i (%d), actual_len %i", i, ret,urb->actual_length);
return ret;
}
urb=list_entry (p, urb_t, urb_list);
- dbg("found queued urb: %p", urb);
+ //dbg("process_urb: found queued urb: %p", urb);
switch (usb_pipetype (urb->pipe)) {
case PIPE_CONTROL:
+ ret = process_transfer (s, urb, 1);
+ break;
case PIPE_BULK:
- ret = process_transfer (s, urb);
+ if (!s->avoid_bulk.counter)
+ ret = process_transfer (s, urb, 1);
+ else
+ return 0;
break;
case PIPE_ISOCHRONOUS:
- ret = process_iso (s, urb);
+ ret = process_iso (s, urb, 0);
break;
case PIPE_INTERRUPT:
ret = process_interrupt (s, urb);
if (status != 1) {
warn("interrupt, status %x, frame# %i", status,
UHCI_GET_CURRENT_FRAME(s));
- //uhci_show_queue(s->control_chain);
+
// remove host controller halted state
if ((status&0x20) && (s->running)) {
- // more to be done - check TDs for invalid entries
- // but TDs are only invalid if somewhere else is a (memory ?) problem
outw (USBCMD_RS | inw(io_addr + USBCMD), io_addr + USBCMD);
}
//uhci_show_status (s);
}
- //beep(1000);
/*
- * the following is very subtle and was blatantly wrong before
* traverse the list in *reverse* direction, because new entries
* may be added at the end.
* also, because process_urb may unlink the current urb,
* we need to advance the list before
- * - Thomas Sailer
*/
spin_lock (&s->urb_list_lock);
p2 = p;
p = p->prev;
process_urb (s, p2);
- if(s->unlink_urb_done)
- {
+ if (s->unlink_urb_done) {
s->unlink_urb_done=0;
goto restart;
}
}
- spin_unlock (&s->urb_list_lock);
- clean_descs(s,0);
+ if ((s->frame_counter & 63) == 0)
+ uhci_check_timeouts(s);
+ clean_descs(s,0);
+ uhci_cleanup_unlink(s, 0);
+
+ spin_unlock (&s->urb_list_lock);
+
+ s->frame_counter++;
outw (status, io_addr + USBSTS);
- dbg("done");
+ //dbg("uhci_interrupt: done");
}
_static void reset_hc (uhci_t *s)
{
struct usb_device *root_hub = s->bus->root_hub;
+ s->running = 0; // Don't allow submit_urb
+
if (root_hub)
usb_disconnect (&root_hub);
- uhci_unlink_urbs(s, 0, 1); // Forced unlink of remaining URBs
+ reset_hc (s);
+ wait_ms (1);
+ uhci_unlink_urbs (s, 0, 1); // Forced unlink of remaining URBs
+ uhci_cleanup_unlink (s, 1); // force cleanup of async killed URBs
+
usb_deregister_bus (s->bus);
- s->running = 0;
- reset_hc (s);
release_region (s->io_addr, s->io_size);
free_irq (s->irq, s);
usb_free_bus (s->bus);
return 0;
}
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,44)
_static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data)
{
uhci_t *s = (uhci_t*) dev->data;
}
return 0;
}
+#endif
_static int __init alloc_uhci (struct pci_dev *dev, int irq, unsigned int io_addr, unsigned int io_size)
{
memset (s, 0, sizeof (uhci_t));
INIT_LIST_HEAD (&s->free_desc);
INIT_LIST_HEAD (&s->urb_list);
+ INIT_LIST_HEAD (&s->urb_unlinked);
spin_lock_init (&s->urb_list_lock);
spin_lock_init (&s->qh_lock);
spin_lock_init (&s->td_lock);
+ atomic_set(&s->avoid_bulk, 0);
s->irq = -1;
s->io_addr = io_addr;
s->io_size = io_size;
s->next = devs; //chain new uhci device into global list
-
+ s->frame_counter = 0;
+
bus = usb_alloc_bus (&uhci_device_operations);
if (!bus) {
kfree (s);
//chain new uhci device into global list
devs = s;
-
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,44)
pmdev = pm_register(PM_PCI_DEV, PM_PCI_ID(dev), handle_pm_event);
if (pmdev)
pmdev->data = s;
-
+#endif
return 0;
}
unsigned int io_addr = dev->resource[i].start;
unsigned int io_size =
dev->resource[i].end - dev->resource[i].start + 1;
- if (!(dev->resource[i].flags & IORESOURCE_IO))
+ if (!(dev->resource[i].flags & 1))
continue;
#else
unsigned int io_addr = dev->base_address[i];
break;
/* disable legacy emulation */
pci_write_config_word (dev, USBLEGSUP, USBLEGSUP_DEFAULT);
+ if(dev->vendor==0x8086) {
+ info("Intel USB controller: setting latency timer to %d", UHCI_LATENCY_TIMER);
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, UHCI_LATENCY_TIMER);
+ }
return alloc_uhci(dev, dev->irq, io_addr, io_size);
}
return -1;
#endif
info(VERSTR);
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+ info("High bandwidth mode enabled");
+#endif
for (;;) {
dev = pci_find_class (PCI_CLASS_SERIAL_USB << 8, dev);
if (!dev)
void cleanup_module (void)
{
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,44)
pm_unregister_all (handle_pm_event);
+#endif
uhci_cleanup ();
}
#define __LINUX_UHCI_H
/*
- $Id: usb-uhci.h,v 1.41 2000/02/13 21:37:38 acher Exp $
+ $Id: usb-uhci.h,v 1.50 2000/03/13 21:18:04 fliegl Exp $
*/
#define MODNAME "usb-uhci"
-#define VERSTR "version v1.184 time " __TIME__ " " __DATE__
+#define VERSTR "$Revision: 1.50 $ time " __TIME__ " " __DATE__
+#define UHCI_LATENCY_TIMER 0
static __inline__ void uhci_wait_ms(unsigned int ms)
{
typedef struct {
struct list_head desc_list; // list pointer to all corresponding TDs/QHs associated with this request
- int short_control_packet;
unsigned long started;
- int use_loop;
+ urb_t *next_queued_urb; // next queued urb for this EP
+ urb_t *prev_queued_urb;
+ uhci_desc_t *bottom_qh;
+ uhci_desc_t *next_qh; // next helper QH
+ char use_loop;
+ char short_control_packet;
} urb_priv_t, *purb_priv_t;
struct virt_root_hub {
spinlock_t urb_list_lock; // lock to keep consistency
int unlink_urb_done;
+ atomic_t avoid_bulk;
struct usb_bus *bus; // our bus
__u32 *framelist;
uhci_desc_t **iso_td;
uhci_desc_t *int_chain[8];
+ uhci_desc_t *ls_control_chain;
uhci_desc_t *control_chain;
uhci_desc_t *bulk_chain;
uhci_desc_t *chain_end;
spinlock_t td_lock;
struct virt_root_hub rh; //private data of the virtual root hub
int loop_usage; // URBs using bandwidth reclamation
+
+ struct list_head urb_unlinked; // list of all unlinked urbs
+ int frame_counter;
} uhci_t, *puhci_t;
/* DAC_CNTL bit constants */
#define DAC_8BIT_EN 0x00000100
#define DAC_MASK 0xFF000000
+#define DAC_BLANKING 0x00000004
+#define DAC_RANGE_CNTL 0x00000003
/* GEN_RESET_CNTL bit constants */
#define SOFT_RESET_GUI 0x00000001
#include <linux/ioport.h>
#include <asm/io.h>
-#if defined(CONFIG_PPC)
+#ifdef CONFIG_PPC
#include <asm/prom.h>
#include <asm/pci-bridge.h>
+#include <video/macmodes.h>
#ifdef CONFIG_NVRAM
#include <linux/nvram.h>
#endif
-#include <video/macmodes.h>
#endif
#ifdef CONFIG_FB_COMPAT_XPMAC
#ifdef CONFIG_MTRR
#include <asm/mtrr.h>
-#endif /* CONFIG_MTRR */
+#endif
#include "aty128.h"
u8 LoopLatency;
u8 DspOn;
u8 Rloop;
+ const char *name;
};
/* various memory configurations */
-const struct aty128_meminfo sdr_128 = { 4, 4, 3, 3, 1, 3, 1, 16, 30, 16 };
-const struct aty128_meminfo sdr_64 = { 4, 8, 3, 3, 1, 3, 1, 17, 46, 17 };
-const struct aty128_meminfo sdr_sgram = { 4, 4, 1, 2, 1, 2, 1, 16, 24, 16 };
-const struct aty128_meminfo ddr_sgram = { 4, 4, 3, 3, 2, 3, 1, 16, 31, 16 };
+const struct aty128_meminfo sdr_128 =
+ { 4, 4, 3, 3, 1, 3, 1, 16, 30, 16, "128-bit SDR SGRAM (1:1)" };
+const struct aty128_meminfo sdr_64 =
+ { 4, 8, 3, 3, 1, 3, 1, 17, 46, 17, "64-bit SDR SGRAM (1:1)" };
+const struct aty128_meminfo sdr_sgram =
+ { 4, 4, 1, 2, 1, 2, 1, 16, 24, 16, "64-bit SDR SGRAM (2:1)" };
+const struct aty128_meminfo ddr_sgram =
+ { 4, 4, 3, 3, 2, 3, 1, 16, 31, 16, "64-bit DDR SGRAM" };
static int currcon = 0;
#ifndef MODULE
static const char *mode_option __initdata = NULL;
#endif
+#ifndef CONFIG_PPC
+static void *bios_seg = NULL;
+#endif
#ifdef CONFIG_PPC
#ifdef CONFIG_NVRAM_NOT_DEFINED
+static int default_vmode __initdata = VMODE_640_480_60;
+static int default_cmode __initdata = CMODE_8;
+#else
static int default_vmode __initdata = VMODE_NVRAM;
static int default_cmode __initdata = CMODE_NVRAM;
-#else
-static int default_vmode __initdata = VMODE_CHOOSE;
-static int default_cmode __initdata = CMODE_8;
#endif
#endif
#ifdef CONFIG_MTRR
static int mtrr = 1;
-#endif /* CONFIG_MTRR */
+#endif
/* PLL constants */
struct aty128_constants {
struct fb_info_aty128 {
struct fb_info fb_info;
struct fb_info_aty128 *next;
- struct aty128_constants constants;
- unsigned long regbase_phys; /* mmio */
- unsigned long frame_buffer_phys; /* framebuffer memory */
+ struct aty128_constants constants; /* PLL and others */
+ unsigned long regbase_phys; /* physical mmio */
+ void *regbase; /* remapped mmio */
+ unsigned long frame_buffer_phys; /* physical fb memory */
unsigned long frame_buffer; /* remaped framebuffer */
- void *regbase;
const struct aty128_meminfo *mem; /* onboard mem info */
u32 vram_size; /* onboard video ram */
-#ifndef CONFIG_PPC
- void *bios_seg; /* video BIOS segment */
-#endif
- unsigned short card_revision; /* video card revision */
struct aty128fb_par default_par, current_par;
struct display disp;
struct display_switch dispsw; /* for cursor and font */
#endif
#ifdef CONFIG_MTRR
struct { int vram; int vram_valid; } mtrr;
-#endif /* CONFIG_MTRR */
+#endif
};
static struct fb_info_aty128 *board_list = NULL;
static int aty128_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
u_int transp, struct fb_info *info);
static void do_install_cmap(int con, struct fb_info *info);
+static int aty128_encode_var(struct fb_var_screeninfo *var,
+ const struct aty128fb_par *par,
+ const struct fb_info_aty128 *info);
+static int aty128_decode_var(struct fb_var_screeninfo *var,
+ struct aty128fb_par *par,
+ const struct fb_info_aty128 *info);
static int aty128_pci_register(struct pci_dev *pdev,
const struct aty128_chip_info *aci);
static struct fb_info_aty128 *aty128_board_list_add(struct fb_info_aty128
*board_list, struct fb_info_aty128 *new_node);
-#ifndef CONFIG_PPC
static int aty128find_ROM(struct fb_info_aty128 *info);
+#ifndef CONFIG_PPC
static void aty128_get_pllinfo(struct fb_info_aty128 *info);
#endif
static void aty128_timings(struct fb_info_aty128 *info);
/* write to the scratch register to test r/w functionality */
-static u32
+static int __init
register_test(const struct fb_info_aty128 *info)
{
u32 val, flag = 0;
aty_st_le32(CRTC_PITCH, crtc->pitch);
aty_st_le32(CRTC_OFFSET, crtc->offset);
aty_st_le32(CRTC_OFFSET_CNTL, crtc->offset_cntl);
+
+ /* Disable ATOMIC updating. Is this the right place? */
+ aty_st_le32(PPLL_CNTL, aty_ld_le32(PPLL_CNTL) & ~(0x00030000));
}
c_sync = sync & FB_SYNC_COMP_HIGH_ACT ? (1 << 4) : 0;
- crtc->gen_cntl = 0x03000000L | c_sync | (depth << 8);
+ crtc->gen_cntl = 0x3000000L | c_sync | (depth << 8);
crtc->h_total = h_total | (h_disp << 16);
crtc->v_total = v_total | (v_disp << 16);
break;
default:
printk(KERN_ERR "Invalid pixel width\n");
- return -1;
+ return -EINVAL;
}
return 0;
u32 pix_width;
/* fun with masking */
- h_total = crtc->h_total & 0x1ff;
- h_disp = (crtc->h_total>>16) & 0xff;
+ h_total = crtc->h_total & 0x1ff;
+ h_disp = (crtc->h_total>>16) & 0xff;
h_sync_strt = (crtc->h_sync_strt_wid>>3) & 0x1ff;
- h_sync_dly = crtc->h_sync_strt_wid & 0x7;
- h_sync_wid = (crtc->h_sync_strt_wid>>16) & 0x3f;
- h_sync_pol = (crtc->h_sync_strt_wid>>23) & 0x1;
- v_total = crtc->v_total & 0x7ff;
- v_disp = (crtc->v_total>>16) & 0x7ff;
+ h_sync_dly = crtc->h_sync_strt_wid & 0x7;
+ h_sync_wid = (crtc->h_sync_strt_wid>>16) & 0x3f;
+ h_sync_pol = (crtc->h_sync_strt_wid>>23) & 0x1;
+ v_total = crtc->v_total & 0x7ff;
+ v_disp = (crtc->v_total>>16) & 0x7ff;
v_sync_strt = crtc->v_sync_strt_wid & 0x7ff;
- v_sync_wid = (crtc->v_sync_strt_wid>>16) & 0x1f;
- v_sync_pol = (crtc->v_sync_strt_wid>>23) & 0x1;
- c_sync = crtc->gen_cntl & CRTC_CSYNC_EN ? 1 : 0;
- pix_width = crtc->gen_cntl & CRTC_PIX_WIDTH_MASK;
-
- xres = (h_disp+1)*8;
- yres = v_disp+1;
- left = (h_total-h_sync_strt-h_sync_wid)*8-h_sync_dly;
+ v_sync_wid = (crtc->v_sync_strt_wid>>16) & 0x1f;
+ v_sync_pol = (crtc->v_sync_strt_wid>>23) & 0x1;
+ c_sync = crtc->gen_cntl & CRTC_CSYNC_EN ? 1 : 0;
+ pix_width = crtc->gen_cntl & CRTC_PIX_WIDTH_MASK;
+
+ xres = (h_disp+1) << 3;
+ yres = v_disp+1;
+ left = (h_total-h_sync_strt-h_sync_wid)*8-h_sync_dly;
right = (h_sync_strt-h_disp)*8+h_sync_dly;
hslen = h_sync_wid*8;
upper = v_total-v_sync_strt-v_sync_wid;
lower = v_sync_strt-v_disp;
vslen = v_sync_wid;
- sync = (h_sync_pol ? 0 : FB_SYNC_HOR_HIGH_ACT) |
- (v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) |
- (c_sync ? FB_SYNC_COMP_HIGH_ACT : 0);
+ sync = (h_sync_pol ? 0 : FB_SYNC_HOR_HIGH_ACT) |
+ (v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) |
+ (c_sync ? FB_SYNC_COMP_HIGH_ACT : 0);
aty128_bpp_to_var(pix_width, var);
const struct fb_info_aty128 *info)
{
const struct aty128_constants c = info->constants;
- unsigned char post_dividers [] = {1,2,4,8,3,6,12};
+ unsigned char post_dividers[] = {1,2,4,8,3,6,12};
u32 output_freq;
u32 vclk; /* in .01 MHz */
int i;
n <<= (11 - p);
x = round_div(n, d);
roff = x * (fifo_depth - 4);
+
if ((ron + m->Rloop) >= roff) {
printk(KERN_ERR "aty128fb: Mode out of range!\n");
return -EINVAL;
struct fb_info_aty128 *info)
{
u32 config;
-
+#ifdef CONFIG_FB_COMPAT_XPMAC
+#if 0 /* enable this when macmodes gets updated */
+ struct vc_mode disp_info;
+#endif
+ struct fb_var_screeninfo var;
+ int cmode, vmode;
+#endif
+
info->current_par = *par;
if (info->blitter_may_be_busy)
if (par->accel_flags & FB_ACCELF_TEXT)
aty128_init_engine(par, info);
+
+#ifdef CONFIG_FB_COMPAT_XPMAC
+#if 0 /* use this when macmodes gets updated */
+ if (!console_fb_info || console_fb_info == &info->fb_info) {
+ disp_info.width = ((par->crtc.v_total >> 16) & 0x7ff)+1;
+ disp_info.height = (((par->crtc.h_total >> 16) & 0xff)+1) << 3;
+ disp_info.depth = par->crtc.bpp;
+ disp_info.pitch = par->crtc.vxres*par->crtc.bpp >> 3;
+ aty128_encode_var(&var, par, info);
+ if (mac_var_to_vmode(&var, &vmode, &cmode))
+ disp_info.mode = 0;
+ else
+ disp_info.mode = vmode;
+ strcpy(disp_info.name, aty128fb_name);
+ disp_info.fb_address = info->frame_buffer_phys;
+ disp_info.cmap_adr_address = 0;
+ disp_info.cmap_data_address = 0;
+ disp_info.disp_reg_address = info->regbase_phys;
+ register_compat_xpmac(disp_info);
+ }
+#else
+ if (!console_fb_info || console_fb_info == &info->fb_info) {
+ display_info.width = ((par->crtc.v_total >> 16) & 0x7ff)+1;
+ display_info.height = (((par->crtc.h_total >> 16) & 0xff)+1) << 3;
+ display_info.depth = par->crtc.bpp;
+ display_info.pitch = par->crtc.vxres*par->crtc.bpp >> 3;
+ aty128_encode_var(&var, par, info);
+ if (mac_var_to_vmode(&var, &vmode, &cmode))
+ display_info.mode = 0;
+ else
+ display_info.mode = vmode;
+ strcpy(display_info.name, aty128fb_name);
+ display_info.fb_address = info->frame_buffer_phys;
+ display_info.cmap_adr_address = 0;
+ display_info.cmap_data_address = 0;
+ display_info.disp_reg_address = info->regbase_phys;
+ register_compat_xpmac(display_info);
+ }
+#endif
+#endif /* CONFIG_FB_COMPAT_XPMAC */
}
var->xres_virtual = var->xres;
if (var->yres > var->yres_virtual)
var->yres_virtual = var->yres;
- if (var->bits_per_pixel <= 8)
- var->bits_per_pixel = 8;
- else if (var->bits_per_pixel <= 16)
- var->bits_per_pixel = 16;
- else if (var->bits_per_pixel <= 24)
- var->bits_per_pixel = 24;
- else if (var->bits_per_pixel <= 32)
- var->bits_per_pixel = 32;
- else
- return -EINVAL;
+
+ switch (var->bits_per_pixel) {
+ case 0 ... 8:
+ var->bits_per_pixel = 8;
+ break;
+ case 9 ... 16:
+ var->bits_per_pixel = 16;
+ break;
+ case 17 ... 24:
+ var->bits_per_pixel = 24;
+ break;
+ case 25 ... 32:
+ var->bits_per_pixel = 32;
+ break;
+ default:
+ return -EINVAL;
+ }
if ((err = aty128_decode_var(var, &par, info)))
return err;
do_install_cmap(con, &info->fb_info);
}
-#ifdef CONFIG_FB_COMPAT_XPMAC
- if (!console_fb_info || console_fb_info == &info->fb_info) {
- int vmode, cmode;
-
- display_info.width = var->xres;
- display_info.height = var->yres;
- display_info.depth = var->bits_per_pixel;
- display_info.pitch = (var->xres_virtual)*(var->bits_per_pixel)/8;
- if (mac_var_to_vmode(var, &vmode, &cmode))
- display_info.mode = 0;
- else
- display_info.mode = vmode;
- strcpy(info->fb_info.modename, aty128fb_name);
- display_info.fb_address = info->frame_buffer_phys;
- display_info.cmap_adr_address = 0;
- display_info.cmap_data_address = 0;
- display_info.disp_reg_address = info->regbase_phys;
- }
-#endif
-
return 0;
}
fix->smem_len = (u32)info->vram_size;
fix->mmio_len = 0x1fff;
- fix->type = FB_TYPE_PACKED_PIXELS;
- fix->type_aux = 0;
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->type_aux = 0;
fix->line_length = par->crtc.vxres*par->crtc.bpp/8;
- fix->visual = par->crtc.bpp <= 8 ? FB_VISUAL_PSEUDOCOLOR
- : FB_VISUAL_DIRECTCOLOR;
+ fix->visual = par->crtc.bpp <= 8 ? FB_VISUAL_PSEUDOCOLOR
+ : FB_VISUAL_DIRECTCOLOR;
fix->ywrapstep = 0;
- fix->xpanstep = 8;
- fix->ypanstep = 1;
+ fix->xpanstep = 8;
+ fix->ypanstep = 1;
fix->accel = FB_ACCEL_ATI_RAGE128;
disp = &fb_display[con];
else
disp = info->disp;
+
if (!disp->cmap.len) { /* no colormap allocated? */
int size = (disp->var.bits_per_pixel <= 8) ? 256 : 32;
if ((err = fb_alloc_cmap(&disp->cmap, size, 0)))
mtrr = 0;
}
#endif /* CONFIG_MTRR */
-#if defined(CONFIG_PPC)
+#ifdef CONFIG_PPC
/* vmode and cmode depreciated */
else if (!strncmp(this_opt, "vmode:", 6)) {
unsigned int vmode = simple_strtoul(this_opt+6, NULL, 0);
const struct aty128_chip_info *aci = &aty128_pci_probe_list[0];
char *video_card = "Rage128";
- if (!register_test(info)) {
- printk(KERN_ERR "aty128fb: Can't write to video registers\n");
- return 0;
- }
-
if (!info->vram_size) /* may have already been probed */
info->vram_size = aty_ld_le32(CONFIG_MEMSIZE) & 0x03FFFFFF;
+ /* Get the chip revision */
chip_rev = (aty_ld_le32(CONFIG_CNTL) >> 16) & 0x1F;
/* put a name with the face */
while (aci->name && info->pdev->device != aci->device) { aci++; }
video_card = (char *)aci->name;
- printk(KERN_INFO "aty128fb: %s [chip rev 0x%x] [card rev %x] ",
- video_card, chip_rev, info->card_revision);
+ printk(KERN_INFO "aty128fb: %s [chip rev 0x%x] ", video_card, chip_rev);
if (info->vram_size % (1024 * 1024) == 0)
- printk("%dM\n", info->vram_size / (1024*1024));
+ printk("%dM %s\n", info->vram_size / (1024*1024), info->mem->name);
else
- printk("%dk\n", info->vram_size / 1024);
+ printk("%dk %s\n", info->vram_size / 1024, info->mem->name);
/* fill in info */
strcpy(info->fb_info.modename, aty128fb_name);
var = default_var;
#else
memset(&var, 0, sizeof(var));
+#ifdef CONFIG_FB_COMPAT_XPMAC /* CONFIG_PPC implied */
+ if (_machine == _MACH_Pmac) {
+ if (mode_option) {
+ if (!mac_find_mode(&var, &info->fb_info, mode_option, 8))
+ var = default_var;
+ } else {
#ifdef CONFIG_NVRAM
- if (default_vmode == VMODE_NVRAM) {
- default_vmode = nvram_read_byte(NV_VMODE);
- if (default_vmode <= 0 || default_vmode > VMODE_MAX)
- default_vmode = VMODE_CHOOSE;
- }
-#endif
-#ifdef CONFIG_PPC
- if (default_vmode == VMODE_CHOOSE) {
- var = default_var;
-#endif /* CONFIG_PPC */
+ if (default_vmode == VMODE_NVRAM)
+ default_vmode = nvram_read_byte(NV_VMODE);
- if (!fb_find_mode(&var, &info->fb_info, mode_option, NULL, 0,
- &defaultmode, initdepth))
- var = default_var;
-
-#ifdef CONFIG_PPC
-#ifdef CONFIG_NVRAM
- if (default_cmode == CMODE_NVRAM)
- default_cmode = nvram_read_byte(NV_CMODE);
+ if (default_cmode == CMODE_NVRAM)
+ default_cmode = nvram_read_byte(NV_CMODE);
#endif
- } else if (_machine == _MACH_Pmac)
- if (mac_vmode_to_var(default_vmode, default_cmode, &var))
- var = default_var;
+ if (default_vmode <= 0 || default_vmode > VMODE_MAX)
+ default_vmode = VMODE_640_480_60;
-#endif
+ if (default_cmode < CMODE_8 || default_cmode > CMODE_32)
+ default_cmode = CMODE_8;
+
+ if (mac_vmode_to_var(default_vmode, default_cmode, &var))
+ var = default_var;
+ }
+ }
+#else
+ if (fb_find_mode(&var, &info->fb_info, mode_option, NULL, 0,
+ &defaultmode, initdepth) == 0)
+ var = default_var;
+#endif /* CONFIG_FB_COMPAT_XPMAC */
#endif /* MODULE */
if (noaccel)
info->palette[j].blue = default_blu[k];
}
- dac = aty_ld_le32(DAC_CNTL) & 15; /* preserve lower three bits */
- dac |= DAC_8BIT_EN; /* set 8 bit dac */
+ dac = aty_ld_le32(DAC_CNTL);
+ dac |= (DAC_8BIT_EN | DAC_RANGE_CNTL | DAC_BLANKING);
dac |= DAC_MASK; /* set DAC mask */
aty_st_le32(DAC_CNTL, dac);
}
memset(info, 0, sizeof(struct fb_info_aty128));
+ /* Copy PCI device info into info->pdev */
info->pdev = pdev;
+ /* Virtualize mmio region */
info->regbase_phys = reg_addr;
info->regbase = ioremap(reg_addr, 0x1FFF);
if (!info->regbase)
goto err_out;
- pci_read_config_word(pdev, 0x08, &tmp);
- info->card_revision = tmp;
-
info->vram_size = aty_ld_le32(CONFIG_MEMSIZE) & 0x03FFFFFF;
+ pci_read_config_word(pdev, PCI_COMMAND, &tmp);
+ if (!(tmp & PCI_COMMAND_MEMORY)) {
+ tmp |= PCI_COMMAND_MEMORY;
+ pci_write_config_word(pdev, PCI_COMMAND, tmp);
+ }
+
+ /* Virtualize the framebuffer */
info->frame_buffer_phys = fb_addr;
info->frame_buffer = (unsigned long)ioremap(fb_addr, info->vram_size);
if (!info->frame_buffer)
goto err_out;
- pci_read_config_word(pdev, PCI_COMMAND, &tmp);
- if (!(tmp & PCI_COMMAND_MEMORY)) {
- tmp |= PCI_COMMAND_MEMORY;
- pci_write_config_word(pdev, PCI_COMMAND, tmp);
+ /* If we can't test scratch registers, something is seriously wrong */
+ if (!register_test(info)) {
+ printk(KERN_ERR "aty128fb: Can't write to video register!\n");
+ goto err_out;
}
-#if defined(CONFIG_PPC)
- aty128_timings(info);
-#else
if (!aty128find_ROM(info)) {
- printk(KERN_INFO "Rage128 BIOS not located. Guessing...\n");
+ printk(KERN_INFO "aty128fb: Rage128 BIOS not located. Guessing...\n");
aty128_timings(info);
}
- else
+#ifndef CONFIG_PPC
+ else
aty128_get_pllinfo(info);
+
+ /* free up to-be unused resources. bios_seg is mapped by
+ * aty128find_ROM() and used by aty128_get_pllinfo()
+ *
+ * TODO: make more elegant. doesn't need to be global */
+ if (bios_seg)
+ iounmap(bios_seg);
#endif
#ifdef CONFIG_MTRR
if (mtrr) {
#endif /* CONFIG_PCI */
-#ifndef CONFIG_PPC
+/* PPC cannot read video ROM, so we fail by default */
static int __init
aty128find_ROM(struct fb_info_aty128 *info)
{
- u32 segstart;
+ int flag = 0;
+#ifndef CONFIG_PPC
+ u32 segstart;
char *rom_base;
- char *rom_base1;
char *rom;
- int stage;
- int i;
+ int stage;
+ int i;
char aty_rom_sig[] = "761295520"; /* ATI ROM Signature */
char R128_sig[] = "R128"; /* Rage128 ROM identifier */
- int flag = 0;
for (segstart=0x000c0000; segstart<0x000f0000; segstart+=0x00001000) {
stage = 1;
rom_base = (char *) ioremap(segstart, 0x1000);
- rom_base1 = (char *) (rom_base+1);
- if ((*rom_base == 0x55) && (((*rom_base1) & 0xff) == 0xaa))
+ if ((*rom_base == 0x55) && (((*(rom_base + 1)) & 0xff) == 0xaa))
stage = 2;
if (stage != 2) {
continue;
}
+ bios_seg = rom_base;
printk(KERN_INFO "aty128fb: Rage128 BIOS located at segment %4.4X\n",
- (u32)rom_base);
- info->bios_seg = rom_base;
+ (unsigned int)rom_base);
flag = 1;
break;
}
+#endif /* !CONFIG_PPC */
return (flag);
}
+#ifndef CONFIG_PPC
static void __init
aty128_get_pllinfo(struct fb_info_aty128 *info)
{
u16 bios_header_offset, pll_info_offset;
PLL_BLOCK pll;
- bios_header = info->bios_seg + 0x48L;
- header_ptr = bios_header;
+ bios_header = bios_seg + 0x48L;
+ header_ptr = bios_header;
bios_header_offset = readw(header_ptr);
- bios_header = info->bios_seg + bios_header_offset;
+ bios_header = bios_seg + bios_header_offset;
bios_header += 0x30;
header_ptr = bios_header;
pll_info_offset = readw(header_ptr);
- header_ptr = info->bios_seg + pll_info_offset;
+ header_ptr = bios_seg + pll_info_offset;
memcpy_fromio(&pll, header_ptr, 50);
info->mem = &sdr_sgram;
}
- /* free up to-be unused resources */
- if (info->bios_seg)
- iounmap(info->bios_seg);
-
return;
}
#endif /* ! CONFIG_PPC */
info->constants.ppll_max = 25000; /* 23000 on some cards? */
#if 1
- /* XXX TODO. Calculuate properly. Fix OF's pll ideas. */
+ /* XXX TODO. Calculuate properly. */
if (!info->constants.ref_divider)
info->constants.ref_divider = 0x3b;
aty_st_pll(PPLL_REF_DIV, info->constants.ref_divider);
};
#endif
-#if defined(MODULE)
-MODULE_AUTHOR("(c)1999-2000 Brad Douglas <brad@neruo.com>, Anthony Tong "
- "<atong@uiuc.edu>");
-MODULE_DESCRIPTION("FBDev driver for ATI Rage128 cards");
+#ifdef MODULE
+MODULE_AUTHOR("(c)1999-2000 Brad Douglas <brad@neruo.com>");
+MODULE_DESCRIPTION("FBDev driver for ATI Rage128 / Pro cards");
int __init
init_module(void)
int fbidx = GET_FB_IDX(file->f_dentry->d_inode->i_rdev);
struct fb_info *info = registered_fb[fbidx];
struct fb_ops *fb = info->fbops;
+ unsigned long off;
+#if !defined(__sparc__) || defined(__sparc_v9__)
struct fb_fix_screeninfo fix;
struct fb_var_screeninfo var;
- unsigned long start, off;
+ unsigned long start;
u32 len;
+#endif
if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
return -EINVAL;
#include <asm/uaccess.h>
#include <asm/pgalloc.h>
-#include <linux/config.h>
-
#define DLINFO_ITEMS 13
#include <linux/elf.h>
ncp_malloced = 0;
ncp_current_malloced = 0;
#endif
- return init_ncp_fs();
+ return register_filesystem(&ncp_fs_type);
}
static void __exit exit_ncp_fs(void)
-/* $Id: inode.c,v 1.8 2000/03/12 03:55:19 davem Exp $
+/* $Id: inode.c,v 1.9 2000/03/13 21:59:43 davem Exp $
* openpromfs.c: /proc/openprom handling routines
*
* Copyright (C) 1996-1999 Jakub Jelinek (jakub@redhat.com)
#ifndef _ASM_AXP_PARPORT_H
#define _ASM_AXP_PARPORT_H 1
-#include <linux/config.h>
-
-/* Maximum number of ports to support. It is useless to set this greater
- than PARPORT_MAX (in <linux/parport.h>). */
-#define PARPORT_PC_MAX_PORTS 8
-
-/* If parport_cs (PCMCIA) is managing ports for us, we'll need the
- * probing routines forever; otherwise we can lose them at boot time. */
-#ifdef CONFIG_PARPORT_PC_PCMCIA
-#define __maybe_initdata
-#define __maybe_init
-#else
-#define __maybe_initdata __initdata
-#define __maybe_init __init
-#endif
-
-static int user_specified __maybe_initdata = 0;
-int __init
-parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
+static int __devinit parport_pc_find_isa_ports (int autoirq, int autodma);
+static int __devinit parport_pc_find_nonpci_ports (int autoirq, int autodma)
{
- int count = 0, i = 0;
-
- if (io && *io) {
- /* Only probe the ports we were given. */
- user_specified = 1;
- do {
- if (!*io_hi) *io_hi = 0x400 + *io;
- if (parport_pc_probe_port(*(io++), *(io_hi++),
- *(irq++), *(dma++), NULL))
- count++;
- } while (*io && (++i < PARPORT_PC_MAX_PORTS));
- } else {
- if (parport_pc_probe_port(0x3bc, 0x7bc, irq[0], dma[0], NULL))
- count++;
- if (parport_pc_probe_port(0x378, 0x778, irq[0], dma[0], NULL))
- count++;
- if (parport_pc_probe_port(0x278, 0x678, irq[0], dma[0], NULL))
- count++;
- }
-
- return count;
+ return parport_pc_find_isa_ports (autoirq, autodma);
}
#endif /* !(_ASM_AXP_PARPORT_H) */
#ifndef __ASMARM_PARPORT_H
#define __ASMARM_PARPORT_H
-#include <linux/config.h>
-
-/* Maximum number of ports to support. It is useless to set this greater
- than PARPORT_MAX (in <linux/parport.h>). */
-#define PARPORT_PC_MAX_PORTS 8
-
-/* If parport_cs (PCMCIA) is managing ports for us, we'll need the
- * probing routines forever; otherwise we can lose them at boot time. */
-#ifdef CONFIG_PARPORT_PC_PCMCIA
-#define __maybe_initdata
-#define __maybe_init
-#else
-#define __maybe_initdata __initdata
-#define __maybe_init __init
-#endif
-
-static int user_specified __maybe_initdata = 0;
-
-
-int __init
-parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
+static int __devinit parport_pc_find_isa_ports (int autoirq, int autodma);
+static int __devinit parport_pc_find_nonpci_ports (int autoirq, int autodma)
{
- int count = 0, i = 0;
-
-#ifndef MODULE
- detect_and_report_winbond();
- detect_and_report_smsc();
-
- count += parport_pc_init_superio ();
-#endif
-
- if (io && *io) {
- /* Only probe the ports we were given. */
- user_specified = 1;
- do {
- if (!*io_hi) *io_hi = 0x400 + *io;
- if (parport_pc_probe_port(*(io++), *(io_hi++),
- *(irq++), *(dma++), NULL))
- count++;
- } while (*io && (++i < PARPORT_PC_MAX_PORTS));
- } else {
- /* Probe all the likely ports. */
- if (parport_pc_probe_port(0x3bc, 0x7bc, irq[0], dma[0], NULL))
- count++;
- if (parport_pc_probe_port(0x378, 0x778, irq[0], dma[0], NULL))
- count++;
- if (parport_pc_probe_port(0x278, 0x678, irq[0], dma[0], NULL))
- count++;
- }
-
- return count;
+ return parport_pc_find_isa_ports (autoirq, autodma);
}
#endif /* !(_ASMARM_PARPORT_H) */
#define MCL_CURRENT 1 /* lock all current mappings */
#define MCL_FUTURE 2 /* lock all future mappings */
+#define MADV_NORMAL 0x0 /* default page-in behavior */
+#define MADV_RANDOM 0x1 /* page-in minimum required */
+#define MADV_SEQUENTIAL 0x2 /* read-ahead aggressively */
+#define MADV_WILLNEED 0x3 /* pre-fault pages */
+#define MADV_DONTNEED 0x4 /* discard these pages */
+
/* compatibility flags */
#define MAP_ANON MAP_ANONYMOUS
#define MAP_FILE 0
#ifndef _ASM_I386_PARPORT_H
#define _ASM_I386_PARPORT_H 1
-/* Maximum number of ports to support. It is useless to set this greater
- than PARPORT_MAX (in <linux/parport.h>). */
-#define PARPORT_PC_MAX_PORTS 8
-
-static int user_specified __devinitdata = 0;
-int __devinit
-parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
+static int __devinit parport_pc_find_isa_ports (int autoirq, int autodma);
+static int __devinit parport_pc_find_nonpci_ports (int autoirq, int autodma)
{
- int count = 0, i = 0;
-
- if (io && *io) {
- /* Only probe the ports we were given. */
- user_specified = 1;
- do {
- if (!*io_hi) *io_hi = 0x400 + *io;
- if (parport_pc_probe_port(*(io++), *(io_hi++),
- *(irq++), *(dma++), NULL))
- count++;
- } while (*io && (++i < PARPORT_PC_MAX_PORTS));
- } else {
- if (parport_pc_probe_port(0x3bc, 0x7bc, irq[0], dma[0], NULL))
- count++;
- if (parport_pc_probe_port(0x378, 0x778, irq[0], dma[0], NULL))
- count++;
- if (parport_pc_probe_port(0x278, 0x678, irq[0], dma[0], NULL))
- count++;
- }
-
- return count;
+ return parport_pc_find_isa_ports (autoirq, autodma);
}
#endif /* !(_ASM_I386_PARPORT_H) */
#define __NR_setfsgid32 216
#define __NR_pivot_root 217
#define __NR_mincore 218
+#define __NR_madvise 219
+#define __NR_madvise1 219 /* delete when C lib stub is removed */
/* user-visible error numbers are in the range -1 - -124: see <asm-i386/errno.h> */
#ifndef _ASM_PARPORT_H
#define _ASM_PARPORT_H 1
-#include <linux/config.h>
-
-/* Maximum number of ports to support. It is useless to set this greater
- than PARPORT_MAX (in <linux/parport.h>). */
-#define PARPORT_PC_MAX_PORTS 8
-
-/* If parport_cs (PCMCIA) is managing ports for us, we'll need the
- * probing routines forever; otherwise we can lose them at boot time. */
-#ifdef CONFIG_PARPORT_PC_PCMCIA
-#define __maybe_initdata
-#define __maybe_init
-#else
-#define __maybe_initdata __initdata
-#define __maybe_init __init
-#endif
-
-static int user_specified __maybe_initdata = 0;
-int __init
-parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
+static int __devinit parport_pc_find_isa_ports (int autoirq, int autodma);
+static int __devinit parport_pc_find_nonpci_ports (int autoirq, int autodma)
{
- int count = 0, i = 0;
-
- if (io && *io) {
- /* Only probe the ports we were given. */
- user_specified = 1;
- do {
- if (!*io_hi) *io_hi = 0x400 + *io;
- if (parport_pc_probe_port(*(io++), *(io_hi++),
- *(irq++), *(dma++), NULL))
- count++;
- } while (*io && (++i < PARPORT_PC_MAX_PORTS));
- } else {
- if (parport_pc_probe_port(0x3bc, 0x7bc, irq[0], dma[0], NULL))
- count++;
- if (parport_pc_probe_port(0x378, 0x778, irq[0], dma[0], NULL))
- count++;
- if (parport_pc_probe_port(0x278, 0x678, irq[0], dma[0], NULL))
- count++;
- }
-
- return count;
+ return parport_pc_find_isa_ports (autoirq, autodma);
}
#endif /* !(_ASM_PARPORT_H) */
#ifndef _ASM_PARPORT_H
#define _ASM_PARPORT_H 1
-#include <linux/config.h>
-
-/* Maximum number of ports to support. It is useless to set this greater
- than PARPORT_MAX (in <linux/parport.h>). */
-#define PARPORT_PC_MAX_PORTS 8
-
-/* If parport_cs (PCMCIA) is managing ports for us, we'll need the
- * probing routines forever; otherwise we can lose them at boot time. */
-#ifdef CONFIG_PARPORT_PC_PCMCIA
-#define __maybe_initdata
-#define __maybe_init
-#else
-#define __maybe_initdata __initdata
-#define __maybe_init __init
-#endif
-
-static int user_specified __maybe_initdata = 0;
-int __init
-parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
+static int __devinit parport_pc_find_isa_ports (int autoirq, int autodma);
+static int __devinit parport_pc_find_nonpci_ports (int autoirq, int autodma)
{
- int count = 0, i = 0;
-
- if (io && *io) {
- /* Only probe the ports we were given. */
- user_specified = 1;
- do {
- if (!*io_hi) *io_hi = 0x400 + *io;
- if (parport_pc_probe_port(*(io++), *(io_hi++),
- *(irq++), *(dma++), NULL))
- count++;
- } while (*io && (++i < PARPORT_PC_MAX_PORTS));
- } else {
- if (parport_pc_probe_port(0x3bc, 0x7bc, irq[0], dma[0], NULL))
- count++;
- if (parport_pc_probe_port(0x378, 0x778, irq[0], dma[0], NULL))
- count++;
- if (parport_pc_probe_port(0x278, 0x678, irq[0], dma[0], NULL))
- count++;
- }
-
- return count;
+ return parport_pc_find_isa_ports (autoirq, autodma);
}
#endif /* !(_ASM_PARPORT_H) */
#include <asm/ebus.h>
#include <asm/ns87303.h>
-#ifdef CONFIG_PARPORT_PC_PCMCIA
-#define __maybe_init
-#define __maybe_initdata
-#else
-#define __maybe_init __init
-#define __maybe_initdata __initdata
-#endif
-
#define PARPORT_PC_MAX_PORTS PARPORT_MAX
static struct linux_ebus_dma *sparc_ebus_dmas[PARPORT_PC_MAX_PORTS];
return res;
}
-static int __maybe_init parport_pc_init_pci(int irq, int dma);
-
-static int user_specified __initdata = 0;
-
-int __init
-parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
+static int parport_pc_find_nonpci_ports (int autoirq, int autodma)
{
struct linux_ebus *ebus;
struct linux_ebus_device *edev;
}
}
- count += parport_pc_init_pci(PARPORT_IRQ_AUTO, PARPORT_DMA_NONE);
return count;
}
struct vm_operations_struct * vm_ops;
unsigned long vm_pgoff; /* offset in PAGE_SIZE units, *not* PAGE_CACHE_SIZE */
struct file * vm_file;
+ unsigned long vm_raend;
void * vm_private_data; /* was vm_pte (shared mem) */
};
#define VM_EXECUTABLE 0x00001000
#define VM_LOCKED 0x00002000
-#define VM_IO 0x00004000 /* Memory mapped I/O or similar */
+#define VM_IO 0x00004000 /* Memory mapped I/O or similar */
+
+#define VM_SEQ_READ 0x00008000 /* App will access data sequentially */
+#define VM_RAND_READ 0x00010000 /* App will not benefit from clustered reads */
#define VM_STACK_FLAGS 0x00000177
+#define VM_READHINTMASK (VM_SEQ_READ | VM_RAND_READ)
+#define VM_ClearReadHint(v) (v)->vm_flags &= ~VM_READHINTMASK
+#define VM_NormalReadHint(v) (!((v)->vm_flags & VM_READHINTMASK))
+#define VM_SequentialReadHint(v) ((v)->vm_flags & VM_SEQ_READ)
+#define VM_RandomReadHint(v) ((v)->vm_flags & VM_RAND_READ)
+
/*
* mapping from the currently active vm_flags protection bits (the
* low four bits) to a page protection mask..
void (*unmap)(struct vm_area_struct *area, unsigned long, size_t);
void (*protect)(struct vm_area_struct *area, unsigned long, size_t, unsigned int newprot);
int (*sync)(struct vm_area_struct *area, unsigned long, size_t, unsigned int flags);
- void (*advise)(struct vm_area_struct *area, unsigned long, size_t, unsigned int advise);
struct page * (*nopage)(struct vm_area_struct * area, unsigned long address, int write_access);
struct page * (*wppage)(struct vm_area_struct * area, unsigned long address, struct page * page);
int (*swapout)(struct page *, struct file *);
#define MAX_ORDER 10
typedef struct free_area_struct {
- struct list_head free_list;
- unsigned int * map;
+ struct list_head free_list;
+ unsigned int *map;
} free_area_t;
struct pglist_data;
/*
* Commonly accessed fields:
*/
- spinlock_t lock;
- unsigned long offset;
- unsigned long free_pages;
- char low_on_memory;
- char zone_wake_kswapd;
- unsigned long pages_min, pages_low, pages_high;
+ spinlock_t lock;
+ unsigned long offset;
+ unsigned long free_pages;
+ char low_on_memory;
+ char zone_wake_kswapd;
+ unsigned long pages_min, pages_low, pages_high;
+ struct list_head lru_cache;
/*
* free areas of different sizes
*/
- free_area_t free_area[MAX_ORDER];
+ free_area_t free_area[MAX_ORDER];
/*
* rarely used fields:
*/
- char * name;
- unsigned long size;
+ char *name;
+ unsigned long size;
/*
* Discontig memory support fields.
*/
- struct pglist_data *zone_pgdat;
- unsigned long zone_start_paddr;
- unsigned long zone_start_mapnr;
- struct page * zone_mem_map;
+ struct pglist_data *zone_pgdat;
+ unsigned long zone_start_paddr;
+ unsigned long zone_start_mapnr;
+ struct page *zone_mem_map;
} zone_t;
#define ZONE_DMA 0
FASTCALL(unsigned int nr_free_buffer_pages(void));
FASTCALL(unsigned int nr_free_highpages(void));
extern int nr_lru_pages;
-extern struct list_head lru_cache;
extern atomic_t nr_async_pages;
extern struct address_space swapper_space;
extern atomic_t page_cache_size;
#define lru_cache_add(page) \
do { \
spin_lock(&pagemap_lru_lock); \
- list_add(&(page)->lru, &lru_cache); \
+ list_add(&(page)->lru, &page->zone->lru_cache); \
nr_lru_pages++; \
spin_unlock(&pagemap_lru_lock); \
} while (0)
*/
typedef int (*usb_device_irq)(int, void *, int, void *);
-/* --------------------------------------------------------------------------*
- * New USB Structures *
- * --------------------------------------------------------------------------*/
+/* -------------------------------------------------------------------------------------*
+ * New USB Structures *
+ * -------------------------------------------------------------------------------------*/
-#define USB_DISABLE_SPD 1
-#define USB_ISO_ASAP 2
-#define USB_URB_EARLY_COMPLETE 4
-#define USB_ASYNC_UNLINK 8
-typedef struct {
+#define USB_DISABLE_SPD 0x0001
+#define USB_ISO_ASAP 0x0002
+#define USB_URB_EARLY_COMPLETE 0x0004
+#define USB_ASYNC_UNLINK 0x0008
+#define USB_QUEUE_BULK 0x0010
+#define USB_TIMEOUT_KILLED 0x1000 // only set by HCD!
+
+typedef struct
+{
unsigned int offset;
unsigned int length; // expected length
unsigned int actual_length;
int number_of_packets; // number of packets in this request (iso/irq only)
int interval; // polling interval (irq only)
int error_count; // number of errors in this transfer (iso only)
+ int timeout; // timeout (in jiffies)
//
void *context; // context for completion routine
usb_complete_t complete; // pointer to completion routine
(a)->start_frame=-1;\
} while (0)
+#define FILL_CONTROL_URB_TO(a,aa,b,c,d,e,f,g,h) \
+ do {\
+ spin_lock_init(&(a)->lock);\
+ (a)->dev=aa;\
+ (a)->pipe=b;\
+ (a)->setup_packet=c;\
+ (a)->transfer_buffer=d;\
+ (a)->transfer_buffer_length=e;\
+ (a)->complete=f;\
+ (a)->context=g;\
+ (a)->timeout=h;\
+ } while (0)
+
+#define FILL_BULK_URB_TO(a,aa,b,c,d,e,f,g) \
+ do {\
+ spin_lock_init(&(a)->lock);\
+ (a)->dev=aa;\
+ (a)->pipe=b;\
+ (a)->transfer_buffer=c;\
+ (a)->transfer_buffer_length=d;\
+ (a)->complete=e;\
+ (a)->context=f;\
+ (a)->timeout=g;\
+ } while (0)
+
purb_t usb_alloc_urb(int iso_packets);
void usb_free_urb (purb_t purb);
int usb_submit_urb(purb_t purb);
if (IS_ERR (name))
return PTR_ERR (name);
+ lock_kernel();
file = filp_open (name, O_RDWR, 0);
putname (name);
- if (IS_ERR (file))
+ if (IS_ERR (file)) {
+ unlock_kernel();
goto bad_file;
- lock_kernel();
+ }
*raddr = do_mmap (file, addr, file->f_dentry->d_inode->i_size,
(shmflg & SHM_RDONLY ? PROT_READ :
PROT_READ | PROT_WRITE), flags, 0);
* was given away by the parent in the first place.)
*/
current->counter += p->counter;
- if (current->counter > current->priority*2)
- current->counter = current->priority*2;
+ if (current->counter >= current->priority*2)
+ current->counter = current->priority*2-1;
free_task_struct(p);
} else {
printk("task releasing itself\n");
p->run_list.next = NULL;
p->run_list.prev = NULL;
- if ((clone_flags & CLONE_VFORK) || !(clone_flags & CLONE_PARENT))
- p->p_pptr = p->p_opptr = current;
+ if ((clone_flags & CLONE_VFORK) || !(clone_flags & CLONE_PARENT)) {
+ p->p_opptr = current;
+ if (!(current->flags & PF_PTRACED))
+ p->p_pptr = current;
+ }
p->p_cptr = NULL;
init_waitqueue_head(&p->wait_chldexit);
p->vfork_sem = NULL;
static spinlock_t pm_devs_lock = SPIN_LOCK_UNLOCKED;
static LIST_HEAD(pm_devs);
-/*
- * Register a device with power management
+/**
+ * pm_register - register a device with power management
+ * @type: The device type
+ * @id: Device ID
+ * @callback: Callback function
+ *
+ * Add a device to the list of devices that wish to be notified about
+ * power management events. A pm_dev structure is returnd on success,
+ * on failure the return is NULL
*/
+
struct pm_dev *pm_register(pm_dev_t type,
unsigned long id,
pm_callback callback)
return dev;
}
-/*
- * Unregister a device with power management
+/**
+ * pm_unregister - unregister a device with power management
+ * @dev: device to unregister
+ *
+ * Remove a device from the power management notification lists. The
+ * dev passed must be a handle previously returned by pm_register.
*/
+
void pm_unregister(struct pm_dev *dev)
{
if (dev) {
}
}
-/*
- * Unregister all devices with matching callback
+/**
+ * pm_unregister_all - unregister all devices with matching callback
+ * @callback: callback function pointer
+ *
+ * Unregister every device that would call the callback passed. This
+ * is primarily meant as a helper function for loadable modules. It
+ * enables a module to give up all its managed devices without keeping
+ * its own private list.
*/
+
void pm_unregister_all(pm_callback callback)
{
struct list_head *entry;
}
}
-/*
- * Send request to a single device
+/**
+ * pm_send - send request to a single device
+ * @dev: device to send to
+ * @rqst: power management request
+ * @data: data for the callback
+ *
+ * Issue a power management request to a given device. The
+ * PM_SUSPEND and PM_RESUME events are handled specially. The
+ * data field must hold the intented next state. No call is made
+ * if the state matches.
+ *
+ * BUGS: what stops two power management requests occuring in parallel
+ * and conflicting.
*/
+
int pm_send(struct pm_dev *dev, pm_request_t rqst, void *data)
{
int status = 0;
}
}
-/*
- * Send a request to all devices
+/**
+ * pm_send - send request to all managed device
+ * @rqst: power management request
+ * @data: data for the callback
+ *
+ * Issue a power management request to a all devices. The
+ * PM_SUSPEND events are handled specially. Any device is
+ * permitted to fail a suspend by returning a non zero (error)
+ * value from its callback function. If any device vetoes a
+ * suspend request then all other devices that have suspended
+ * during the processing of this request are restored to their
+ * previous state.
+ *
+ * Zero is returned on success. If a suspend fails then the status
+ * from the device that vetoes the suspend is returned.
+ *
+ * BUGS: what stops two power management requests occuring in parallel
+ * and conflicting.
*/
+
int pm_send_all(pm_request_t rqst, void *data)
{
struct list_head *entry = pm_devs.next;
return 0;
}
-/*
- * Find a device
+/**
+ * pm_find - find a device
+ * @type: type of device
+ * @from: Where to start looking
+ *
+ * Scan the power management list for devices of a specific type. The
+ * return value for a matching device may be passed to further calls
+ * to this function to find further matches. A NULL indicates the end
+ * of the list.
+ *
+ * To search from the beginning pass NULL as the from value.
*/
+
struct pm_dev *pm_find(pm_dev_t type, struct pm_dev *from)
{
struct list_head *entry = from ? from->entry.next:pm_devs.next;
#include <asm/pgalloc.h>
#include <asm/uaccess.h>
+#include <asm/mman.h>
#include <linux/highmem.h>
struct list_head * page_lru, * dispose;
struct page * page;
+ if (!zone)
+ BUG();
+
count = nr_lru_pages / (priority+1);
spin_lock(&pagemap_lru_lock);
- while (count > 0 && (page_lru = lru_cache.prev) != &lru_cache) {
+ while (count > 0 && (page_lru = zone->lru_cache.prev) != &zone->lru_cache) {
page = list_entry(page_lru, struct page, lru);
list_del(page_lru);
- dispose = &lru_cache;
+ dispose = &zone->lru_cache;
if (test_and_clear_bit(PG_referenced, &page->flags))
/* Roll the page at the top of the lru list,
* we could also be more aggressive putting
nr_lru_pages--;
out:
- list_splice(&young, &lru_cache);
- list_splice(&old, lru_cache.prev);
+ list_splice(&young, &zone->lru_cache);
+ list_splice(&old, zone->lru_cache.prev);
spin_unlock(&pagemap_lru_lock);
return retval;
}
+/*
+ * Read-ahead and flush behind for MADV_SEQUENTIAL areas. Since we are
+ * sure this is sequential access, we don't need a flexible read-ahead
+ * window size -- we can always use a large fixed size window.
+ */
+static void nopage_sequential_readahead(struct vm_area_struct * vma,
+ unsigned long pgoff, unsigned long filesize)
+{
+ unsigned long ra_window;
+
+ ra_window = get_max_readahead(vma->vm_file->f_dentry->d_inode);
+ ra_window = CLUSTER_OFFSET(ra_window + CLUSTER_PAGES - 1);
+
+ /* vm_raend is zero if we haven't read ahead in this area yet. */
+ if (vma->vm_raend == 0)
+ vma->vm_raend = vma->vm_pgoff + ra_window;
+
+ /*
+ * If we've just faulted the page half-way through our window,
+ * then schedule reads for the next window, and release the
+ * pages in the previous window.
+ */
+ if ((pgoff + (ra_window >> 1)) == vma->vm_raend) {
+ unsigned long start = vma->vm_pgoff + vma->vm_raend;
+ unsigned long end = start + ra_window;
+
+ if (end > ((vma->vm_end >> PAGE_SHIFT) + vma->vm_pgoff))
+ end = (vma->vm_end >> PAGE_SHIFT) + vma->vm_pgoff;
+ if (start > end)
+ return;
+
+ while ((start < end) && (start < filesize)) {
+ if (read_cluster_nonblocking(vma->vm_file,
+ start, filesize) < 0)
+ break;
+ start += CLUSTER_PAGES;
+ }
+ run_task_queue(&tq_disk);
+
+ /* if we're far enough past the beginning of this area,
+ recycle pages that are in the previous window. */
+ if (vma->vm_raend > (vma->vm_pgoff + ra_window + ra_window)) {
+ unsigned long window = ra_window << PAGE_SHIFT;
+
+ end = vma->vm_start + (vma->vm_raend << PAGE_SHIFT);
+ end -= window + window;
+ filemap_sync(vma, end - window, window, MS_INVALIDATE);
+ }
+
+ vma->vm_raend += ra_window;
+ }
+
+ return;
+}
+
/*
* filemap_nopage() is invoked via the vma operations vector for a
* mapped memory region to read in file data during a page fault.
goto page_not_uptodate;
success:
+ /*
+ * Try read-ahead for sequential areas.
+ */
+ if (VM_SequentialReadHint(area))
+ nopage_sequential_readahead(area, pgoff, size);
+
/*
* Found the page and have a reference on it, need to check sharing
* and possibly copy it over to another page..
page_cache_release(page);
return new_page;
}
-
+
flush_page_to_ram(old_page);
return old_page;
* Otherwise, we're off the end of a privately mapped file,
* so we need to map a zero page.
*/
- if (pgoff < size)
+ if ((pgoff < size) && !VM_RandomReadHint(area))
error = read_cluster_nonblocking(file, pgoff, size);
else
error = page_cache_read(file, pgoff);
return 0;
}
-
/*
* The msync() system call.
*/
return error;
}
+static inline void setup_read_behavior(struct vm_area_struct * vma,
+ int behavior)
+{
+ VM_ClearReadHint(vma);
+ switch(behavior) {
+ case MADV_SEQUENTIAL:
+ vma->vm_flags |= VM_SEQ_READ;
+ break;
+ case MADV_RANDOM:
+ vma->vm_flags |= VM_RAND_READ;
+ break;
+ default:
+ break;
+ }
+ return;
+}
+
+static long madvise_fixup_start(struct vm_area_struct * vma,
+ unsigned long end, int behavior)
+{
+ struct vm_area_struct * n;
+
+ n = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+ if (!n)
+ return -EAGAIN;
+ *n = *vma;
+ n->vm_end = end;
+ setup_read_behavior(n, behavior);
+ n->vm_raend = 0;
+ get_file(n->vm_file);
+ if (n->vm_ops && n->vm_ops->open)
+ n->vm_ops->open(n);
+ vmlist_modify_lock(vma->vm_mm);
+ vma->vm_pgoff += (end - vma->vm_start) >> PAGE_SHIFT;
+ vma->vm_start = end;
+ insert_vm_struct(current->mm, n);
+ vmlist_modify_unlock(vma->vm_mm);
+ return 0;
+}
+
+static long madvise_fixup_end(struct vm_area_struct * vma,
+ unsigned long start, int behavior)
+{
+ struct vm_area_struct * n;
+
+ n = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+ if (!n)
+ return -EAGAIN;
+ *n = *vma;
+ n->vm_start = start;
+ n->vm_pgoff += (n->vm_start - vma->vm_start) >> PAGE_SHIFT;
+ setup_read_behavior(n, behavior);
+ n->vm_raend = 0;
+ get_file(n->vm_file);
+ if (n->vm_ops && n->vm_ops->open)
+ n->vm_ops->open(n);
+ vmlist_modify_lock(vma->vm_mm);
+ vma->vm_end = start;
+ insert_vm_struct(current->mm, n);
+ vmlist_modify_unlock(vma->vm_mm);
+ return 0;
+}
+
+static long madvise_fixup_middle(struct vm_area_struct * vma,
+ unsigned long start, unsigned long end, int behavior)
+{
+ struct vm_area_struct * left, * right;
+
+ left = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+ if (!left)
+ return -EAGAIN;
+ right = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+ if (!right) {
+ kmem_cache_free(vm_area_cachep, left);
+ return -EAGAIN;
+ }
+ *left = *vma;
+ *right = *vma;
+ left->vm_end = start;
+ right->vm_start = end;
+ right->vm_pgoff += (right->vm_start - left->vm_start) >> PAGE_SHIFT;
+ left->vm_raend = 0;
+ right->vm_raend = 0;
+ atomic_add(2, &vma->vm_file->f_count);
+
+ if (vma->vm_ops && vma->vm_ops->open) {
+ vma->vm_ops->open(left);
+ vma->vm_ops->open(right);
+ }
+ vmlist_modify_lock(vma->vm_mm);
+ vma->vm_pgoff += (start - vma->vm_start) >> PAGE_SHIFT;
+ vma->vm_start = start;
+ vma->vm_end = end;
+ setup_read_behavior(vma, behavior);
+ vma->vm_raend = 0;
+ insert_vm_struct(current->mm, left);
+ insert_vm_struct(current->mm, right);
+ vmlist_modify_unlock(vma->vm_mm);
+ return 0;
+}
+
+/*
+ * We can potentially split a vm area into separate
+ * areas, each area with its own behavior.
+ */
+static long madvise_behavior(struct vm_area_struct * vma,
+ unsigned long start, unsigned long end, int behavior)
+{
+ int error = 0;
+
+ /* This caps the number of vma's this process can own */
+ if (vma->vm_mm->map_count > MAX_MAP_COUNT)
+ return -ENOMEM;
+
+ if (start == vma->vm_start) {
+ if (end == vma->vm_end) {
+ setup_read_behavior(vma, behavior);
+ vma->vm_raend = 0;
+ } else
+ error = madvise_fixup_start(vma, end, behavior);
+ } else {
+ if (end == vma->vm_end)
+ error = madvise_fixup_end(vma, start, behavior);
+ else
+ error = madvise_fixup_middle(vma, start, end, behavior);
+ }
+
+ return error;
+}
+
+/*
+ * Schedule all required I/O operations, then run the disk queue
+ * to make sure they are started. Do not wait for completion.
+ */
+static long madvise_willneed(struct vm_area_struct * vma,
+ unsigned long start, unsigned long end)
+{
+ long error = -EBADF;
+ struct file * file;
+ unsigned long size, rlim_rss;
+
+ /* Doesn't work if there's no mapped file. */
+ if (!vma->vm_file)
+ return error;
+ file = vma->vm_file;
+ size = (file->f_dentry->d_inode->i_size + PAGE_CACHE_SIZE - 1) >>
+ PAGE_CACHE_SHIFT;
+
+ start = ((start - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
+ if (end > vma->vm_end)
+ end = vma->vm_end;
+ end = ((end - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
+
+ /* Make sure this doesn't exceed the process's max rss. */
+ error = -EIO;
+ rlim_rss = current->rlim ? current->rlim[RLIMIT_RSS].rlim_cur :
+ LONG_MAX; /* default: see resource.h */
+ if ((vma->vm_mm->rss + (end - start)) > rlim_rss)
+ return error;
+
+ /* round to cluster boundaries if this isn't a "random" area. */
+ if (!VM_RandomReadHint(vma)) {
+ start = CLUSTER_OFFSET(start);
+ end = CLUSTER_OFFSET(end + CLUSTER_PAGES - 1);
+
+ while ((start < end) && (start < size)) {
+ error = read_cluster_nonblocking(file, start, size);
+ start += CLUSTER_PAGES;
+ if (error < 0)
+ break;
+ }
+ } else {
+ while ((start < end) && (start < size)) {
+ error = page_cache_read(file, start);
+ start++;
+ if (error < 0)
+ break;
+ }
+ }
+
+ /* Don't wait for someone else to push these requests. */
+ run_task_queue(&tq_disk);
+
+ return error;
+}
+
+/*
+ * Application no longer needs these pages. If the pages are dirty,
+ * it's OK to just throw them away. The app will be more careful about
+ * data it wants to keep. Be sure to free swap resources too. The
+ * zap_page_range call sets things up for shrink_mmap to actually free
+ * these pages later if no one else has touched them in the meantime,
+ * although we could add these pages to a global reuse list for
+ * shrink_mmap to pick up before reclaiming other pages.
+ *
+ * NB: This interface discards data rather than pushes it out to swap,
+ * as some implementations do. This has performance implications for
+ * applications like large transactional databases which want to discard
+ * pages in anonymous maps after committing to backing store the data
+ * that was kept in them. There is no reason to write this data out to
+ * the swap area if the application is discarding it.
+ *
+ * An interface that causes the system to free clean pages and flush
+ * dirty pages is already available as msync(MS_INVALIDATE).
+ */
+static long madvise_dontneed(struct vm_area_struct * vma,
+ unsigned long start, unsigned long end)
+{
+ if (vma->vm_flags & VM_LOCKED)
+ return -EINVAL;
+
+ lock_kernel(); /* is this really necessary? */
+
+ flush_cache_range(vma->vm_mm, start, end);
+ zap_page_range(vma->vm_mm, start, end - start);
+ flush_tlb_range(vma->vm_mm, start, end);
+
+ unlock_kernel();
+ return 0;
+}
+
+static long madvise_vma(struct vm_area_struct * vma, unsigned long start,
+ unsigned long end, int behavior)
+{
+ long error = -EBADF;
+
+ switch (behavior) {
+ case MADV_NORMAL:
+ case MADV_SEQUENTIAL:
+ case MADV_RANDOM:
+ error = madvise_behavior(vma, start, end, behavior);
+ break;
+
+ case MADV_WILLNEED:
+ error = madvise_willneed(vma, start, end);
+ break;
+
+ case MADV_DONTNEED:
+ error = madvise_dontneed(vma, start, end);
+ break;
+
+ default:
+ error = -EINVAL;
+ break;
+ }
+
+ return error;
+}
+
+/*
+ * The madvise(2) system call.
+ *
+ * Applications can use madvise() to advise the kernel how it should
+ * handle paging I/O in this VM area. The idea is to help the kernel
+ * use appropriate read-ahead and caching techniques. The information
+ * provided is advisory only, and can be safely disregarded by the
+ * kernel without affecting the correct operation of the application.
+ *
+ * behavior values:
+ * MADV_NORMAL - the default behavior is to read clusters. This
+ * results in some read-ahead and read-behind.
+ * MADV_RANDOM - the system should read the minimum amount of data
+ * on any access, since it is unlikely that the appli-
+ * cation will need more than what it asks for.
+ * MADV_SEQUENTIAL - pages in the given range will probably be accessed
+ * once, so they can be aggressively read ahead, and
+ * can be freed soon after they are accessed.
+ * MADV_WILLNEED - the application is notifying the system to read
+ * some pages ahead.
+ * MADV_DONTNEED - the application is finished with the given range,
+ * so the kernel can free resources associated with it.
+ *
+ * return values:
+ * zero - success
+ * -EINVAL - start + len < 0, start is not page-aligned,
+ * "behavior" is not a valid value, or application
+ * is attempting to release locked or shared pages.
+ * -ENOMEM - addresses in the specified range are not currently
+ * mapped, or are outside the AS of the process.
+ * -EIO - an I/O error occurred while paging in data.
+ * -EBADF - map exists, but area maps something that isn't a file.
+ * -EAGAIN - a kernel resource was temporarily unavailable.
+ */
+asmlinkage long sys_madvise(unsigned long start, size_t len, int behavior)
+{
+ unsigned long end;
+ struct vm_area_struct * vma;
+ int unmapped_error = 0;
+ int error = -EINVAL;
+
+ down(¤t->mm->mmap_sem);
+
+ if (start & ~PAGE_MASK)
+ goto out;
+ len = (len + ~PAGE_MASK) & PAGE_MASK;
+ end = start + len;
+ if (end < start)
+ goto out;
+
+ error = 0;
+ if (end == start)
+ goto out;
+
+ /*
+ * If the interval [start,end) covers some unmapped address
+ * ranges, just ignore them, but return -ENOMEM at the end.
+ */
+ vma = find_vma(current->mm, start);
+ for (;;) {
+ /* Still start < end. */
+ error = -ENOMEM;
+ if (!vma)
+ goto out;
+
+ /* Here start < vma->vm_end. */
+ if (start < vma->vm_start) {
+ unmapped_error = -ENOMEM;
+ start = vma->vm_start;
+ }
+
+ /* Here vma->vm_start <= start < vma->vm_end. */
+ if (end <= vma->vm_end) {
+ if (start < end) {
+ error = madvise_vma(vma, start, end,
+ behavior);
+ if (error)
+ goto out;
+ }
+ error = unmapped_error;
+ goto out;
+ }
+
+ /* Here vma->vm_start <= start < vma->vm_end < end. */
+ error = madvise_vma(vma, start, vma->vm_end, behavior);
+ if (error)
+ goto out;
+ start = vma->vm_end;
+ vma = vma->vm_next;
+ }
+
+out:
+ up(¤t->mm->mmap_sem);
+ return error;
+}
+
/*
* Later we can get more picky about what "in core" means precisely.
* For now, simply check to see if the page is in the page cache,
*n = *vma;
n->vm_end = end;
n->vm_flags = newflags;
+ n->vm_raend = 0;
if (n->vm_file)
get_file(n->vm_file);
if (n->vm_ops && n->vm_ops->open)
n->vm_start = start;
n->vm_pgoff += (n->vm_start - vma->vm_start) >> PAGE_SHIFT;
n->vm_flags = newflags;
+ n->vm_raend = 0;
if (n->vm_file)
get_file(n->vm_file);
if (n->vm_ops && n->vm_ops->open)
right->vm_start = end;
right->vm_pgoff += (right->vm_start - left->vm_start) >> PAGE_SHIFT;
vma->vm_flags = newflags;
+ left->vm_raend = 0;
+ right->vm_raend = 0;
if (vma->vm_file)
atomic_add(2, &vma->vm_file->f_count);
vma->vm_start = start;
vma->vm_end = end;
vma->vm_flags = newflags;
+ vma->vm_raend = 0;
insert_vm_struct(current->mm, left);
insert_vm_struct(current->mm, right);
vmlist_modify_unlock(vma->vm_mm);
vma->vm_flags = vm_flags(prot,flags) | mm->def_flags;
if (file) {
+ VM_ClearReadHint(vma);
+ vma->vm_raend = 0;
+
if (file->f_mode & 1)
vma->vm_flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
if (flags & MAP_SHARED) {
mpnt->vm_end = area->vm_end;
mpnt->vm_page_prot = area->vm_page_prot;
mpnt->vm_flags = area->vm_flags;
+ mpnt->vm_raend = 0;
mpnt->vm_ops = area->vm_ops;
mpnt->vm_pgoff = area->vm_pgoff + ((end - area->vm_start) >> PAGE_SHIFT);
mpnt->vm_file = area->vm_file;
*n = *vma;
n->vm_end = end;
n->vm_flags = newflags;
+ n->vm_raend = 0;
n->vm_page_prot = prot;
if (n->vm_file)
get_file(n->vm_file);
n->vm_start = start;
n->vm_pgoff += (n->vm_start - vma->vm_start) >> PAGE_SHIFT;
n->vm_flags = newflags;
+ n->vm_raend = 0;
n->vm_page_prot = prot;
if (n->vm_file)
get_file(n->vm_file);
left->vm_end = start;
right->vm_start = end;
right->vm_pgoff += (right->vm_start - left->vm_start) >> PAGE_SHIFT;
+ left->vm_raend = 0;
+ right->vm_raend = 0;
if (vma->vm_file)
atomic_add(2,&vma->vm_file->f_count);
if (vma->vm_ops && vma->vm_ops->open) {
vma->vm_start = start;
vma->vm_end = end;
vma->vm_flags = newflags;
+ vma->vm_raend = 0;
vma->vm_page_prot = prot;
insert_vm_struct(current->mm, left);
insert_vm_struct(current->mm, right);
*new_vma = *vma;
new_vma->vm_start = new_addr;
new_vma->vm_end = new_addr+new_len;
- new_vma->vm_pgoff = vma->vm_pgoff;
new_vma->vm_pgoff += (addr - vma->vm_start) >> PAGE_SHIFT;
+ new_vma->vm_raend = 0;
if (new_vma->vm_file)
get_file(new_vma->vm_file);
if (new_vma->vm_ops && new_vma->vm_ops->open)
int nr_swap_pages = 0;
int nr_lru_pages;
-LIST_HEAD(lru_cache);
pg_data_t *pgdat_list = (pg_data_t *)0;
static char *zone_names[MAX_NR_ZONES] = { "DMA", "Normal", "HighMem" };
*/
#define BAD_RANGE(zone,x) (((zone) != (x)->zone) || (((x)-mem_map) < (zone)->offset) || (((x)-mem_map) >= (zone)->offset+(zone)->size))
+static inline unsigned long classfree(zone_t *zone)
+{
+ unsigned long free = 0;
+ zone_t *z = zone->zone_pgdat->node_zones;
+
+ while (z != zone) {
+ free += z->free_pages;
+ z++;
+ }
+ free += zone->free_pages;
+ return(free);
+}
+
/*
* Buddy system. Hairy. You really aren't expected to understand this
*
memlist_add_head(&(base + page_idx)->list, &area->free_list);
spin_unlock_irqrestore(&zone->lock, flags);
+
+ if (classfree(zone) > zone->pages_high)
+ zone->zone_wake_kswapd = 0;
}
#define MARK_USED(index, order, area) \
return NULL;
}
-static inline unsigned long classfree(zone_t *zone)
-{
- unsigned long free = 0;
- zone_t *z = zone->zone_pgdat->node_zones;
-
- while (z != zone) {
- free += z->free_pages;
- z++;
- }
- free += zone->free_pages;
- return(free);
-}
-
static inline int zone_balance_memory (zone_t *zone, int gfp_mask)
{
int freed;
{
unsigned long free = classfree(z);
- if (free > z->pages_high)
- {
- if (z->low_on_memory)
- z->low_on_memory = 0;
- z->zone_wake_kswapd = 0;
- }
- else
+ if (free <= z->pages_high)
{
extern wait_queue_head_t kswapd_wait;
- if (free <= z->pages_low) {
- z->zone_wake_kswapd = 1;
- wake_up_interruptible(&kswapd_wait);
- } else
- z->zone_wake_kswapd = 0;
+ z->zone_wake_kswapd = 1;
+ wake_up_interruptible(&kswapd_wait);
if (free <= z->pages_min)
z->low_on_memory = 1;
unsigned long bitmap_size;
memlist_init(&zone->free_area[i].free_list);
+ memlist_init(&zone->lru_cache);
mask += mask;
size = (size + ~mask) & mask;
bitmap_size = size >> i;
while (pgdat) {
for (i = 0; i < MAX_NR_ZONES; i++) {
zone = pgdat->node_zones + i;
- if ((!zone->size) ||
- (!zone->zone_wake_kswapd))
+ if ((!zone->size) || (!zone->zone_wake_kswapd))
continue;
do_try_to_free_pages(GFP_KSWAPD, zone);
}
if (n < 0)
return n;
- } else
+ } else {
sk->protinfo.af_at.src_port = addr->sat_port;
- if (atalk_find_or_insert_socket(sk, addr) != NULL)
- return -EADDRINUSE;
+ if (atalk_find_or_insert_socket(sk, addr) != NULL)
+ return -EADDRINUSE;
+ }
sk->zapped = 0;
how you count) it makes sense to use a simple lookup table. */
static struct net_device *net2dev_map[256];
+#define EC_PORT_IP 0xd2
+
#ifdef CONFIG_ECONET_AUNUDP
static spinlock_t aun_queue_lock;
static struct socket *udpsock;
#define AUN_PORT 0x8000
-#define EC_PORT_IP 0xd2
struct aunhdr
{
return NULL;
}
+/*
+ * Queue a received packet for a socket.
+ */
+
+static int ec_queue_packet(struct sock *sk, struct sk_buff *skb,
+ unsigned char stn, unsigned char net,
+ unsigned char cb, unsigned char port)
+{
+ struct ec_cb *eb = (struct ec_cb *)&skb->cb;
+ struct sockaddr_ec *sec = (struct sockaddr_ec *)&eb->sec;
+
+ memset(sec, 0, sizeof(struct sockaddr_ec));
+ sec->sec_family = AF_ECONET;
+ sec->type = ECTYPE_PACKET_RECEIVED;
+ sec->port = port;
+ sec->cb = cb;
+ sec->addr.net = net;
+ sec->addr.station = stn;
+
+ return sock_queue_rcv_skb(sk, skb);
+}
+
#ifdef CONFIG_ECONET_AUNUDP
/*
set_fs(oldfs);
}
-/*
- * Queue a received packet for a socket.
- */
-
-static int ec_queue_packet(struct sock *sk, struct sk_buff *skb,
- unsigned char stn, unsigned char net,
- unsigned char cb, unsigned char port)
-{
- struct ec_cb *eb = (struct ec_cb *)&skb->cb;
- struct sockaddr_ec *sec = (struct sockaddr_ec *)&eb->sec;
-
- memset(sec, 0, sizeof(struct sockaddr_ec));
- sec->sec_family = AF_ECONET;
- sec->type = ECTYPE_PACKET_RECEIVED;
- sec->port = port;
- sec->cb = cb;
- sec->addr.net = net;
- sec->addr.station = stn;
-
- return sock_queue_rcv_skb(sk, skb);
-}
/*
* Handle incoming AUN packets. Work out if anybody wants them,
int __init econet_proto_init(struct net_proto *pro)
{
extern void econet_sysctl_register(void);
- spin_lock_init(&aun_queue_lock);
sock_register(&econet_family_ops);
#ifdef CONFIG_ECONET_AUNUDP
+ spin_lock_init(&aun_queue_lock);
aun_udp_initialise();
#endif
#ifdef CONFIG_ECONET_NATIVE
*
* PACKET - implements raw packet sockets.
*
- * Version: $Id: af_packet.c,v 1.32 2000/02/21 16:25:55 davem Exp $
+ * Version: $Id: af_packet.c,v 1.33 2000/03/13 22:11:50 davem Exp $
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
case SIOCGIFBR:
case SIOCSIFBR:
#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+#ifdef CONFIG_INET
#ifdef CONFIG_KMOD
if (br_ioctl_hook == NULL)
request_module("bridge");
if (br_ioctl_hook != NULL)
return br_ioctl_hook(arg);
#endif
+#endif
return -ENOPKG;
#!/bin/sh
-X=`$TOPDIR/scripts/gen-all-syms "$1"`
+X=`$TOPDIR/scripts/gen-all-syms "$*"`
$TOPDIR/scripts/docproc $X