S: USA
N: Stephen Tweedie
-E: sct@dcs.ed.ac.uk
+E: sct@redhat.com
P: 1024/E7A417AD E2 FE A4 20 34 EC ED FC 7D 7E 67 8D E0 31 D1 69
+P: 1024D/43BE7544 D2A4 8556 08E6 90E7 076C BA3F 243F 20A4 43BE 7544
D: Second extended file system developer
D: General filesystem hacker
D: kswap vm management code
-S: Dept. of Computer Science
-S: University of Edinburgh
-S: JCMB, The King's Buildings
-S: Mayfield Road
-S: Edinburgh
-S: EH9 3JZ
+S: 44 Campbell Park Crescent
+S: Edinburgh EH13 0HT
S: United Kingdom
N: Thomas Uhl
- info on the Digiboard PC/X{i,e,eve} multiport boards.
digiepca.txt
- info on Digi Intl. {PC,PCI,EISA}Xx and Xem series cards.
+dnotify.txt
+ - info about directory notification in Linux.
exception.txt
- how Linux v2.2 handles exceptions without verify_area etc.
fb/
--- /dev/null
+ Linux Directory Notification
+ ============================
+
+ Stephen Rothwell <sfr@linuxcare.com.au>
+
+The intention of directory notification is to allow user applications
+to be notified when a directory, or any of the files in it, are changed.
+The basic mechanism involves the application registering for notification
+on a directory using a fcntl(2) call and the notifications themselves
+being delivered using signals.
+
+The application decides which "events" it wants to be notified about.
+The currently defined events are:
+
+ DN_ACCESS A file in the directory was accessed (read)
+ DN_MODIFY A file in the directory was modified (write,truncate)
+ DN_CREATE A file was created in the directory
+ DN_DELETE A file was unlinked from directory
+ DN_RENAME A file in the directory was renamed
+ DN_ATTRIB A file in the directory had its attributes
+ changed (chmod,chown)
+
+Usually, the application must reregister after each notification, but
+if DN_MULTISHOT is or'ed with the event mask, then the registration will
+remain until explicitly removed (by registering for no events).
+
+By default, SIGIO will be delivered to the process and no other useful
+information. However, if the F_SETSIG fcntl(2) call is used to let the
+kernel know which signal to deliver, a siginfo structure will be passed to
+the signal handler and the si_fd member of that structure will contain the
+file descriptor associated with the direcory in which the event occured.
+
+Preferably the application will choose one of the real time signals
+(SIGRTMIN + <n>) so that the notifications may be queued. This is
+especially important if DN_MULTISHOT is specified.
+
+Implementation expectations (features and bugs :-))
+---------------------------
+
+The notification should work for any local access to files even if the
+actual file system is on a remote server. This implies that remote
+access to files served by local user mode servers should be notified.
+Also, remote accesses to files served by a local kernel NFS server should
+be notified.
+
+In order to make the impact on the file system code as small as possible,
+the problem of hard links to files has been ignored. So if a file (x)
+exists in two directories (a and b) then a change to the file using the
+name "a/x" should be notified to a program expecting notifications on
+directory "a", but will not be notified to one expecting notifications on
+directory "b".
+
+Also, files that are unlinked, will still cause notifications in the
+last directory that they were linked to.
+
+Example
+-------
+
+ #define _GNU_SOURCE /* needed to get the defines */
+ #include <fcntl.h> /* in glibc 2.2 this has the needed
+ values defined */
+ #include <signal.h>
+ #include <stdio.h>
+ #include <unistd.h>
+
+ static volatile int event_fd;
+
+ static void handler(int sig, siginfo_t *si, void *data)
+ {
+ event_fd = si->si_fd;
+ }
+
+ int main(void)
+ {
+ struct sigaction act;
+ int fd;
+
+ act.sa_sigaction = handler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_SIGINFO;
+ sigaction(SIGRTMIN, &act, NULL);
+
+ fd = open(".", O_RDONLY);
+ fcntl(fd, F_SETSIG, SIGRTMIN);
+ fcntl(fd, F_NOTIFY, DN_MODIFY|DN_CREATE|DN_MULTISHOT);
+ /* we will now be notified if any of the files
+ in "." is modified or new files are created */
+ while (1) {
+ pause();
+ printf("Got event on fd=%d\n", event_fd);
+ }
+ }
--- /dev/null
+USB HOTPLUGGING
+
+In hotpluggable busses like USB (and Cardbus PCI), end-users plug devices
+into the bus with power on. In most cases, users expect the devices to become
+immediately usable. That means the system must do many things, including:
+
+ - Find a driver that can handle the device. That may involve
+ loading a kernel module; newer drivers can use modutils to
+ publish their device (and class) support to user utilities.
+
+ - Bind a driver to that device. That's done using the USB
+ device driver's probe() routine.
+
+ - Tell other subsystems to configure the new device. Print
+ queues may need to be enabled, networks brought up, disk
+ partitions mounted, and so on. In some cases these will
+ be driver-specific actions.
+
+This involves a mix of kernel mode and user mode actions. Making devices
+be immediately usable means that any user mode actions can't wait for an
+administrator to do them: the kernel must trigger them, either passively
+(triggering some monitoring daemon to invoke a helper program) or
+actively (calling such a user mode helper program directly).
+
+Those triggered actions must support a system's administrative policies;
+such programs are called "policy agents" here. Typically they involve
+shell scripts that dispatch to more familiar administration tools.
+
+
+KERNEL HOTPLUG HELPER (/sbin/hotplug)
+
+When you compile with CONFIG_HOTPLUG, you get a new kernel parameter:
+/proc/sys/kernel/hotplug, which normally holds the pathname "/sbin/hotplug".
+That parameter names a program which the kernel may invoke at various times.
+
+The /sbin/hotplug program can be invoked by any subsystem as part of its
+reaction to a configuration change, from a thread in that subsystem.
+Only one parameter is required: the name of a subsystem being notified of
+some kernel event. That name is used as the first key for further event
+dispatch; any other argument and environment parameters are specified by
+the subsystem making that invocation.
+
+A reference implementation of a /sbin/hotplug script is available at the
+http://www.linux-usb.org website, which works USB for but also knows how to
+delegate to any /etc/hotplug/$TYPE.agent policy agent present.
+
+
+USB POLICY AGENT
+
+The USB subsystem currently invokes /sbin/hotplug when USB devices
+are added or removed from system. The invocation is done by the kernel
+hub daemon thread [khubd], or else as part of root hub initialization
+(done by init, modprobe, kapmd, etc). Its single command line parameter
+is the string "usb", and it passes these environment variables:
+
+ ACTION ... "add", "remove"
+ PRODUCT ... USB vendor, product, and version codes (hex)
+ TYPE ... device class codes (decimal)
+ INTERFACE ... interface 0 class codes (decimal)
+
+If "usbdevfs" is configured, DEVICE and DEVFS are also passed. DEVICE is
+the pathname of the device, and is useful for devices with multiple and/or
+alternate interfaces that complicate driver selection.
+
+Currently available policy agent implementations can load drivers for
+modules, and can invoke driver-specific setup scripts. The newest ones
+leverage USB modutils support. Later agents might unload drivers.
+
+
+USB MODUTILS SUPPORT
+
+Current versions of modutils will create a "modules.usbmap" file which
+contains the entries from each driver's MODULE_DEVICE_TABLE. Such files
+can be used by various user mode policy agents to make sure all the right
+driver modules get loaded, either at boot time or later.
+
+See <linux/usb.h> for full information about such table entries; or look
+at existing drivers. Each table entry describes one or more criteria to
+be used when matching a driver to a device or class of devices.
+
+A short example, for a driver that supports several specific USB devices
+and their quirks, might have a MODULE_DEVICE_TABLE like this:
+
+ static const struct usb_device_id mydriver_id_table = {
+ { idVendor: 0x9999, idProduct 0xaaaa, driver_info: QUIRK_X },
+ { idVendor: 0xbbbb, idProduct 0x8888, driver_info: QUIRK_Y|QUIRK_Z },
+ ...
+ { } /* end with an all-zeroes entry */
+ }
+ MODULE_DEVICE_TABLE (usb, mydriver_id_table);
+
+Most USB device drivers should pass these tables to the USB subsystem as
+well as to the module management subsystem. Not all, though: some driver
+frameworks connect using interfaces layered over USB, and so they won't
+need such a "struct usb_driver".
+
+Drivers that connect directly to the USB subsystem should be declared
+something like this:
+
+ static struct usb_driver mydriver = {
+ name: "mydriver",
+ id_table: mydriver_id_table,
+ probe: my_probe,
+ disconnect: my_disconnect,
+
+ /*
+ if using the usb chardev framework:
+ minor: MY_USB_MINOR_START,
+ fops: my_file_ops,
+ if exposing any operations through usbdevfs:
+ ioctl: my_ioctl,
+ */
+ }
+
+When the USB subsystem knows about a driver's device ID table, it's used when
+choosing drivers to probe(). The thread doing new device processing checks
+drivers' device ID entries from the MODULE_DEVICE_TABLE against interface and
+device descriptors for the device. It will only call probe() if there is a
+match, and the third argument to probe() will be the entry that matched.
+
+If you don't provide an id_table for your driver, then your driver may get
+probed for each new device; the third parameter to probe() will be null.
+
+
Author: Mark McClelland
Homepage: http://alpha.dyndns.org/ov511
-NEW IN THIS VERSION:
- o Stability improvements
- o Support for hue control
- o 160x120 mostly working
- o OV6620 color problems fixed
- o More WebCam 3 detection improvements
-
INTRODUCTION:
This is a driver for the OV511, a USB-only chip used in many "webcam" devices.
Any camera using the OV511/OV511+ and the OV7610/20/20AE CCD should work. It
supports streaming and capture of color or monochrome video via the Video4Linux
-API. Most V4L apps are compatible with it, but a few videoconferencing programs
+API. Most V4L apps are compatible with it, but a few video-conferencing programs
do not work yet. The following resolutions are supported: 640x480, 448x336,
384x288, 352x288, and 320x240.
+If you need more information, please visit the OV511 homepage at the above URL.
+
WHAT YOU NEED:
- If you want to help with the development, get the chip's specification docs at
you get a scrambled image it is likely that you made a mistake in Xawtv.ad.
Try setting the size to 320x240 if all else fails.
-FAQ:
-Q: "Why does the picture have noise and look grainy"
-A: This is a problem at low light levels, and may be also due to subtle bugs in
- the code. The cause is most likely the OV7610 settings we are currently
- using. I am looking into this problem.
-
-Q: "The driver sometimes says `Failed to read OV7610 ID.' What is the deal?"
-A: The I2C code that allows the OV511 to communicate with the camera chip is a
- bit flaky right now. This message means that the I2C bus never got
- initialized properly, and the camera will most likely not work even if you
- disable this warning. Try unloading/reloading the driver or unplugging/re-
- plugging the camera if this happens. Also try increasing the i2c_detect_tries
- parameter (see below).
-
-Q: "Why do you bother with this phony camera detection crap? It doesn't do
- anything useful!"
-A: The main purpose of only supporting known camera models is to force people
- with new camera models to tell me about them, so I can assemble the list
- above, and so the code can know what CCD chip you have. Right now, nearly all
- of the cameras use the OV7610 and consequently I have not put support for
- other ones in, so the value of the detection code is questionable. Eventually
- though, new CCDs might appear and we will be fortunate to have the detection.
-
MODULE PARAMETERS:
You can set these with: insmod ov511 NAME=VALUE
or so lines higher than the red component. This is only apparent in
images with white objects on black backgrounds at 640x480. Setting this
to 1 will realign the color planes correctly. NOTE: This is still
- experimental and very buggy. You will likely need a fast (500 Mhz) CPU.
+ experimental and very buggy. You will likely need a fast (500 MHz) CPU.
NAME: snapshot
TYPE: integer (boolean)
DESC: Prevent apps from timing out if frame is not done in time. This is
useful if you are having problems with Xawtv getting "stuck" on a frame
when your system is under heavy load.
-
+
+ NAME: sensor_gbr
+ TYPE: boolean
+ DEFAULT: 0
+ DESC: This makes the sensor output GBR422 instead of YUV420. This saves the
+ driver the trouble of converting YUV to RGB, but it currently does not
+ work very well (the colors are not quite right)
+
WORKING FEATURES:
- o Color streaming/capture at 640x480, 448x336, 384x288, 352x288, 320x240, and
- 160x120
- o RGB24, YUV420, YUV422, YUYV, and YUV422P color
+ o Color streaming/capture at 640x480, 448x336, 384x288, 352x288, and 320x240
+ o RGB24, RGB565, YUV420, YUV422, YUYV, and YUV422P color
o Monochrome
o Setting/getting of saturation, contrast, brightness, and hue (only some of
them work the OV7620 and OV7620AE)
- o proc status reporting
+ o /proc status reporting
EXPERIMENTAL FEATURES:
o fix_rgb_offset: Sometimes works, but other times causes errors with xawtv and
o Snapshot mode (only works with some read() based apps; see below for more)
o OV6620 sensor support
o GBR422 parsing
+ o 160x120
TODO:
o Fix the noise / grainy image problem.
so we can't really work on that yet. Please kindly inform OmniVision that you
would like them to release their specifications to the Linux community.
o YUV422
- o Get snapshot mode working with mmap().
o Fix fixFrameRGBoffset(). It is not stable yet with streaming video.
- o Get autoadjust disable working
o V4L2 support (Probably not until it goes into the kernel)
- o Creative WebCam III has problems initializing its sensor. This should be
- fixed now, but if you still have problems let me know.
o Get rid of the memory management functions (put them in videodev.c??)
o Setting of contrast and brightness not working with 7620/7620AE
o Driver/camera state save/restore for when USB supports suspend/resume
o Unstable on SMP systems
o OV7620/OV6620 experience frame corruption with moving objects
o OV6620 is too dark
+ o 176x144 support
+ o Driver sometimes hangs upon close() with OHCI
+ o The image should always be written properly to the mmap'ed buffer as long as
+ the requested image size is at least the minimum size. This will likely
+ require a rewrite of all the parsing code.
HOW TO CONTACT ME:
card=46 - Zoltrix Genie TV
card=47 - Terratec TV/Radio+
card=48 - Dynalink Magic TView
+ card=49 - GV-BCTV3
+ card=50 - Prolink PV-BT878P+4E (PixelView PlayTV PAK)
+ card=51 - Eagle Wireless Capricorn2 (bt878A)
+ card=52 - Pinnacle Studio PCTV Pro
+ card=53 - Typhoon TView RDS
tuner.o
type=0 - Temic PAL
type=12 - Alps TSBE5
type=13 - Alps TSBC5
type=14 - Temic 4006FH5
+ type=15 - Alps TSCH6
at the hardware). default is 1.
bttv_debug=0/1 debug messages (for capture).
default is 0 (off).
+ irq_debug=0/1 irq handler debug messages.
+ default is 0 (off).
gbuffers=2-64 number of capture buffers for mmap'ed capture.
default is 2.
gbufsize= size of capture buffers. default and
maximum value is 0x208000 (~2MB)
+ bttv_gpio=0/1
+ gpiomask=
+ audioall=
+ audiomux=
+ See Sound-FAQ for a detailed description.
+
remap, card, radio and pll accept up to four comma-separated arguments
(for multiple boards).
new, experimental module which is supported to provide a single
driver for all simple i2c audio control chips (tda/tea*).
+ insmod args:
+ tda8425 = 1 enable/disable the support for the
+ tda9840 = 1 various chips.
+ tda9850 = 1 The tea6300 can't be autodetected and is
+ tda9855 = 1 therefore off by default, if you have
+ tda9873 = 1 this one on your card (STB uses these)
+ tea6300 = 0 you have to enable it explicitly.
+ tea6420 = 1 The two tda985x chips use the same i2c
+ pic16c54 = 1 address and can't be disturgished from
+ each other, you might have to disable
+ the wrong one.
+ debug = 1 print debug messages
+
msp3400.o
The driver for the msp34xx sound processor chips. If you have a
stereo card, you probably want to insmod this one.
should improve things for french people, the
carrier autoscan seems to work with FM only...
-tea6300.o
+tea6300.o - OBSOLETE (use tvaudio instead)
The driver for the tea6300 fader chip. If you have a stereo
card and the msp3400.o doesn't work, you might want to try this
one. This chip is seen on most STB TV/FM cards (usually from
insmod args:
debug=1 print some debug info to the syslog.
-tda8425.o
+tda8425.o - OBSOLETE (use tvaudio instead)
The driver for the tda8425 fader chip. This driver used to be
part of bttv.c, so if your sound used to work but does not
anymore, try loading this module.
insmod args:
debug=1 print some debug info to the syslog.
-tda985x.o
+tda985x.o - OBSOLETE (use tvaudio instead)
The driver for the tda9850/55 audio chips.
insmod args:
+IMPORTANT: Don't send me mails with images attached unless I ask you
+to do so. Mails with images attached will go to /dev/null unseen.
+
+
Release notes for bttv-0.7.x
============================
CONFIG_I2C_ALGOBIT=m
The latest bttv version is available here:
- http://me.in-berlin.de/~kraxel/bttv.html
+ http://www.strusel007.de/linux/bttv/
You'll find Ralphs original (mostly outdated) documentation in the
ralphs-doc subdirectory.
not, go to /usr/src/linux and run at least "make config". Even
better, compile your own kernel, you'll never become a real hacker
else ;-)
+Note that you have to turn on video4linux support (CONFIG_VIDEO_DEV)
+in the kernel to get the videodev.o module which is required by bttv.
Make bttv work with your card
specified the wrong (or no) card type. A list of supported cards is
in CARDLIST.
-If your card isn't listed in CARDLIST, you should read the Sound-FAQ.
+If your card isn't listed in CARDLIST or if you have trouble making
+audio work, you should read the Sound-FAQ.
Still doesn't work?
sound is handled in slightly different ways on each board.
To handle the grabber boards correctly, there is a array tvcards[] in
-bttv.c, which holds the informations required for each board. Sound
-will work only, if the correct entry is used (for video it often makes
-no difference). The bttv driver prints a line to the kernel log,
-telling which card type is used. Like this one:
+bttv-cards.c, which holds the informations required for each board.
+Sound will work only, if the correct entry is used (for video it often
+makes no difference). The bttv driver prints a line to the kernel
+log, telling which card type is used. Like this one:
- bttv0: model: BT848(Hauppauge old)
+ bttv0: model: BT848(Hauppauge old) [autodetected]
You should verify this is correct. If it is'nt, you have to pass the
correct board type as insmod argument, "insmod bttv card=2" for
Of course you need a correctly installed soundcard unless you have the
speakers connected directly to the grabber board. Hint: check the
-mixer settings too...
+mixer settings too. ALSA for example has everything muted by default.
How sound works in detail
you can get/set the status if these pins. They can be used for input
and output.
-All grabber board vendors use these pins to control an external chip
+Most grabber board vendors use these pins to control an external chip
which does the sound routing. But every board is a little different.
These pins are also used by some companies to drive remote control
-receiver chips.
+receiver chips. Some boards use the i2c bus instead of the gpio pins
+to connect the mux chip.
As mentioned above, there is a array which holds the required
informations for each known board. You basically have to create a new
-line for your board. What is in there:
+line for your board. The important fields are these two:
struct tvcard
{
- char *name;
- int inputs; /* number of video inputs */
- int tuner; /* which of them is the tuner */
- int svhs; /* which of them is the svhs input */
+ [ ... ]
u32 gpiomask;
- u32 muxsel[8]; /* video mux */
- u32 audiomux[6]; /* audio mux: Tuner, Radio, external, internal, mute, stereo */
- u32 gpiomask2; /* GPIO MUX mask (this is video) */
+ u32 audiomux[5]; /* audio mux: tuner, radio, external, internal, mute */
};
-gpiomask has all bits set which are used to control the audio mux.
-This value basically goes to the gpio output enable register. It is
-also used to mask bits when switching the audio mux (which is done by
-read-modify-write on the gpio data register).
+gpiomask specifies which pins are used to control the audio mux chip.
+The corresponding bits in the output enable register
+(BT848_GPIO_OUT_EN) will be set as these pins must be driven by the
+bt848 chip.
+
+The audiomux[] array holds the data values for the different inputs
+(i.e. which pins must be high/low for tuner/mute/...). This will be
+written to the data register (BT848_GPIO_DATA) to switch the audio
+mux.
+
What you have to do is figure out the correct values for gpiomask and
the audiomux array. If you have Windows and the drivers four your
card installed, you might to check out if you can read these registers
values used by the windows driver. A tool to do this is available
-from ftp://telepresence.dmem.strath.ac.uk/pub/bt848/winutil. There is
-some #ifdef'ed code in bttv.c (search for "new card") which prints
-these values before board initialization, this might help too: boot
-win, start tv app, softboot (loadlin) into linux and load bttv with
-this enabled. If you hav'nt Windows installed, this is a trial and
-error game...
+from ftp://telepresence.dmem.strath.ac.uk/pub/bt848/winutil (doesn't
+work with bt878 boards according to some reports I received). You
+might also dig around in the *.ini files of the Windows applications.
+You can have a look at the board to see which of the gpio pins are
+connected at all and then start trial-and-error ...
+
+
+Starting with release 0.7.41 bttv has a number of insmod options to
+make the gpio debugging easier:
+
+bttv_gpio=0/1 enable/disable gpio debug messages
+gpiomask=n set the gpiomask value
+audiomux=i,j,... set the values of the audiomux array
+audioall=a set the values of the audiomux array (one
+ value for all array elements, useful to check
+ out which effect the particular value has).
+
+The messages printed with bttv_gpio=1 look like this:
+
+ bttv0: gpio: en=00000027, out=00000024 in=00ffffd8 [audio: off]
+
+en = output _en_able register (BT848_GPIO_OUT_EN)
+out = _out_put bits of the data register (BT848_GPIO_DATA),
+ i.e. BT848_GPIO_DATA & BT848_GPIO_OUT_EN
+in = _in_put bits of the data register,
+ i.e. BT848_GPIO_DATA & ~BT848_GPIO_OUT_EN
Good luck,
--- /dev/null
+Philips http://www.Semiconductors.COM/pip/
+Conexant http://www.conexant.com/techinfo/default.asp
+Micronas http://www.micronas.de/pages/product_documentation/index.html
DRIVERS += $(DRIVERS-y)
+
+# files removed with 'make clean'
+CLEAN_FILES = \
+ kernel/ksyms.lst include/linux/compile.h \
+ vmlinux System.map \
+ .tmp* \
+ drivers/char/consolemap_deftbl.c drivers/video/promcon_tbl.c \
+ drivers/char/conmakehash \
+ drivers/pci/devlist.h drivers/pci/classlist.h drivers/pci/gen-devlist \
+ drivers/zorro/devlist.h drivers/zorro/gen-devlist \
+ drivers/sound/bin2hex drivers/sound/hex2hex \
+ drivers/atm/fore200e_mkfirm drivers/atm/{pca,sba}*{.bin,.bin1,.bin2} \
+ net/khttpd/make_times_h \
+ net/khttpd/times.h \
+ submenu*
+# directories removed with 'make clean'
+CLEAN_DIRS = \
+ modules
+
+# files removed with 'make mrproper'
+MRPROPER_FILES = \
+ include/linux/autoconf.h include/linux/version.h \
+ drivers/net/hamradio/soundmodem/sm_tbl_{afsk1200,afsk2666,fsk9600}.h \
+ drivers/net/hamradio/soundmodem/sm_tbl_{hapn4800,psk4800}.h \
+ drivers/net/hamradio/soundmodem/sm_tbl_{afsk2400_7,afsk2400_8}.h \
+ drivers/net/hamradio/soundmodem/gentbl \
+ drivers/char/hfmodem/gentbl drivers/char/hfmodem/tables.h \
+ drivers/sound/*_boot.h drivers/sound/.*.boot \
+ drivers/sound/msndinit.c \
+ drivers/sound/msndperm.c \
+ drivers/sound/pndsperm.c \
+ drivers/sound/pndspini.c \
+ drivers/atm/fore200e_*_fw.c drivers/atm/.fore200e_*.fw \
+ .version .config* config.in config.old \
+ scripts/tkparse scripts/kconfig.tk scripts/kconfig.tmp \
+ scripts/lxdialog/*.o scripts/lxdialog/lxdialog \
+ .menuconfig.log \
+ include/asm \
+ .hdepend scripts/mkdep scripts/split-include scripts/docproc \
+ $(TOPDIR)/include/linux/modversions.h
+# directories removed with 'make mrproper'
+MRPROPER_DIRS = \
+ include/config \
+ $(TOPDIR)/include/linux/modules
+
+
include arch/$(ARCH)/Makefile
export CPPFLAGS CFLAGS AFLAGS
endif
clean: archclean
- rm -f kernel/ksyms.lst include/linux/compile.h
find . \( -name '*.[oas]' -o -name core -o -name '.*.flags' \) -type f -print \
| grep -v lxdialog/ | xargs rm -f
- rm -f vmlinux System.map
- rm -f .tmp*
- rm -f drivers/char/consolemap_deftbl.c drivers/video/promcon_tbl.c
- rm -f drivers/char/conmakehash
- rm -f drivers/pci/devlist.h drivers/pci/classlist.h drivers/pci/gen-devlist
- rm -f drivers/zorro/devlist.h drivers/zorro/gen-devlist
- rm -f drivers/sound/bin2hex drivers/sound/hex2hex
- rm -f drivers/atm/fore200e_mkfirm drivers/atm/{pca,sba}*{.bin,.bin1,.bin2}
- rm -f net/khttpd/make_times_h
- rm -f net/khttpd/times.h
- rm -f submenu*
- rm -rf modules
+ rm -f $(CLEAN_FILES)
+ rm -rf $(CLEAN_DIRS)
$(MAKE) -C Documentation/DocBook clean
mrproper: clean archmrproper
- rm -f include/linux/autoconf.h include/linux/version.h
- rm -f drivers/net/hamradio/soundmodem/sm_tbl_{afsk1200,afsk2666,fsk9600}.h
- rm -f drivers/net/hamradio/soundmodem/sm_tbl_{hapn4800,psk4800}.h
- rm -f drivers/net/hamradio/soundmodem/sm_tbl_{afsk2400_7,afsk2400_8}.h
- rm -f drivers/net/hamradio/soundmodem/gentbl
- rm -f drivers/char/hfmodem/gentbl drivers/char/hfmodem/tables.h
- rm -f drivers/sound/*_boot.h drivers/sound/.*.boot
- rm -f drivers/sound/msndinit.c
- rm -f drivers/sound/msndperm.c
- rm -f drivers/sound/pndsperm.c
- rm -f drivers/sound/pndspini.c
- rm -f drivers/atm/fore200e_*_fw.c drivers/atm/.fore200e_*.fw
- rm -f .version .config* config.in config.old
- rm -f scripts/tkparse scripts/kconfig.tk scripts/kconfig.tmp
- rm -f scripts/lxdialog/*.o scripts/lxdialog/lxdialog
- rm -f .menuconfig.log
- rm -f include/asm
- rm -rf include/config
find . \( -size 0 -o -name .depend \) -type f -print | xargs rm -f
- rm -f .hdepend scripts/mkdep scripts/split-include scripts/docproc
- rm -f $(TOPDIR)/include/linux/modversions.h
- rm -rf $(TOPDIR)/include/linux/modules
+ rm -f $(MRPROPER_FILES)
+ rm -rf $(MRPROPER_DIRS)
$(MAKE) -C Documentation/DocBook mrproper
+
distclean: mrproper
rm -f core `find . \( -name '*.orig' -o -name '*.rej' -o -name '*~' \
-o -name '*.bak' -o -name '#*#' -o -name '.*.orig' \
CONFIG_NET_PCI=y
# CONFIG_PCNET32 is not set
# CONFIG_ADAPTEC_STARFIRE is not set
+# CONFIG_AC3200 is not set
# CONFIG_APRICOT is not set
# CONFIG_CS89x0 is not set
-# CONFIG_DE4X5 is not set
# CONFIG_TULIP is not set
+# CONFIG_DE4X5 is not set
# CONFIG_DGRS is not set
+# CONFIG_DM9102 is not set
CONFIG_EEPRO100=y
+# CONFIG_EEPRO100_PM is not set
+# CONFIG_LNE390 is not set
# CONFIG_NATSEMI is not set
# CONFIG_NE2K_PCI is not set
+# CONFIG_NE3210 is not set
+# CONFIG_ES3210 is not set
# CONFIG_8139TOO is not set
+# CONFIG_RTL8129 is not set
# CONFIG_SIS900 is not set
# CONFIG_EPIC100 is not set
# CONFIG_SUNDANCE is not set
# CONFIG_TLAN is not set
# CONFIG_VIA_RHINE is not set
# CONFIG_WINBOND_840 is not set
+# CONFIG_HAPPYMEAL is not set
# CONFIG_NET_POCKET is not set
#
&apm_bios_fops
};
-#define APM_INIT_ERROR_RETURN return -1
-
/*
* Just start the APM thread. We do NOT want to do APM BIOS
* calls from anything but the APM thread, if for no other reason
{
if (apm_bios_info.version == 0) {
printk(KERN_INFO "apm: BIOS not found.\n");
- APM_INIT_ERROR_RETURN;
+ return -ENODEV;
}
printk(KERN_INFO
"apm: BIOS version %d.%d Flags 0x%02x (Driver version %s)\n",
driver_version);
if ((apm_bios_info.flags & APM_32_BIT_SUPPORT) == 0) {
printk(KERN_INFO "apm: no 32 bit BIOS support\n");
- APM_INIT_ERROR_RETURN;
+ return -ENODEV;
}
/*
if (apm_disabled) {
printk(KERN_NOTICE "apm: disabled on user request.\n");
- APM_INIT_ERROR_RETURN;
+ return -ENODEV;
}
if ((smp_num_cpus > 1) && !power_off) {
printk(KERN_NOTICE "apm: disabled - APM is not SMP safe.\n");
- APM_INIT_ERROR_RETURN;
+ return -ENODEV;
}
if (PM_IS_ACTIVE()) {
printk(KERN_NOTICE "apm: overridden by ACPI.\n");
- APM_INIT_ERROR_RETURN;
+ return -ENODEV;
}
pm_active = 1;
if (smp_num_cpus > 1) {
printk(KERN_NOTICE
"apm: disabled - APM is not SMP safe (power off active).\n");
- APM_INIT_ERROR_RETURN;
+ return 0;
}
misc_register(&apm_device);
* This has to be run for each processor
*/
-void mcheck_init(void)
+void mcheck_init(struct cpuinfo_x86 *c)
{
u32 l, h;
int i;
- struct cpuinfo_x86 *c;
static int done;
- c=cpu_data+smp_processor_id();
-
- if(c->x86_vendor!=X86_VENDOR_INTEL)
+ if( c->x86_vendor != X86_VENDOR_INTEL )
return;
- if(!(c->x86_capability&X86_FEATURE_MCE))
+ if( !test_bit(X86_FEATURE_TSC, &c->x86_capability) )
return;
- if(!(c->x86_capability&X86_FEATURE_MCA))
+ if( !test_bit(X86_FEATURE_MCA, &c->x86_capability) )
return;
/* Ok machine check is available */
processor.mpc_cpufeature = (boot_cpu_data.x86 << 8) |
(boot_cpu_data.x86_model << 4) |
boot_cpu_data.x86_mask;
- processor.mpc_featureflag = boot_cpu_data.x86_capability;
+ processor.mpc_featureflag = boot_cpu_data.x86_capability[0];
processor.mpc_reserved[0] = 0;
processor.mpc_reserved[1] = 0;
for (i = 0; i < 2; i++) {
if ( !(cpu_online_map & (1UL << cpu)) )
return -ENXIO; /* No such CPU */
- if ( !(c->x86_capability & X86_FEATURE_MSR) )
+ if ( !test_bit(X86_FEATURE_MSR, &c->x86_capability) )
return -EIO; /* MSR not supported */
return 0;
20000221 Richard Gooch <rgooch@atnf.csiro.au>
Compile fix if procfs and devfs not enabled.
Formatting changes.
+ v1.37
+ 20001109 H. Peter Anvin <hpa@zytor.com>
+ Use the new centralized CPU feature detects.
*/
#include <linux/types.h>
#include <linux/errno.h>
#include <asm/hardirq.h>
#include <linux/irq.h>
-#define MTRR_VERSION "1.36 (20000221)"
+#define MTRR_VERSION "1.37 (20001109)"
#define TRUE 1
#define FALSE 0
+/*
+ * The code assumes all processors support the same MTRR
+ * interface. This is generally a good assumption, but could
+ * potentially be a problem.
+ */
+enum mtrr_if_type {
+ MTRR_IF_NONE, /* No MTRRs supported */
+ MTRR_IF_INTEL, /* Intel (P6) standard MTRRs */
+ MTRR_IF_AMD_K6, /* AMD pre-Athlon MTRRs */
+ MTRR_IF_CYRIX_ARR, /* Cyrix ARRs */
+ MTRR_IF_CENTAUR_MCR, /* Centaur MCRs */
+} mtrr_if = MTRR_IF_NONE;
+
+static __initdata char *mtrr_if_name[] = {
+ "none", "Intel", "AMD K6", "Cyrix ARR", "Centaur MCR"
+};
+
#define MTRRcap_MSR 0x0fe
#define MTRRdefType_MSR 0x2ff
/* Disable interrupts locally */
__save_flags (ctxt->flags); __cli ();
- switch (boot_cpu_data.x86_vendor)
- {
- case X86_VENDOR_AMD:
- if (boot_cpu_data.x86 >= 6) break; /* Athlon and post-Athlon CPUs */
- /* else fall through */
- case X86_VENDOR_CENTAUR:
- if(boot_cpu_data.x86 != 6)
- return;
- /*break;*/
- }
+ if ( mtrr_if != MTRR_IF_INTEL && mtrr_if != MTRR_IF_CYRIX_ARR )
+ return;
+
/* Save value of CR4 and clear Page Global Enable (bit 7) */
- if (boot_cpu_data.x86_capability & X86_FEATURE_PGE)
+ if ( test_bit(X86_FEATURE_PGE, &boot_cpu_data.x86_capability) )
asm volatile ("movl %%cr4, %0\n\t"
"movl %0, %1\n\t"
"andb $0x7f, %b1\n\t"
"wbinvd\n\t"
: "=r" (tmp) : : "memory");
- switch (boot_cpu_data.x86_vendor)
- {
- case X86_VENDOR_AMD:
- case X86_VENDOR_INTEL:
- case X86_VENDOR_CENTAUR:
+ if ( mtrr_if == MTRR_IF_INTEL ) {
/* Disable MTRRs, and set the default type to uncached */
rdmsr (MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi);
wrmsr (MTRRdefType_MSR, ctxt->deftype_lo & 0xf300UL, ctxt->deftype_hi);
- break;
- case X86_VENDOR_CYRIX:
+ } else {
+ /* Cyrix ARRs - everything else were excluded at the top */
tmp = getCx86 (CX86_CCR3);
setCx86 (CX86_CCR3, (tmp & 0x0f) | 0x10);
ctxt->ccr3 = tmp;
- break;
}
} /* End Function set_mtrr_prepare */
{
unsigned long tmp;
- switch (boot_cpu_data.x86_vendor)
- {
- case X86_VENDOR_AMD:
- if (boot_cpu_data.x86 >= 6) break; /* Athlon and post-Athlon CPUs */
- /* else fall through */
- case X86_VENDOR_CENTAUR:
- if(boot_cpu_data.x86 != 6)
- {
- __restore_flags (ctxt->flags);
- return;
- }
- /*break;*/
+ if ( mtrr_if != MTRR_IF_INTEL && mtrr_if != MTRR_IF_CYRIX_ARR ) {
+ __restore_flags (ctxt->flags);
+ return;
}
+
/* Flush caches and TLBs */
asm volatile ("wbinvd" : : : "memory" );
/* Restore MTRRdefType */
- switch (boot_cpu_data.x86_vendor)
- {
- case X86_VENDOR_AMD:
- case X86_VENDOR_INTEL:
- case X86_VENDOR_CENTAUR:
+ if ( mtrr_if == MTRR_IF_INTEL ) {
+ /* Intel (P6) standard MTRRs */
wrmsr (MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi);
- break;
- case X86_VENDOR_CYRIX:
+ } else {
+ /* Cyrix ARRs - everything else was excluded at the top */
setCx86 (CX86_CCR3, ctxt->ccr3);
- break;
}
/* Enable caches */
: "=r" (tmp) : : "memory");
/* Restore value of CR4 */
- if (boot_cpu_data.x86_capability & X86_FEATURE_PGE)
+ if ( test_bit(X86_FEATURE_PGE, &boot_cpu_data.x86_capability) )
asm volatile ("movl %0, %%cr4"
: : "r" (ctxt->cr4val) : "memory");
{
unsigned long config, dummy;
- switch (boot_cpu_data.x86_vendor)
+ switch ( mtrr_if )
{
- case X86_VENDOR_AMD:
- if (boot_cpu_data.x86 < 6) return 2; /* pre-Athlon CPUs */
- /* else fall through */
- case X86_VENDOR_INTEL:
+ case MTRR_IF_INTEL:
rdmsr (MTRRcap_MSR, config, dummy);
return (config & 0xff);
- /*break;*/
- case X86_VENDOR_CYRIX:
- /* Cyrix have 8 ARRs */
+ case MTRR_IF_AMD_K6:
+ return 2;
+ case MTRR_IF_CYRIX_ARR:
return 8;
- case X86_VENDOR_CENTAUR:
- /* and Centaur has 8 MCR's */
- if(boot_cpu_data.x86==5)
- return 8;
- /* the cyrix III has intel compatible MTRR */
- if(boot_cpu_data.x86==6)
- {
- rdmsr (MTRRcap_MSR, config, dummy);
- return (config & 0xff);
- }
- /*break;*/
+ case MTRR_IF_CENTAUR_MCR:
+ return 8;
+ default:
+ return 0;
}
- return 0;
} /* End Function get_num_var_ranges */
/* Returns non-zero if we have the write-combining memory type */
{
unsigned long config, dummy;
- switch (boot_cpu_data.x86_vendor)
+ switch ( mtrr_if )
{
- case X86_VENDOR_AMD:
- if (boot_cpu_data.x86 < 6) return 1; /* pre-Athlon CPUs */
- /* else fall through */
- case X86_VENDOR_CENTAUR:
- if (boot_cpu_data.x86 == 5)
- return 1; /* C6 */
- /* CyrixIII is Intel like */
- case X86_VENDOR_INTEL:
+ case MTRR_IF_INTEL:
rdmsr (MTRRcap_MSR, config, dummy);
return (config & (1<<10));
- /*break;*/
- case X86_VENDOR_CYRIX:
return 1;
- /*break;*/
+ case MTRR_IF_AMD_K6:
+ case MTRR_IF_CENTAUR_MCR:
+ case MTRR_IF_CYRIX_ARR:
+ return 1;
+ default:
+ return 0;
}
- return 0;
} /* End Function have_wrcomb */
static void intel_get_mtrr (unsigned int reg, unsigned long *base,
mtrr_type ltype;
unsigned long lbase, lsize, last;
- if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return -ENODEV;
- switch (boot_cpu_data.x86_vendor)
+ switch ( mtrr_if )
{
- case X86_VENDOR_AMD:
- if (boot_cpu_data.x86 < 6)
- { /* pre-Athlon CPUs */
- /* Apply the K6 block alignment and size rules
- In order
- o Uncached or gathering only
- o 128K or bigger block
- o Power of 2 block
- o base suitably aligned to the power
- */
- if ( type > MTRR_TYPE_WRCOMB || size < (1 << 17) ||
- (size & ~(size-1))-size || ( base & (size-1) ) )
- return -EINVAL;
- break;
- }
- /* Else fall through */
- case X86_VENDOR_INTEL:
- /* Double check for Intel, we may run on Athlon */
- if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL)
+ case MTRR_IF_NONE:
+ return -ENXIO; /* No MTRRs whatsoever */
+
+ case MTRR_IF_AMD_K6:
+ /* Apply the K6 block alignment and size rules
+ In order
+ o Uncached or gathering only
+ o 128K or bigger block
+ o Power of 2 block
+ o base suitably aligned to the power
+ */
+ if ( type > MTRR_TYPE_WRCOMB || size < (1 << 17) ||
+ (size & ~(size-1))-size || ( base & (size-1) ) )
+ return -EINVAL;
+ break;
+
+ case MTRR_IF_INTEL:
+ /* For Intel PPro stepping <= 7, must be 4 MiB aligned */
+ if ( boot_cpu_data.x86_vendor == X86_VENDOR_INTEL &&
+ boot_cpu_data.x86 == 6 &&
+ boot_cpu_data.x86_model == 1 &&
+ boot_cpu_data.x86_mask <= 7 )
{
- /* For Intel PPro stepping <= 7, must be 4 MiB aligned */
- if ( (boot_cpu_data.x86 == 6) && (boot_cpu_data.x86_model == 1) &&
- (boot_cpu_data.x86_mask <= 7) && ( base & ( (1 << 22) -1 ) ) )
+ if ( base & ((1 << 22)-1) )
{
printk (KERN_WARNING "mtrr: base(0x%lx) is not 4 MiB aligned\n", base);
return -EINVAL;
}
}
- /* Fall through */
- case X86_VENDOR_CYRIX:
- case X86_VENDOR_CENTAUR:
+ /* Fall through */
+
+ case MTRR_IF_CYRIX_ARR:
+ case MTRR_IF_CENTAUR_MCR:
if ( (base & 0xfff) || (size & 0xfff) )
{
printk ("mtrr: size and base must be multiples of 4 kiB\n");
printk ("mtrr: size: %lx base: %lx\n", size, base);
return -EINVAL;
}
- if (boot_cpu_data.x86_vendor == X86_VENDOR_CENTAUR && boot_cpu_data.x86 == 5)
+ if ( mtrr_if == MTRR_IF_CENTAUR_MCR )
{
if (type != MTRR_TYPE_WRCOMB)
{
return -EINVAL;
}
break;
- default:
+
+ default:
return -EINVAL;
- /*break;*/
}
+
if (type >= MTRR_NUM_TYPES)
{
printk ("mtrr: type: %u illegal\n", type);
mtrr_type ltype;
unsigned long lbase, lsize;
- if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return -ENODEV;
+ if ( mtrr_if == MTRR_IF_NONE ) return -ENXIO;
+
max = get_num_var_ranges ();
down (&main_lock);
if (reg < 0)
printk ("mtrr: register: %d too big\n", reg);
return -EINVAL;
}
- if (boot_cpu_data.x86_vendor == X86_VENDOR_CYRIX)
+ if ( mtrr_if == MTRR_IF_CYRIX_ARR )
{
if ( (reg == 3) && arr3_protected )
{
set_mtrr_done (&ctxt);
} /* End Function centaur_mcr_init */
-static void __init mtrr_setup(void)
+static int __init mtrr_setup(void)
{
- printk ("mtrr: v%s Richard Gooch (rgooch@atnf.csiro.au)\n", MTRR_VERSION);
- switch (boot_cpu_data.x86_vendor)
- {
- case X86_VENDOR_AMD:
- if (boot_cpu_data.x86 < 6)
- {
- /* pre-Athlon CPUs */
- get_mtrr = amd_get_mtrr;
- set_mtrr_up = amd_set_mtrr_up;
- break;
- }
- /* Else fall through */
- case X86_VENDOR_INTEL:
+ if ( test_bit(X86_FEATURE_MTRR, &boot_cpu_data.x86_capability) ) {
+ /* Intel (P6) standard MTRRs */
+ mtrr_if = MTRR_IF_INTEL;
get_mtrr = intel_get_mtrr;
set_mtrr_up = intel_set_mtrr_up;
- break;
- case X86_VENDOR_CYRIX:
+ } else if ( test_bit(X86_FEATURE_K6_MTRR, &boot_cpu_data.x86_capability) ) {
+ /* Pre-Athlon (K6) AMD CPU MTRRs */
+ mtrr_if = MTRR_IF_AMD_K6;
+ get_mtrr = amd_get_mtrr;
+ set_mtrr_up = amd_set_mtrr_up;
+ } else if ( test_bit(X86_FEATURE_CYRIX_ARR, &boot_cpu_data.x86_capability) ) {
+ /* Cyrix ARRs */
+ mtrr_if = MTRR_IF_CYRIX_ARR;
get_mtrr = cyrix_get_arr;
set_mtrr_up = cyrix_set_arr_up;
get_free_region = cyrix_get_free_region;
- break;
- case X86_VENDOR_CENTAUR:
- if(boot_cpu_data.x86 == 5)
- {
- get_mtrr = centaur_get_mcr;
- set_mtrr_up = centaur_set_mcr_up;
- }
- if(boot_cpu_data.x86 == 6)
- {
- get_mtrr = intel_get_mtrr;
- set_mtrr_up = intel_set_mtrr_up;
- }
- break;
+ cyrix_arr_init();
+ } else if ( test_bit(X86_FEATURE_CENTAUR_MCR, &boot_cpu_data.x86_capability) ) {
+ /* Centaur MCRs */
+ mtrr_if = MTRR_IF_CENTAUR_MCR;
+ get_mtrr = centaur_get_mcr;
+ set_mtrr_up = centaur_set_mcr_up;
+ centaur_mcr_init();
+ } else {
+ /* No supported MTRR interface */
+ mtrr_if = MTRR_IF_NONE;
}
+
+ printk ("mtrr: v%s Richard Gooch (rgooch@atnf.csiro.au)\n"
+ "mtrr: detected mtrr type: %s\n",
+ MTRR_VERSION, mtrr_if_name[mtrr_if]);
+
+ return (mtrr_if != MTRR_IF_NONE);
} /* End Function mtrr_setup */
#ifdef CONFIG_SMP
void __init mtrr_init_boot_cpu(void)
{
- if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return;
- mtrr_setup ();
- switch (boot_cpu_data.x86_vendor)
- {
- case X86_VENDOR_AMD:
- if (boot_cpu_data.x86 < 6) break; /* Pre-Athlon CPUs */
- case X86_VENDOR_INTEL:
+ if ( !mtrr_setup () )
+ return;
+
+ if ( mtrr_if == MTRR_IF_INTEL ) {
+ /* Only for Intel MTRRs */
get_mtrr_state (&smp_mtrr_state);
- break;
- case X86_VENDOR_CYRIX:
- cyrix_arr_init ();
- break;
- case X86_VENDOR_CENTAUR: /* C6 and Cyrix III have different ones */
- if(boot_cpu_data.x86 == 5)
- centaur_mcr_init ();
- if(boot_cpu_data.x86 == 6)
- get_mtrr_state(&smp_mtrr_state);
- break;
}
} /* End Function mtrr_init_boot_cpu */
void __init mtrr_init_secondary_cpu(void)
{
- if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return;
- switch (boot_cpu_data.x86_vendor)
- {
- case X86_VENDOR_AMD:
- /* Just for robustness: pre-Athlon CPUs cannot do SMP */
- if (boot_cpu_data.x86 < 6) break;
- case X86_VENDOR_INTEL:
- intel_mtrr_init_secondary_cpu ();
+ switch ( mtrr_if ) {
+ case MTRR_IF_INTEL:
+ /* Intel (P6) standard MTRRs */
+ intel_mtrr_init_secondary_cpu();
break;
- case X86_VENDOR_CYRIX:
+ case MTRR_IF_CYRIX_ARR:
/* This is _completely theoretical_!
* I assume here that one day Cyrix will support Intel APIC.
* In reality on non-Intel CPUs we won't even get to this routine.
*/
cyrix_arr_init_secondary ();
break;
- default:
+ default:
+ /* I see no MTRRs I can support in SMP mode... */
printk ("mtrr: SMP support incomplete for this vendor\n");
- break;
}
} /* End Function mtrr_init_secondary_cpu */
#endif /* CONFIG_SMP */
int __init mtrr_init(void)
{
- if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return 0;
#ifdef CONFIG_SMP
- switch (boot_cpu_data.x86_vendor)
- {
- case X86_VENDOR_AMD:
- if (boot_cpu_data.x86 < 6) break; /* Pre-Athlon CPUs */
- case X86_VENDOR_INTEL:
+ /* mtrr_setup() should already have been called from mtrr_init_boot_cpu() */
+
+ if ( mtrr_if == MTRR_IF_INTEL ) {
finalize_mtrr_state (&smp_mtrr_state);
mtrr_state_warn (smp_changes_mask);
- break;
}
-#else /* CONFIG_SMP */
- mtrr_setup ();
- switch (boot_cpu_data.x86_vendor)
- {
- case X86_VENDOR_CYRIX:
- cyrix_arr_init ();
- break;
- case X86_VENDOR_CENTAUR:
- if(boot_cpu_data.x86 == 5)
- centaur_mcr_init ();
- break;
- }
-#endif /* !CONFIG_SMP */
+#else
+ if ( !mtrr_setup() )
+ return 0; /* MTRRs not supported? */
+#endif
#ifdef CONFIG_PROC_FS
proc_root_mtrr = create_proc_entry ("mtrr", S_IWUSR | S_IRUGO, &proc_root);
init_table ();
return 0;
} /* End Function mtrr_init */
+
+/*
+ * Local Variables:
+ * mode:c
+ * c-file-style:"k&r"
+ * c-basic-offset:4
+ * End:
+ */
* Cyrix III, Pentium IV support.
* Dave Jones <davej@suse.de>, October 2000
*
+ * Massive cleanup of CPU detection and bug handling;
+ * Transmeta CPU detection,
+ * H. Peter Anvin <hpa@zytor.com>, November 2000
*/
/*
to--;
if (!memcmp(from+4, "nopentium", 9)) {
from += 9+4;
- boot_cpu_data.x86_capability &= ~X86_FEATURE_PSE;
+ clear_bit(X86_FEATURE_PSE, &boot_cpu_data.x86_capability);
} else if (!memcmp(from+4, "exactmap", 8)) {
from += 8+4;
e820.nr_map = 0;
#endif
}
+#ifndef CONFIG_X86_TSC
+static int tsc_disable __initdata = 0;
+
+static int __init tsc_setup(char *str)
+{
+ tsc_disable = 1;
+ return 1;
+}
+
+__setup("notsc", tsc_setup);
+#endif
+
static int __init get_model_name(struct cpuinfo_x86 *c)
{
- unsigned int n, dummy, *v;
+ unsigned int *v;
+ char *p, *q;
- cpuid(0x80000000, &n, &dummy, &dummy, &dummy);
- if (n < 0x80000004)
+ if (cpuid_eax(0x80000000) < 0x80000004)
return 0;
- cpuid(0x80000001, &dummy, &dummy, &dummy, &(c->x86_capability));
+
v = (unsigned int *) c->x86_model_id;
cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]);
cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]);
cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]);
c->x86_model_id[48] = 0;
+
+ /* Intel chips right-justify this string for some dumb reason;
+ undo that brain damage */
+ p = q = &c->x86_model_id[0];
+ while ( *p == ' ' )
+ p++;
+ if ( p != q ) {
+ while ( *p )
+ *q++ = *p++;
+ while ( q <= &c->x86_model_id[48] )
+ *q++ = '\0'; /* Zero-pad the rest */
+ }
+
return 1;
}
static void __init display_cacheinfo(struct cpuinfo_x86 *c)
{
- unsigned int n, dummy, ecx, edx;
+ unsigned int n, dummy, ecx, edx, l2size;
- cpuid(0x80000000, &n, &dummy, &dummy, &dummy);
+ n = cpuid_eax(0x80000000);
if (n >= 0x80000005) {
cpuid(0x80000005, &dummy, &dummy, &ecx, &edx);
- printk("CPU: L1 I Cache: %dK L1 D Cache: %dK (%d bytes/line)\n",
- edx>>24, ecx>>24, edx&0xFF);
+ printk("CPU: L1 I Cache: %dK (%d bytes/line), D cache %dK (%d bytes/line)\n",
+ edx>>24, edx&0xFF, ecx>>24, ecx&0xFF);
c->x86_cache_size=(ecx>>24)+(edx>>24);
}
- if (n < 0x80000006) /* Cyrix just has large L1. */
+ if (n < 0x80000006) /* Some chips just has a large L1. */
return;
- cpuid(0x80000006, &dummy, &dummy, &ecx, &edx);
- c->x86_cache_size = ecx >>16;
+ ecx = cpuid_ecx(0x80000006);
+ l2size = ecx >> 16;
/* AMD errata T13 (order #21922) */
- if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD &&
- boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model == 3 &&
- boot_cpu_data.x86_mask == 0)
- {
- c->x86_cache_size = 64;
+ if (c->x86_vendor == X86_VENDOR_AMD &&
+ c->x86 == 6 &&
+ c->x86_model == 3 &&
+ c->x86_mask == 0) {
+ l2size = 64;
}
- printk("CPU: L2 Cache: %dK\n", ecx>>16);
+
+ if ( l2size == 0 )
+ return; /* Again, no L2 cache is possible */
+
+ c->x86_cache_size = l2size;
+
+ printk("CPU: L2 Cache: %dK (%d bytes/line)\n",
+ l2size, ecx & 0xFF);
}
+/*
+ * B step AMD K6 before B 9730xxxx have hardware bugs that can cause
+ * misexecution of code under Linux. Owners of such processors should
+ * contact AMD for precise details and a CPU swap.
+ *
+ * See http://www.mygale.com/~poulot/k6bug.html
+ * http://www.amd.com/K6/k6docs/revgd.html
+ *
+ * The following test is erm.. interesting. AMD neglected to up
+ * the chip setting when fixing the bug but they also tweaked some
+ * performance at the same time..
+ */
+
+extern void vide(void);
+__asm__(".align 4\nvide: ret");
-static int __init amd_model(struct cpuinfo_x86 *c)
+static int __init init_amd(struct cpuinfo_x86 *c)
{
u32 l, h;
unsigned long flags;
int mbytes = max_mapnr >> (20-PAGE_SHIFT);
+ int r;
- int r=get_model_name(c);
+ /* Bit 31 in normal CPUID used for nonstandard 3DNow ID;
+ 3DNow is IDd by bit 31 in extended CPUID (1*32+31) anyway */
+ clear_bit(0*32+31, &c->x86_capability);
+
+ r = get_model_name(c);
switch(c->x86)
{
case 5:
if( c->x86_model < 6 )
{
- /* Anyone with a K5 want to fill this in */
+ /* Based on AMD doc 20734R - June 2000 */
+ if ( c->x86_model == 0 ) {
+ clear_bit(X86_FEATURE_APIC, &c->x86_capability);
+ set_bit(X86_FEATURE_PGE, &c->x86_capability);
+ }
break;
}
+ if ( c->x86_model == 6 && c->x86_mask == 1 ) {
+ const int K6_BUG_LOOP = 1000000;
+ int n;
+ void (*f_vide)(void);
+ unsigned long d, d2;
+
+ printk(KERN_INFO "AMD K6 stepping B detected - ");
+
+ /*
+ * It looks like AMD fixed the 2.6.2 bug and improved indirect
+ * calls at the same time.
+ */
+
+ n = K6_BUG_LOOP;
+ f_vide = vide;
+ rdtscl(d);
+ while (n--)
+ f_vide();
+ rdtscl(d2);
+ d = d2-d;
+
+ /* Knock these two lines out if it debugs out ok */
+ printk(KERN_INFO "K6 BUG %ld %d (Report these if test report is incorrect)\n", d, 20*K6_BUG_LOOP);
+ printk(KERN_INFO "AMD K6 stepping B detected - ");
+ /* -- cut here -- */
+ if (d > 20*K6_BUG_LOOP)
+ printk("system stability may be impaired when more than 32 MB are used.\n");
+ else
+ printk("probably OK (after B9730xxxx).\n");
+ printk(KERN_INFO "Please see http://www.mygale.com/~poulot/k6bug.html\n");
+ }
+
/* K6 with old style WHCR */
if( c->x86_model < 8 ||
(c->x86_model== 8 && c->x86_mask < 8))
}
/* Set MTRR capability flag if appropriate */
- if((boot_cpu_data.x86_model == 13) ||
- (boot_cpu_data.x86_model == 9) ||
- ((boot_cpu_data.x86_model == 8) &&
- (boot_cpu_data.x86_mask >= 8)))
- c->x86_capability |= X86_FEATURE_MTRR;
+ if ( (c->x86_model == 13) ||
+ (c->x86_model == 9) ||
+ ((c->x86_model == 8) &&
+ (c->x86_mask >= 8)) )
+ set_bit(X86_FEATURE_K6_MTRR, &c->x86_capability);
break;
}
return r;
}
-static void __init intel_model(struct cpuinfo_x86 *c)
-{
- unsigned int *v = (unsigned int *) c->x86_model_id;
- cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]);
- cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]);
- cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]);
- c->x86_model_id[48] = 0;
- printk("CPU: %s\n", c->x86_model_id);
-}
-
-
/*
* Read Cyrix DEVID registers (DIR) to get more detailed info. about the CPU
*/
static char cyrix_model_mult1[] __initdata = "12??43";
static char cyrix_model_mult2[] __initdata = "12233445";
-static void __init cyrix_model(struct cpuinfo_x86 *c)
+/*
+ * Reset the slow-loop (SLOP) bit on the 686(L) which is set by some old
+ * BIOSes for compatability with DOS games. This makes the udelay loop
+ * work correctly, and improves performance.
+ */
+
+extern void calibrate_delay(void) __init;
+
+static void __init check_cx686_slop(struct cpuinfo_x86 *c)
+{
+ if (Cx86_dir0_msb == 3) {
+ unsigned char ccr3, ccr5;
+
+ cli();
+ ccr3 = getCx86(CX86_CCR3);
+ setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */
+ ccr5 = getCx86(CX86_CCR5);
+ if (ccr5 & 2)
+ setCx86(CX86_CCR5, ccr5 & 0xfd); /* reset SLOP */
+ setCx86(CX86_CCR3, ccr3); /* disable MAPEN */
+ sti();
+
+ if (ccr5 & 2) { /* possible wrong calibration done */
+ printk(KERN_INFO "Recalibrating delay loop with SLOP bit reset\n");
+ calibrate_delay();
+ c->loops_per_sec = loops_per_sec;
+ }
+ }
+}
+
+static void __init init_cyrix(struct cpuinfo_x86 *c)
{
unsigned char dir0, dir0_msn, dir0_lsn, dir1 = 0;
char *buf = c->x86_model_id;
const char *p = NULL;
+ /* Bit 31 in normal CPUID used for nonstandard 3DNow ID;
+ 3DNow is IDd by bit 31 in extended CPUID (1*32+31) anyway */
+ clear_bit(0*32+31, &c->x86_capability);
+
+ /* Cyrix used bit 24 in extended (AMD) CPUID for Cyrix MMX extensions */
+ if ( test_bit(1*32+24, &c->x86_capability) ) {
+ clear_bit(1*32+24, &c->x86_capability);
+ set_bit(X86_FEATURE_CXMMX, &c->x86_capability);
+ }
+
do_cyrix_devid(&dir0, &dir1);
+ check_cx686_slop(c);
+
Cx86_dir0_msb = dir0_msn = dir0 >> 4; /* identifies CPU "family" */
dir0_lsn = dir0 & 0xf; /* model or clock multiplier */
} else /* 686 */
p = Cx86_cb+1;
/* Emulate MTRRs using Cyrix's ARRs. */
- c->x86_capability |= X86_FEATURE_MTRR;
+ set_bit(X86_FEATURE_CYRIX_ARR, &c->x86_capability);
/* 6x86's contain this bug */
c->coma_bug = 1;
break;
/* GXm supports extended cpuid levels 'ala' AMD */
if (c->cpuid_level == 2) {
get_model_name(c); /* get CPU marketing name */
- c->x86_capability&=~X86_FEATURE_TSC;
+ clear_bit(X86_FEATURE_TSC, c->x86_capability);
return;
}
else { /* MediaGX */
Cx86_cb[2] = (dir0_lsn & 1) ? '3' : '4';
p = Cx86_cb+2;
c->x86_model = (dir1 & 0x20) ? 1 : 2;
- c->x86_capability&=~X86_FEATURE_TSC;
+ clear_bit(X86_FEATURE_TSC, &c->x86_capability);
}
break;
if (((dir1 & 0x0f) > 4) || ((dir1 & 0xf0) == 0x20))
(c->x86_model)++;
/* Emulate MTRRs using Cyrix's ARRs. */
- c->x86_capability |= X86_FEATURE_MTRR;
+ set_bit(X86_FEATURE_CYRIX_ARR, &c->x86_capability);
break;
case 0xf: /* Cyrix 486 without DEVID registers */
return;
}
-static void __init centaur_model(struct cpuinfo_x86 *c)
+static void __init init_centaur(struct cpuinfo_x86 *c)
{
enum {
ECX8=1<<1,
u32 lo,hi,newlo;
u32 aa,bb,cc,dd;
+ /* Bit 31 in normal CPUID used for nonstandard 3DNow ID;
+ 3DNow is IDd by bit 31 in extended CPUID (1*32+31) anyway */
+ clear_bit(0*32+31, &c->x86_capability);
+
switch (c->x86) {
case 5:
fcr_set=ECX8|DSMC|EDCTLB|EMMX|ERETSTK;
fcr_clr=DPDC;
printk("Disabling bugged TSC.\n");
- c->x86_capability &= ~X86_FEATURE_TSC;
+ clear_bit(X86_FEATURE_TSC, &c->x86_capability);
break;
case 8:
switch(c->x86_mask) {
printk("Centaur FCR is 0x%X\n",lo);
}
/* Emulate MTRRs using Centaur's MCR. */
- c->x86_capability |= X86_FEATURE_MTRR;
+ set_bit(X86_FEATURE_CENTAUR_MCR, &c->x86_capability);
/* Report CX8 */
- c->x86_capability |= X86_FEATURE_CX8;
+ set_bit(X86_FEATURE_CX8, &c->x86_capability);
/* Set 3DNow! on Winchip 2 and above. */
if (c->x86_model >=8)
- c->x86_capability |= X86_FEATURE_AMD3D;
+ set_bit(X86_FEATURE_3DNOW, &c->x86_capability);
/* See if we can find out some more. */
- cpuid(0x80000000,&aa,&bb,&cc,&dd);
- if (aa>=0x80000005) { /* Yes, we can. */
+ if ( cpuid_eax(0x80000000) >= 0x80000005 ) {
+ /* Yes, we can. */
cpuid(0x80000005,&aa,&bb,&cc,&dd);
/* Add L1 data and code cache sizes. */
c->x86_cache_size = (cc>>24)+(dd>>24);
lo |= (1<<1 | 1<<7); /* Report CX8 & enable PGE */
wrmsr (0x1107, lo, hi);
- c->x86_capability |= X86_FEATURE_CX8;
+ set_bit(X86_FEATURE_CX8, &c->x86_capability);
rdmsr (0x80000001, lo, hi);
if (hi & (1<<31))
- c->x86_capability |= X86_FEATURE_AMD3D;
+ set_bit(X86_FEATURE_3DNOW, &c->x86_capability);
get_model_name(c);
display_cacheinfo(c);
}
-static void __init transmeta_model(struct cpuinfo_x86 *c)
+static void __init init_transmeta(struct cpuinfo_x86 *c)
{
unsigned int cap_mask, uk, max, dummy;
unsigned int cms_rev1, cms_rev2;
display_cacheinfo(c);
/* Print CMS and CPU revision */
- cpuid(0x80860000, &max, &dummy, &dummy, &dummy);
+ max = cpuid_eax(0x80860000);
if ( max >= 0x80860001 ) {
cpuid(0x80860001, &dummy, &cpu_rev, &cpu_freq, &cpu_flags);
- printk("CPU: Processor revision %u.%u.%u.%u, %u MHz%s%s\n",
+ printk("CPU: Processor revision %u.%u.%u.%u, %u MHz\n",
(cpu_rev >> 24) & 0xff,
(cpu_rev >> 16) & 0xff,
(cpu_rev >> 8) & 0xff,
cpu_rev & 0xff,
- cpu_freq,
- (cpu_flags & 1) ? " [recovery]" : "",
- (cpu_flags & 2) ? " [longrun]" : "");
+ cpu_freq);
}
if ( max >= 0x80860002 ) {
cpuid(0x80860002, &dummy, &cms_rev1, &cms_rev2, &dummy);
printk("CPU: %s\n", cpu_info);
}
- /* Unhide possibly hidden flags */
+ /* Unhide possibly hidden capability flags */
rdmsr(0x80860004, cap_mask, uk);
wrmsr(0x80860004, ~0, uk);
- cpuid(0x00000001, &dummy, &dummy, &dummy, &c->x86_capability);
+ c->x86_capability[0] = cpuid_edx(0x00000001);
wrmsr(0x80860004, cap_mask, uk);
}
+extern void trap_init_f00f_bug(void);
+
+static void __init init_intel(struct cpuinfo_x86 *c)
+{
+#ifndef CONFIG_M686
+ static int f00f_workaround_enabled = 0;
+#endif
+ extern void mcheck_init(struct cpuinfo_x86 *c);
+ char *p = NULL;
+
+#ifndef CONFIG_M686
+ /*
+ * All current models of Pentium and Pentium with MMX technology CPUs
+ * have the F0 0F bug, which lets nonpriviledged users lock up the system.
+ * Note that the workaround only should be initialized once...
+ */
+ c->f00f_bug = 0;
+ if ( c->x86 == 5 ) {
+ c->f00f_bug = 1;
+ if ( !f00f_workaround_enabled ) {
+ trap_init_f00f_bug();
+ printk(KERN_INFO "Intel Pentium with F0 0F bug - workaround enabled.\n");
+ f00f_workaround_enabled = 1;
+ }
+ }
+#endif
+
+
+ if (c->cpuid_level > 1) {
+ /* supports eax=2 call */
+ int edx = cpuid_edx(2);
+
+ /* We need only the LSB */
+ edx &= 0xff;
+
+ switch (edx) {
+ case 0x40:
+ c->x86_cache_size = 0;
+ break;
+
+ case 0x41: /* 4-way 128 */
+ c->x86_cache_size = 128;
+ break;
+
+ case 0x42: /* 4-way 256 */
+ case 0x82: /* 8-way 256 */
+ c->x86_cache_size = 256;
+ break;
+
+ case 0x43: /* 4-way 512 */
+ c->x86_cache_size = 512;
+ break;
+
+ case 0x44: /* 4-way 1024 */
+ case 0x84: /* 8-way 1024 */
+ c->x86_cache_size = 1024;
+ break;
+
+ case 0x45: /* 4-way 2048 */
+ case 0x85: /* 8-way 2048 */
+ c->x86_cache_size = 2048;
+ break;
+
+ default:
+ c->x86_cache_size = 0;
+ break;
+ }
+ }
+
+ /* SEP CPUID bug: Pentium Pro reports SEP but doesn't have it */
+ if ( c->x86 == 6 && c->x86_model < 3 && c->x86_mask < 3 )
+ clear_bit(X86_FEATURE_SEP, &c->x86_capability);
+
+ /* Names for the Pentium II/Celeron processors
+ detectable only by also checking the cache size.
+ Dixon is NOT a Celeron. */
+ if (c->x86 == 6) {
+ switch (c->x86_model) {
+ case 5:
+ if (c->x86_cache_size == 0)
+ p = "Celeron (Covington)";
+ if (c->x86_cache_size == 256)
+ p = "Mobile Pentium II (Dixon)";
+ break;
+
+ case 6:
+ if (c->x86_cache_size == 128)
+ p = "Celeron (Mendocino)";
+ break;
+
+ case 8:
+ if (c->x86_cache_size == 128)
+ p = "Celeron (Coppermine)";
+ break;
+ }
+ }
+
+ if ( p )
+ strcpy(c->x86_model_id, p);
+
+ /* Enable MCA if available */
+ mcheck_init(c);
+}
void __init get_cpu_vendor(struct cpuinfo_x86 *c)
{
c->x86_vendor = X86_VENDOR_NEXGEN;
else if (!strcmp(v, "RiseRiseRise"))
c->x86_vendor = X86_VENDOR_RISE;
- else if (!strcmp(v, "GenuineTMx86"))
+ else if (!strcmp(v, "GenuineTMx86") ||
+ !strcmp(v, "TransmetaCPU"))
c->x86_vendor = X86_VENDOR_TRANSMETA;
else
c->x86_vendor = X86_VENDOR_UNKNOWN;
struct cpu_model_info {
int vendor;
- int x86;
+ int family;
char *model_names[16];
};
/* Naming convention should be: <Name> [(<Codename>)] */
+/* This table only is used unless init_<vendor>() below doesn't set it; */
+/* in particular, if CPUID levels 0x80000002..4 are supported, this isn't used */
static struct cpu_model_info cpu_models[] __initdata = {
{ X86_VENDOR_INTEL, 4,
{ "486 DX-25/33", "486 DX-50", "486 SX", "486 DX/2", "486 SL",
{ NULL, NULL, NULL, "486 DX/2", NULL, NULL, NULL, "486 DX/2-WB",
"486 DX/4", "486 DX/4-WB", NULL, NULL, NULL, NULL, "Am5x86-WT",
"Am5x86-WB" }},
- { X86_VENDOR_AMD, 5,
+ { X86_VENDOR_AMD, 5, /* Is this this really necessary?? */
{ "K5/SSA5", "K5",
"K5", "K5", NULL, NULL,
"K6", "K6", "K6-2",
"K6-3", NULL, NULL, NULL, NULL, NULL, NULL }},
- { X86_VENDOR_AMD, 6,
+ { X86_VENDOR_AMD, 6, /* Is this this really necessary?? */
{ "Athlon", "Athlon",
"Athlon", NULL, "Athlon", NULL,
NULL, NULL, NULL,
{ X86_VENDOR_RISE, 5,
{ "mP6", "mP6", NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }},
- { X86_VENDOR_TRANSMETA, 5,
- { NULL, NULL, NULL, "Crusoe", NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }},
};
+/* Look up CPU names by table lookup. */
+static char __init *table_lookup_model(struct cpuinfo_x86 *c)
+{
+ struct cpu_model_info *info = cpu_models;
+ int i;
+
+ if ( c->x86_model >= 16 )
+ return NULL; /* Range check */
+
+ for ( i = 0 ; i < sizeof(cpu_models)/sizeof(struct cpu_model_info) ; i++ ) {
+ if ( info->vendor == c->x86_vendor &&
+ info->family == c->x86 ) {
+ return info->model_names[c->x86_model];
+ }
+ info++;
+ }
+ return NULL; /* Not found */
+}
+
/*
* Detect a NexGen CPU running without BIOS hypercode new enough
* to have CPUID. (Thanks to Herbert Oppmann)
static void __init squash_the_stupid_serial_number(struct cpuinfo_x86 *c)
{
- if(c->x86_capability&(X86_FEATURE_PN) && disable_x86_serial_nr) {
+ if( test_bit(X86_FEATURE_PN, &c->x86_capability) &&
+ disable_x86_serial_nr ) {
/* Disable processor serial number */
unsigned long lo,hi;
rdmsr(0x119,lo,hi);
lo |= 0x200000;
wrmsr(0x119,lo,hi);
printk(KERN_INFO "CPU serial number disabled.\n");
+ clear_bit(X86_FEATURE_PN, &c->x86_capability);
}
}
__setup("serialnumber", x86_serial_nr_setup);
-void __init identify_cpu(struct cpuinfo_x86 *c)
+/* Standard macro to see if a specific flag is changeable */
+static inline int flag_is_changeable_p(u32 flag)
{
- int i=0;
- char *p = NULL;
- extern void mcheck_init(void);
-
- c->loops_per_sec = loops_per_sec;
- c->x86_cache_size = -1;
+ u32 f1, f2;
+
+ asm("pushfl\n\t"
+ "pushfl\n\t"
+ "popl %0\n\t"
+ "movl %0,%1\n\t"
+ "xorl %2,%0\n\t"
+ "pushl %0\n\t"
+ "popfl\n\t"
+ "pushfl\n\t"
+ "popl %0\n\t"
+ "popfl\n\t"
+ : "=&r" (f1), "=&r" (f2)
+ : "ir" (flag));
+
+ return ((f1^f2) & flag) != 0;
+}
- get_cpu_vendor(c);
+/* Probe for the CPUID instruction */
+static int __init have_cpuid_p(void)
+{
+ return flag_is_changeable_p(X86_EFLAGS_ID);
+}
- switch (c->x86_vendor) {
+/*
+ * Cyrix CPUs without cpuid or with cpuid not yet enabled can be detected
+ * by the fact that they preserve the flags across the division of 5/2.
+ * PII and PPro exhibit this behavior too, but they have cpuid available.
+ */
+
+/*
+ * Perform the Cyrix 5/2 test. A Cyrix won't change
+ * the flags, while other 486 chips will.
+ */
+static inline int test_cyrix_52div(void)
+{
+ unsigned int test;
+
+ __asm__ __volatile__(
+ "sahf\n\t" /* clear flags (%eax = 0x0005) */
+ "div %b2\n\t" /* divide 5 by 2 */
+ "lahf" /* store flags into %ah */
+ : "=a" (test)
+ : "0" (5), "q" (2)
+ : "cc");
+
+ /* AH is 0x02 on Cyrix after the divide.. */
+ return (unsigned char) (test >> 8) == 0x02;
+}
- case X86_VENDOR_UNKNOWN:
- if (c->cpuid_level < 0)
- {
- /* It may be a nexgen with cpuid disabled.. */
- if(deep_magic_nexgen_probe())
- {
- strcpy(c->x86_model_id, "Nx586");
- c->x86_vendor = X86_VENDOR_NEXGEN;
- }
- return;
- }
- break;
+/* Try to detect a CPU with disabled CPUID, and if so, enable. This routine
+ may also be used to detect non-CPUID processors and fill in some of
+ the information manually. */
+static int __init id_and_try_enable_cpuid(struct cpuinfo_x86 *c)
+{
+ /* First of all, decide if this is a 486 or higher */
+ /* It's a 486 if we can modify the AC flag */
+ if ( flag_is_changeable_p(X86_EFLAGS_AC) )
+ c->x86 = 4;
+ else
+ c->x86 = 3;
- case X86_VENDOR_CYRIX:
- cyrix_model(c);
- return;
+ /* Detect Cyrix with disabled CPUID */
+ if ( c->x86 == 4 && test_cyrix_52div() ) {
+ strcpy(c->x86_vendor_id, "CyrixInstead");
+ }
- case X86_VENDOR_AMD:
- if (amd_model(c))
- return;
- break;
+ /* Detect NexGen with old hypercode */
+ if ( deep_magic_nexgen_probe() ) {
+ strcpy(c->x86_vendor_id, "NexGenDriven");
+ }
- case X86_VENDOR_CENTAUR:
- centaur_model(c);
- return;
+ return have_cpuid_p(); /* Check to see if CPUID now enabled? */
+}
- case X86_VENDOR_INTEL:
+/*
+ * This does the hard work of actually picking apart the CPU stuff...
+ */
+void __init identify_cpu(struct cpuinfo_x86 *c)
+{
+ int junk, i;
+ u32 xlvl, tfms;
- squash_the_stupid_serial_number(c);
- mcheck_init();
+ c->loops_per_sec = loops_per_sec;
+ c->x86_cache_size = -1;
+ c->x86_vendor = X86_VENDOR_UNKNOWN;
+ c->cpuid_level = -1; /* CPUID not detected */
+ c->x86_model = c->x86_mask = 0; /* So far unknown... */
+ c->x86_vendor_id[0] = '\0'; /* Unset */
+ c->x86_model_id[0] = '\0'; /* Unset */
+ memset(&c->x86_capability, 0, sizeof c->x86_capability);
+
+ if ( !have_cpuid_p() && !id_and_try_enable_cpuid(c) ) {
+ /* CPU doesn't have CPUID */
+
+ /* If there are any capabilities, they're vendor-specific */
+ /* enable_cpuid() would have set c->x86 for us. */
+ } else {
+ /* CPU does have CPUID */
+
+ /* Get vendor name */
+ cpuid(0x00000000, &c->cpuid_level,
+ (int *)&c->x86_vendor_id[0],
+ (int *)&c->x86_vendor_id[8],
+ (int *)&c->x86_vendor_id[4]);
+
+ get_cpu_vendor(c);
+
+ /* Initialize the standard set of capabilities */
+ /* Note that the vendor-specific code below might override */
+
+ /* Intel-defined flags: level 0x00000001 */
+ if ( c->cpuid_level >= 0x00000001 ) {
+ cpuid(0x00000001, &tfms, &junk, &junk,
+ &c->x86_capability[0]);
+ c->x86 = (tfms >> 8) & 15;
+ c->x86_model = (tfms >> 4) & 15;
+ c->x86_mask = tfms & 15;
+ } else {
+ /* Have CPUID level 0 only - unheard of */
+ c->x86 = 4;
+ }
- if (c->cpuid_level > 1) {
- /* supports eax=2 call */
- int edx, dummy;
+ /* AMD-defined flags: level 0x80000001 */
+ xlvl = cpuid_eax(0x80000000);
+ if ( (xlvl & 0xffff0000) == 0x80000000 ) {
+ if ( xlvl >= 0x80000001 )
+ c->x86_capability[1] = cpuid_edx(0x80000001);
+ if ( xlvl >= 0x80000004 )
+ get_model_name(c); /* Default name */
+ }
- cpuid(2, &dummy, &dummy, &dummy, &edx);
+ /* Transmeta-defined flags: level 0x80860001 */
+ xlvl = cpuid_eax(0x80860000);
+ if ( (xlvl & 0xffff0000) == 0x80860000 ) {
+ if ( xlvl >= 0x80860001 )
+ c->x86_capability[2] = cpuid_edx(0x80860001);
+ }
+ }
- /* We need only the LSB */
- edx &= 0xff;
+ printk("CPU: Before vendor init, caps: %08x %08x %08x, vendor = %d\n",
+ c->x86_capability[0],
+ c->x86_capability[1],
+ c->x86_capability[2],
+ c->x86_vendor);
- switch (edx) {
- case 0x40:
- c->x86_cache_size = 0;
- break;
+ /*
+ * Vendor-specific initialization. In this section we
+ * canonicalize the feature flags, meaning if there are
+ * features a certain CPU supports which CPUID doesn't
+ * tell us, CPUID claiming incorrect flags, or other bugs,
+ * we handle them here.
+ *
+ * At the end of this section, c->x86_capability better
+ * indicate the features this CPU genuinely supports!
+ */
+ switch ( c->x86_vendor ) {
+ case X86_VENDOR_UNKNOWN:
+ default:
+ /* Not much we can do here... */
+ break;
- case 0x41: /* 4-way 128 */
- c->x86_cache_size = 128;
- break;
+ case X86_VENDOR_CYRIX:
+ init_cyrix(c);
+ break;
- case 0x42: /* 4-way 256 */
- case 0x82: /* 8-way 256 */
- c->x86_cache_size = 256;
- break;
+ case X86_VENDOR_AMD:
+ init_amd(c);
+ break;
- case 0x43: /* 4-way 512 */
- c->x86_cache_size = 512;
- break;
+ case X86_VENDOR_CENTAUR:
+ init_centaur(c);
+ break;
- case 0x44: /* 4-way 1024 */
- case 0x84: /* 8-way 1024 */
- c->x86_cache_size = 1024;
- break;
+ case X86_VENDOR_INTEL:
+ init_intel(c);
+ break;
- case 0x45: /* 4-way 2048 */
- case 0x85: /* 8-way 2048 */
- c->x86_cache_size = 2048;
- break;
+ case X86_VENDOR_NEXGEN:
+ c->x86_cache_size = 256; /* A few had 1 MB... */
+ break;
- default:
- c->x86_cache_size = 0;
- break;
- }
- }
+ case X86_VENDOR_TRANSMETA:
+ init_transmeta(c);
+ break;
+ }
+
+ printk("CPU: After vendor init, caps: %08x %08x %08x %08x\n",
+ c->x86_capability[0],
+ c->x86_capability[1],
+ c->x86_capability[2],
+ c->x86_capability[3]);
- /* Pentium IV. */
- if (c->x86 == 15) {
- intel_model(c);
- return;
- }
+ /*
+ * The vendor-specific functions might have changed features. Now
+ * we do "generic changes."
+ */
- /* Names for the Pentium II/Celeron processors
- detectable only by also checking the cache size.
- Dixon is NOT a Celeron. */
- if (c->x86 == 6) {
- switch (c->x86_model) {
- case 5:
- if (c->x86_cache_size == 0)
- p = "Celeron (Covington)";
- if (c->x86_cache_size == 256)
- p = "Mobile Pentium II (Dixon)";
- break;
-
- case 6:
- if (c->x86_cache_size == 128)
- p = "Celeron (Mendocino)";
- break;
-
- case 8:
- if (c->x86_cache_size == 128)
- p = "Celeron (Coppermine)";
- break;
- }
- }
- if (p!=NULL)
- goto name_decoded;
+ /* TSC disabled? */
+#ifdef CONFIG_TSC
+ if ( tsc_disable )
+ clear_bit(X86_FEATURE_TSC, &c->x86_capability);
+#endif
- break;
+ /* Disable the PN if appropriate */
+ squash_the_stupid_serial_number(c);
- case X86_VENDOR_TRANSMETA:
- transmeta_model(c);
- squash_the_stupid_serial_number(c);
- return;
+ /* If the model name is still unset, do table lookup. */
+ if ( !c->x86_model_id[0] ) {
+ char *p;
+ p = table_lookup_model(c);
+ if ( p )
+ strcpy(c->x86_model_id, p);
+ else
+ /* Last resort... */
+ sprintf(c->x86_model_id, "%02x/%02x",
+ c->x86_vendor, c->x86_model);
}
- /* may be changed in the switch so needs to be after */
-
- if(c->x86_vendor == X86_VENDOR_NEXGEN)
- c->x86_cache_size = 256; /* A few had 1Mb.. */
-
- for (i = 0; i < sizeof(cpu_models)/sizeof(struct cpu_model_info); i++) {
- if (cpu_models[i].vendor == c->x86_vendor &&
- cpu_models[i].x86 == c->x86) {
- if (c->x86_model <= 16)
- p = cpu_models[i].model_names[c->x86_model];
- }
- }
+ /* Now the feature flags better reflect actual CPU features! */
-name_decoded:
+ printk("CPU: After generic, caps: %08x %08x %08x %08x\n",
+ c->x86_capability[0],
+ c->x86_capability[1],
+ c->x86_capability[2],
+ c->x86_capability[3]);
- if (p) {
- strcpy(c->x86_model_id, p);
- return;
+ /*
+ * On SMP, boot_cpu_data holds the common feature set between
+ * all CPUs; so make sure that we indicate which features are
+ * common between the CPUs. The first time this routine gets
+ * executed, c == &boot_cpu_data.
+ */
+ if ( c != &boot_cpu_data ) {
+ /* AND the already accumulated flags with these */
+ for ( i = 0 ; i < NCAPINTS ; i++ )
+ boot_cpu_data.x86_capability[i] &= c->x86_capability[i];
}
- sprintf(c->x86_model_id, "%02x/%02x", c->x86_vendor, c->x86_model);
+ printk("CPU: Common caps: %08x %08x %08x %08x\n",
+ boot_cpu_data.x86_capability[0],
+ boot_cpu_data.x86_capability[1],
+ boot_cpu_data.x86_capability[2],
+ boot_cpu_data.x86_capability[3]);
}
-
/*
* Perform early boot up checks for a valid TSC. See arch/i386/kernel/time.c
*/
{
get_cpu_vendor(&boot_cpu_data);
- if(boot_cpu_data.x86_vendor != X86_VENDOR_CYRIX)
- return;
-
- cyrix_model(&boot_cpu_data);
+ if ( boot_cpu_data.x86_vendor == X86_VENDOR_CYRIX )
+ init_cyrix(&boot_cpu_data);
}
-
+/* These need to match <asm/processor.h> */
static char *cpu_vendor_names[] __initdata = {
"Intel", "Cyrix", "AMD", "UMC", "NexGen", "Centaur", "Rise", "Transmeta" };
else
printk("%s", c->x86_model_id);
- if (c->x86_mask || c->cpuid_level>=0)
+ if (c->x86_mask || c->cpuid_level >= 0)
printk(" stepping %02x\n", c->x86_mask);
else
printk("\n");
int get_cpuinfo(char * buffer)
{
char *p = buffer;
- int sep_bug;
/*
- * Flags should be entered into the array ONLY if there is no overlap.
- * Else a number should be used and then overridden in the case
- * statement below. --Jauder <jauderho@carumba.com>
- *
- * NOTE: bits 10, 19-22, 26-31 are reserved.
- *
- * Data courtesy of http://www.sandpile.org/arch/cpuid.htm
- * Thanks to the Greasel!
+ * These flag bits must match the definitions in <asm/cpufeature.h>.
+ * NULL means this bit is undefined or reserved; either way it doesn't
+ * have meaning as far as Linux is concerned. Note that it's important
+ * to realize there is a difference between this table and CPUID -- if
+ * applications want to get the raw CPUID data, they should access
+ * /dev/cpu/<cpu_nr>/cpuid instead.
*/
static char *x86_cap_flags[] = {
+ /* Intel-defined */
"fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce",
- "cx8", "apic", "10", "sep", "mtrr", "pge", "mca", "cmov",
- "16", "pse36", "psn", "19", "20", "21", "22", "mmx",
- "24", "xmm", "26", "27", "28", "29", "30", "31"
+ "cx8", "apic", NULL, "sep", "mtrr", "pge", "mca", "cmov",
+ "pat", "pse36", "pn", "clflsh", NULL, "dtes", NULL, "mmx",
+ "fxsr", "sse", "sse2", "selfsnoop", NULL, "acc", "ia64", NULL,
+
+ /* AMD-defined */
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, "syscall", NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, "mmxext", NULL,
+ NULL, NULL, NULL, NULL, NULL, "lm", "3dnowext", "3dnow",
+
+ /* Transmeta-defined */
+ "recovery", "longrun", NULL, "lrti", NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+
+ /* Other (Linux-defined) */
+ "cxmmx", "k6_mtrr", "cyrix_arr", "centaur_mcr", NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
};
struct cpuinfo_x86 *c = cpu_data;
int i, n;
else
p += sprintf(p, "stepping\t: unknown\n");
- if (c->x86_capability & X86_FEATURE_TSC) {
+ if ( test_bit(X86_FEATURE_TSC, &c->x86_capability) ) {
p += sprintf(p, "cpu MHz\t\t: %lu.%06lu\n",
cpu_khz / 1000, (cpu_khz % 1000));
}
if (c->x86_cache_size >= 0)
p += sprintf(p, "cache size\t: %d KB\n", c->x86_cache_size);
- /* Modify the capabilities according to chip type */
- switch (c->x86_vendor) {
-
- case X86_VENDOR_CYRIX:
- x86_cap_flags[24] = "cxmmx";
- break;
-
- case X86_VENDOR_AMD:
- if (c->x86 == 5 && c->x86_model == 6)
- x86_cap_flags[10] = "sep";
- if (c->x86 < 6)
- x86_cap_flags[16] = "fcmov";
- else
- x86_cap_flags[16] = "pat";
- x86_cap_flags[22] = "mmxext";
- x86_cap_flags[24] = "fxsr";
- x86_cap_flags[30] = "3dnowext";
- x86_cap_flags[31] = "3dnow";
- break;
-
- case X86_VENDOR_INTEL:
- x86_cap_flags[16] = "pat";
- x86_cap_flags[18] = "pn";
- x86_cap_flags[24] = "fxsr";
- x86_cap_flags[25] = "xmm";
- break;
-
- case X86_VENDOR_CENTAUR:
- if (c->x86_model >=8) /* Only Winchip2 and above */
- x86_cap_flags[31] = "3dnow";
- break;
-
- default:
- /* Unknown CPU manufacturer or no special handling needed */
- break;
- }
-
- sep_bug = c->x86_vendor == X86_VENDOR_INTEL &&
- c->x86 == 0x06 &&
- c->cpuid_level >= 0 &&
- (c->x86_capability & X86_FEATURE_SEP) &&
- c->x86_model < 3 &&
- c->x86_mask < 3;
-
/* We use exception 16 if we have hardware math and we've either seen it or the CPU claims it is internal */
- fpu_exception = c->hard_math && (ignore_irq13 | (c->x86_capability & X86_FEATURE_FPU));
+ fpu_exception = c->hard_math && (ignore_irq13 | test_bit(X86_FEATURE_FPU, &c->x86_capability));
p += sprintf(p, "fdiv_bug\t: %s\n"
"hlt_bug\t\t: %s\n"
- "sep_bug\t\t: %s\n"
"f00f_bug\t: %s\n"
"coma_bug\t: %s\n"
"fpu\t\t: %s\n"
"fpu_exception\t: %s\n"
"cpuid level\t: %d\n"
"wp\t\t: %s\n"
- "flags\t\t:",
+ "features\t:",
c->fdiv_bug ? "yes" : "no",
c->hlt_works_ok ? "no" : "yes",
- sep_bug ? "yes" : "no",
c->f00f_bug ? "yes" : "no",
c->coma_bug ? "yes" : "no",
c->hard_math ? "yes" : "no",
c->cpuid_level,
c->wp_works_ok ? "yes" : "no");
- for ( i = 0 ; i < 32 ; i++ )
- if ( c->x86_capability & (1 << i) )
+ for ( i = 0 ; i < 32*NCAPINTS ; i++ )
+ if ( test_bit(i, &c->x86_capability) &&
+ x86_cap_flags[i] != NULL )
p += sprintf(p, " %s", x86_cap_flags[i]);
p += sprintf(p, "\nbogomips\t: %lu.%02lu\n\n",
return p - buffer;
}
-#ifndef CONFIG_X86_TSC
-static int tsc_disable __initdata = 0;
-
-static int __init tsc_setup(char *str)
-{
- tsc_disable = 1;
- return 1;
-}
-
-__setup("notsc", tsc_setup);
-#endif
-
static unsigned long cpu_initialized __initdata = 0;
/*
#ifndef CONFIG_X86_TSC
if (tsc_disable && cpu_has_tsc) {
printk("Disabling TSC...\n");
- boot_cpu_data.x86_capability &= ~X86_FEATURE_TSC;
+ /**** FIX-HPA: DOES THIS REALLY BELONG HERE? ****/
+ clear_bit(&boot_cpu_data.x86_capability, X86_FEATURE_TSC);
set_in_cr4(X86_CR4_TSD);
}
#endif
current->used_math = 0;
stts();
}
+
+/*
+ * Local Variables:
+ * mode:c
+ * c-file-style:"k&r"
+ * c-basic-offset:8
+ * End:
+ */
-# $Id: config.in,v 1.104 2000/10/04 09:01:38 anton Exp $
+# $Id: config.in,v 1.105 2000/11/12 10:01:41 davem Exp $
# For a description of the syntax of this configuration file,
# see Documentation/kbuild/config-language.txt.
#
-/* $Id: init.c,v 1.94 2000/10/19 00:49:51 davem Exp $
+/* $Id: init.c,v 1.95 2000/11/10 04:49:56 davem Exp $
* linux/arch/sparc/mm/init.c
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
-/* $Id: dtlb_base.S,v 1.7 2000/03/26 09:13:48 davem Exp $
+/* $Id: dtlb_base.S,v 1.8 2000/11/10 08:28:45 davem Exp $
* dtlb_base.S: Front end to DTLB miss replacement strategy.
* This is included directly into the trap table.
*
srax %g4, VPTE_SHIFT, %g6 ! Create VPTE offset
ldxa [%g3 + %g6] ASI_S, %g5 ! Load VPTE
1: brlz,pt %g5, 9f ! Valid, load into TLB
- and %g5, (_PAGE_PRESENT|_PAGE_READ), %g4 ! Mask readable bits
+ nop ! Delay-slot
ba,a,pt %xcc, 4f ! Invalid, branch out
/* DTLB ** ICACHE line 2: Quick kernel TLB misses */
nop
9: stxa %g5, [%g0] ASI_DTLB_DATA_IN ! Reload TLB
retry ! Trap return
- nop
+4: rdpr %pstate, %g5 ! Move into alternate globals
/* DTLB ** ICACHE line 3: winfixups+real_faults */
-4: cmp %g4, (_PAGE_PRESENT|_PAGE_READ) ! Readable page?
- be,pn %xcc, 5f ! Yep, refbit update
- sllx %g1, 60, %g4 ! Get valid bit
- rdpr %pstate, %g5 ! Move into alternate globals
wrpr %g5, PSTATE_AG|PSTATE_MG, %pstate
rdpr %tl, %g4 ! See where we came from.
cmp %g4, 1 ! Is etrap/rtrap window fault?
mov TLB_TAG_ACCESS, %g4 ! Prepare for fault processing
-
-/* DTLB ** ICACHE line 4: padding */
ldxa [%g4] ASI_DMMU, %g5 ! Load faulting VA page
be,pt %xcc, sparc64_realfault_common ! Jump to normal fault handling
mov FAULT_CODE_DTLB, %g4 ! It was read from DTLB
ba,a,pt %xcc, winfix_trampoline ! Call window fixup code
-5: or %g5, _PAGE_ACCESSED, %g5 ! Indicate reference
- or %g5, %g4, %g5 ! Set valid
- stxa %g5, [%g3 + %g6] ASI_S ! Update PTE table (cant trap)
- ba,a,pt %xcc, 9b ! Complete tlb miss
+
+/* DTLB ** ICACHE line 4: Unused... */
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
#undef TAG_CONTEXT_BITS
#undef VPTE_SHIFT
-/* $Id: dtlb_prot.S,v 1.20 2000/03/26 09:13:48 davem Exp $
+/* $Id: dtlb_prot.S,v 1.21 2000/11/10 08:28:45 davem Exp $
* dtlb_prot.S: DTLB protection trap strategy.
* This is included directly into the trap table.
*
* Copyright (C) 1997,1998 Jakub Jelinek (jj@ultra.linux.cz)
*/
-#define TAG_CONTEXT_BITS 0x3ff
-#define VPTE_SHIFT (PAGE_SHIFT - 3)
-#define MODIFIED_BITS (_PAGE_WRITE | _PAGE_W | _PAGE_MODIFIED | _PAGE_ACCESSED)
-
/* Ways we can get here:
*
* [TL == 0] 1) User stores to readonly pages.
*/
/* PROT ** ICACHE line 1: User DTLB protection trap */
- ldxa [%g1] ASI_DMMU, %g6 ! Primary or Secondary ctx?
- and %g6, 0x10, %g6 ! Get pri/sec ctx bit
stxa %g0, [%g1] ASI_DMMU ! Clear SFSR FaultValid bit
membar #Sync ! Synchronize ASI stores
- ldxa [%g1 + %g1] ASI_DMMU, %g4 ! Load TAG_ACCESS
- andn %g4, TAG_CONTEXT_BITS, %g4 ! Clear CTX bits
- stxa %g0, [%g4 + %g6] ASI_DMMU_DEMAP ! Perform TLB flush of page
- membar #Sync ! Synchronize ASI stores
-
-/* PROT ** ICACHE line 2: Further normal processing */
- srax %g4, VPTE_SHIFT, %g6 ! Compute VPTE offset
- ldxa [%g3 + %g6] ASI_S, %g5 ! Load PTE entry
- andcc %g5, _PAGE_WRITE, %g0 ! Writable page?
- be,pt %xcc, 1f ! Nope, real fault
- or %g5, (MODIFIED_BITS), %g5 ! Mark as writable/modified
- stxa %g5, [%g3 + %g6] ASI_S ! Update PTE entry
- stxa %g5, [%g0] ASI_DTLB_DATA_IN ! Load PTE into TLB
- retry ! Trap return
-
-/* PROT ** ICACHE line 3: Real user faults */
-1: rdpr %pstate, %g5 ! Move into alternate globals
+ rdpr %pstate, %g5 ! Move into alternate globals
wrpr %g5, PSTATE_AG|PSTATE_MG, %pstate
rdpr %tl, %g1 ! Need to do a winfixup?
cmp %g1, 1 ! Trap level >1?
mov TLB_TAG_ACCESS, %g4 ! Prepare reload of vaddr
+ nop
+
+/* PROT ** ICACHE line 2: More real fault processing */
bgu,pn %xcc, winfix_trampoline ! Yes, perform winfixup
ldxa [%g4] ASI_DMMU, %g5 ! Put tagaccess in %g5
ba,pt %xcc, sparc64_realfault_common ! Nope, normal fault
-
-/* PROT ** ICACHE line 4: More real fault processing */
mov FAULT_CODE_DTLB | FAULT_CODE_WRITE, %g4
nop
nop
nop
nop
+
+/* PROT ** ICACHE line 3: Unused... */
+ nop
+ nop
+ nop
+ nop
+ nop
nop
nop
nop
-#undef TAG_CONTEXT_BITS
-#undef VPTE_SHIFT
-#undef MODIFIED_BITS
+/* PROT ** ICACHE line 3: Unused... */
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
-/* $Id: itlb_base.S,v 1.9 2000/03/26 09:13:48 davem Exp $
+/* $Id: itlb_base.S,v 1.10 2000/11/10 08:28:45 davem Exp $
* itlb_base.S: Front end to ITLB miss replacement strategy.
* This is included directly into the trap table.
*
srax %g4, VPTE_SHIFT, %g6 ! Create VPTE offset
ldxa [%g3 + %g6] ASI_P, %g5 ! Load VPTE
1: brgez,pn %g5, 3f ! Not valid, branch out
- and %g5, (_PAGE_PRESENT|_PAGE_READ), %g4 ! Mask readable bits
+ nop ! Delay-slot
2: stxa %g5, [%g0] ASI_ITLB_DATA_IN ! Load PTE into TLB
retry ! Trap return
-3: cmp %g4, (_PAGE_PRESENT|_PAGE_READ) ! Readable page?
+3: rdpr %pstate, %g4 ! Move into alternate globals
-/* ITLB ** ICACHE line 2: Quick user ref updates */
- bne,pn %xcc, 4f ! Nope, real missing page
- sllx %g1, 60, %g4 ! Sliiickkk...
- or %g5, _PAGE_ACCESSED, %g5 ! Mark as touched
- or %g5, %g4, %g5 ! Allow user to see it
- ba,pt %xcc, 2b ! Branch to load TLB
- stxa %g5, [%g3 + %g6] ASI_S ! Update PTE table
-4: rdpr %pstate, %g4 ! Move into alternate globals
+/* ITLB ** ICACHE line 2: Real faults */
wrpr %g4, PSTATE_AG|PSTATE_MG, %pstate
-
-/* ITLB ** ICACHE line 3: Real faults */
rdpr %tpc, %g5 ! And load faulting VA
mov FAULT_CODE_ITLB, %g4 ! It was read from ITLB
sparc64_realfault_common: ! Called by TL0 dtlb_miss too
stx %g5, [%g6 + AOFF_task_thread + AOFF_thread_fault_address]
ba,pt %xcc, etrap ! Save state
1: rd %pc, %g7 ! ...
+ nop
+
+/* ITLB ** ICACHE line 3: Finish faults + window fixups */
call do_sparc64_fault ! Call fault handler
add %sp, STACK_BIAS + REGWIN_SZ, %o0! Compute pt_regs arg
-
-/* ITLB ** ICACHE line 4: Finish faults + window fixups */
ba,pt %xcc, rtrap_clr_l6 ! Restore cpu state
nop
winfix_trampoline:
or %g3, 0x7c, %g3 ! Compute offset to branch
wrpr %g3, %tnpc ! Write it into TNPC
done ! Do it to it
+
+/* ITLB ** ICACHE line 4: Unused... */
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
nop
nop
-/* $Id: sys_sparc32.c,v 1.165 2000/10/10 04:47:31 davem Exp $
+/* $Id: sys_sparc32.c,v 1.166 2000/11/10 04:49:56 davem Exp $
* sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
*
* Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
proc_read: ns_proc_read
};
static struct timer_list ns_timer;
-static char *mac[NS_MAX_CARDS] = { NULL
-#if NS_MAX_CARDS > 1
- , NULL
-#endif /* NS_MAX_CARDS > 1 */
-#if NS_MAX_CARDS > 2
- , NULL
-#endif /* NS_MAX_CARDS > 2 */
-#if NS_MAX_CARDS > 3
- , NULL
-#endif /* NS_MAX_CARDS > 3 */
-#if NS_MAX_CARDS > 4
- , NULL
-#endif /* NS_MAX_CARDS > 4 */
- };
-
-#ifdef MODULE
+static char *mac[NS_MAX_CARDS];
MODULE_PARM(mac, "1-" __MODULE_STRING(NS_MAX_CARDS) "s");
-#endif /* MODULE */
/* Functions*******************************************************************/
{
unsigned int sector, count;
int max_segments = MAX_SEGMENTS;
- struct request * req = NULL;
+ struct request * req = NULL, *freereq = NULL;
int rw_ahead, max_sectors, el_ret;
struct list_head *head = &q->queue_head;
int latency;
* Now we acquire the request spinlock, we have to be mega careful
* not to schedule or do something nonatomic
*/
+again:
spin_lock_irq(&io_request_lock);
/*
* are not crucial.
*/
get_rq:
- if ((req = get_request(q, rw)) == NULL) {
+ if (freereq) {
+ req = freereq;
+ freereq = NULL;
+ } else if ((req = get_request(q, rw)) == NULL) {
spin_unlock_irq(&io_request_lock);
if (rw_ahead)
goto end_io;
- req = __get_request_wait(q, rw);
- spin_lock_irq(&io_request_lock);
-
- if (q->head_active) {
- head = &q->queue_head;
- if (!q->plugged)
- head = head->next;
- }
+ freereq = __get_request_wait(q, rw);
+ goto again;
}
/* fill up the request-info, and add it to the queue */
out:
if (!q->plugged)
(q->request_fn)(q);
+ if (freereq)
+ blkdev_release_request(freereq);
spin_unlock_irq(&io_request_lock);
return 0;
end_io:
#endif
#ifdef CONFIG_ISP16_CDI
isp16_init();
-#endif CONFIG_ISP16_CDI
+#endif
#if defined(CONFIG_IDE) && defined(CONFIG_BLK_DEV_IDE)
ide_init(); /* this MUST precede hd_init */
#endif
#endif
#ifdef CONFIG_CDU31A
cdu31a_init();
-#endif CONFIG_CDU31A
+#endif
#ifdef CONFIG_ATARI_ACSI
acsi_init();
-#endif CONFIG_ATARI_ACSI
+#endif
#ifdef CONFIG_MCD
mcd_init();
-#endif CONFIG_MCD
+#endif
#ifdef CONFIG_MCDX
mcdx_init();
-#endif CONFIG_MCDX
+#endif
#ifdef CONFIG_SBPCD
sbpcd_init();
-#endif CONFIG_SBPCD
+#endif
#ifdef CONFIG_AZTCD
aztcd_init();
-#endif CONFIG_AZTCD
+#endif
#ifdef CONFIG_CDU535
sony535_init();
-#endif CONFIG_CDU535
+#endif
#ifdef CONFIG_GSCD
gscd_init();
-#endif CONFIG_GSCD
+#endif
#ifdef CONFIG_CM206
cm206_init();
#endif
#ifdef CONFIG_OPTCD
optcd_init();
-#endif CONFIG_OPTCD
+#endif
#ifdef CONFIG_SJCD
sjcd_init();
-#endif CONFIG_SJCD
+#endif
#ifdef CONFIG_APBLOCK
ap_init();
#endif
#endif
#ifdef CONFIG_BLK_DEV_LVM
lvm_init();
-#endif
+#endif
return 0;
};
-/* $Id: ffb_drv.c,v 1.6 2000/08/10 05:26:23 davem Exp $
+/* $Id: ffb_drv.c,v 1.7 2000/11/12 10:01:41 davem Exp $
* ffb_drv.c: Creator/Creator3D direct rendering driver.
*
* Copyright (C) 2000 David S. Miller (davem@redhat.com)
* best to be responsive. -- REW
* */
+#include <linux/module.h>
+#include <linux/kernel.h>
#include <linux/tty.h>
#include <linux/serial.h>
#include <linux/mm.h>
+#include <linux/generic_serial.h>
#include <asm/semaphore.h>
#include <asm/uaccess.h>
-#include <linux/version.h>
-#include <linux/module.h>
-#include <linux/generic_serial.h>
#define DEBUG
static char * tmp_buf;
static DECLARE_MUTEX(tmp_buf_sem);
-int gs_debug;
+static int gs_debug;
#ifdef DEBUG
#define RS_EVENT_WRITE_WAKEUP 1
-#ifdef MODULE
MODULE_PARM(gs_debug, "i");
-#endif
-
-#ifdef DEBUG
-static void my_hd (unsigned char *addr, int len)
-{
- int i, j, ch;
-
- for (i=0;i<len;i+=16) {
- printk ("%08x ", (int) addr+i);
- for (j=0;j<16;j++) {
- printk ("%02x %s", addr[j+i], (j==7)?" ":"");
- }
- for (j=0;j<16;j++) {
- ch = addr[j+i];
- printk ("%c", (ch < 0x20)?'.':((ch > 0x7f)?'.':ch));
- }
- printk ("\n");
- }
-}
-#else
-#define my_hd(addr,len)
-#endif
void gs_put_char(struct tty_struct * tty, unsigned char ch)
copy_to_user(sp, &sio, sizeof(struct serial_struct));
}
-
-#ifdef MODULE
-int init_module (void)
-{
- return 0;
-}
-
-int cleanup_module (void)
-{
- return 0;
-}
-#endif
+EXPORT_SYMBOL(gs_put_char);
+EXPORT_SYMBOL(gs_write);
+EXPORT_SYMBOL(gs_write_room);
+EXPORT_SYMBOL(gs_chars_in_buffer);
+EXPORT_SYMBOL(gs_flush_buffer);
+EXPORT_SYMBOL(gs_flush_chars);
+EXPORT_SYMBOL(gs_stop);
+EXPORT_SYMBOL(gs_start);
+EXPORT_SYMBOL(gs_hangup);
+EXPORT_SYMBOL(gs_do_softint);
+EXPORT_SYMBOL(block_til_ready);
+EXPORT_SYMBOL(gs_close);
+EXPORT_SYMBOL(gs_set_termios);
+EXPORT_SYMBOL(gs_init_port);
+EXPORT_SYMBOL(gs_setserial);
+EXPORT_SYMBOL(gs_getserial);
+
+EXPORT_SYMBOL(gs_debug);
#include <linux/gameport.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("Analog joystick and gamepad driver for Linux");
/*
* Option parsing.
*/
-MODULE_PARM(js,"1-16s");
-
#define ANALOG_PORTS 16
static char *js[ANALOG_PORTS];
static int analog_options[ANALOG_PORTS];
+MODULE_PARM(js, "1-" __MODULE_STRING(ANALOG_PORTS) "s");
+MODULE_PARM_DESC(js, "Analog joystick options");
/*
* Times, feature definitions.
* caching for the high addresses through the KEN pin, but
* we maintain the tradition of paranoia in this code.
*/
- return !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR)
- && addr >= __pa(high_memory);
+ return !( test_bit(X86_FEATURE_MTRR, &boot_cpu_data.x86_capability) ||
+ test_bit(X86_FEATURE_K6_MTRR, &boot_cpu_data.x86_capability) ||
+ test_bit(X86_FEATURE_CYRIX_ARR, &boot_cpu_data.x86_capability) ||
+ test_bit(X86_FEATURE_CENTAUR_MCR, &boot_cpu_data.x86_capability) )
+ && addr >= __pa(high_memory);
#else
return addr >= __pa(high_memory);
#endif
#define CI104J_ASIC_ID 5
enum {
- MXSER_BOARD_C168_ISA = 1,
+ MXSER_BOARD_C168_ISA = 0,
MXSER_BOARD_C104_ISA,
MXSER_BOARD_CI104J,
MXSER_BOARD_C168_PCI,
int entropy = 0;
#if defined (__i386__)
- if (boot_cpu_data.x86_capability & X86_FEATURE_TSC) {
+ if ( test_bit(X86_FEATURE_TSC, &boot_cpu_data.x86_capability) ) {
__u32 high;
__asm__(".byte 0x0f,0x31"
:"=a" (time), "=d" (high));
slot = lp->ppp_slot;
if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
printk(KERN_ERR "isdn_ppp_push_higher: lp->ppp_slot %d\n", lp->ppp_slot);
- kfree_skb(skb);
- return;
+ goto drop_packet;
}
is = ippp_table[slot];
slot = ((isdn_net_local *) (lp->master->priv))->ppp_slot;
if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
printk(KERN_ERR "isdn_ppp_push_higher: master->ppp_slot %d\n", lp->ppp_slot);
- kfree_skb(skb);
- return;
+ goto drop_packet;
}
}
mis = ippp_table[slot];
- if (mis != is) {
- printk(KERN_WARNING __FUNCTION__ ": BUG: is != mis %d %d %p %p\n", slot, lp->ppp_slot, mis, is);
- }
+
if (is->debug & 0x10) {
printk(KERN_DEBUG "push, skb %d %04x\n", (int) skb->len, proto);
isdn_ppp_frame_log("rpush", skb->data, skb->len, 32,is->unit,lp->ppp_slot);
case PPP_IPX: /* untested */
if (is->debug & 0x20)
printk(KERN_DEBUG "isdn_ppp: IPX\n");
- skb->dev = dev;
- skb->mac.raw = skb->data;
skb->protocol = htons(ETH_P_IPX);
break;
case PPP_IP:
if (is->debug & 0x20)
printk(KERN_DEBUG "isdn_ppp: IP\n");
- skb->dev = dev;
- skb->mac.raw = skb->data;
skb->protocol = htons(ETH_P_IP);
break;
#ifdef CONFIG_ISDN_PPP_VJ
printk(KERN_DEBUG "isdn_ppp: VJC_UNCOMP\n");
if (slhc_remember(ippp_table[net_dev->local->ppp_slot]->slcomp, skb->data, skb->len) <= 0) {
printk(KERN_WARNING "isdn_ppp: received illegal VJC_UNCOMP frame!\n");
- net_dev->local->stats.rx_dropped++;
- kfree_skb(skb);
- return;
+ goto drop_packet;
}
+ skb->protocol = htons(ETH_P_IP);
+ break;
case PPP_VJC_COMP:
if (is->debug & 0x20)
printk(KERN_DEBUG "isdn_ppp: VJC_COMP\n");
if (!skb) {
printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
- net_dev->local->stats.rx_dropped++;
- kfree_skb(skb_old);
- return;
+ skb = skb_old;
+ goto drop_packet;
}
- skb->dev = dev;
skb_put(skb, skb_old->len + 128);
memcpy(skb->data, skb_old->data, skb_old->len);
- skb->mac.raw = skb->data;
pkt_len = slhc_uncompress(ippp_table[net_dev->local->ppp_slot]->slcomp,
skb->data, skb_old->len);
kfree_skb(skb_old);
- if (pkt_len < 0) {
- kfree_skb(skb);
- lp->stats.rx_dropped++;
- return;
- }
+ if (pkt_len < 0)
+ goto drop_packet;
+
skb_trim(skb, pkt_len);
skb->protocol = htons(ETH_P_IP);
}
/* fall through */
default:
isdn_ppp_fill_rq(skb->data, skb->len, proto, lp->ppp_slot); /* push data to pppd device */
- dev_kfree_skb(skb);
+ kfree_skb(skb);
return;
}
/* Reset hangup-timer */
lp->huptimer = 0;
- netif_rx(skb);
- /* net_dev->local->stats.rx_packets++; *//* done in isdn_net.c */
+ skb->dev = dev;
+ skb->mac.raw = skb->data;
+ netif_rx(skb);
+ /* net_dev->local->stats.rx_packets++; done in isdn_net.c */
return;
+
+ drop_packet:
+ net_dev->local->stats.rx_dropped++;
+ kfree_skb(skb);
}
/*
#include <linux/module.h>
#include <linux/config.h>
-#include <linux/sysctl.h>
#include <linux/raid/md.h>
+#include <linux/sysctl.h>
#include <linux/raid/xor.h>
#include <linux/devfs_fs_kernel.h>
obj-$(CONFIG_VIDEO_DEV) += videodev.o
obj-$(CONFIG_BUS_I2C) += i2c-old.o
-obj-$(CONFIG_VIDEO_BT848) += bttv.o msp3400.o \
- tda7432.o tda8425.o tda985x.o tda9875.o tea6300.o tea6420.o tuner.o
+obj-$(CONFIG_VIDEO_BT848) += bttv.o msp3400.o tvaudio.o \
+ tda7432.o tda9875.o tuner.o
obj-$(CONFIG_SOUND_TVMIXER) += tvmixer.o
obj-$(CONFIG_VIDEO_ZR36120) += zoran.o i2c-old.o tuner.o saa7110.o saa7111.o saa7185.o
/* select from TV,radio,extern,MUTE */
#define AUDC_SET_INPUT _IOW('m',17,int)
+/* audio inputs */
+#define AUDIO_TUNER 0x00
+#define AUDIO_RADIO 0x01
+#define AUDIO_EXTERN 0x02
+#define AUDIO_INTERN 0x03
+#define AUDIO_OFF 0x04
+#define AUDIO_ON 0x05
+#define AUDIO_MUTE 0x80
+#define AUDIO_UNMUTE 0x81
+
/* all the stuff below is obsolete and just here for reference. I'll
* remove it once the driver is tested and works fine.
*
/*
bttv-cards.c -- this file has card-specific stuff
-
bttv - Bt848 frame grabber driver
Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
#include <linux/module.h>
#include <linux/kmod.h>
#include <linux/init.h>
+#include <linux/pci.h>
#include <asm/io.h>
-#include "bttv.h"
+#include "bttvp.h"
#include "tuner.h"
/* fwd decl */
static void hauppauge_eeprom(struct bttv *btv);
-static void hauppauge_boot_msp34xx(struct bttv *btv);
static void init_PXC200(struct bttv *btv);
+#if 0
static void init_tea5757(struct bttv *btv);
+#endif
+
+static void winview_audio(struct bttv *btv, struct video_audio *v, int set);
+static void avermedia_tvphone_audio(struct bttv *btv, struct video_audio *v,
+ int set);
+static void terratv_audio(struct bttv *btv, struct video_audio *v, int set);
+static void gvbctv3pci_audio(struct bttv *btv, struct video_audio *v, int set);
MODULE_PARM(card,"1-4i");
+MODULE_PARM_DESC(card,"specify TV/grabber card model, see CARDLIST file for a list");
MODULE_PARM(pll,"1-4i");
+MODULE_PARM_DESC(pll,"specify installed crystal (0=none, 28=28 MHz, 35=35 MHz)");
MODULE_PARM(autoload,"i");
+MODULE_PARM_DESC(autoload,"automatically load i2c modules like tuner.o, default is 1 (yes)");
static unsigned int card[4] = { -1, -1, -1, -1 };
static unsigned int pll[4] = { -1, -1, -1, -1 };
static unsigned int autoload = 0;
#endif
+MODULE_PARM(gpiomask,"i");
+MODULE_PARM(audioall,"i");
+MODULE_PARM(audiomux,"1-5i");
+static unsigned int gpiomask = -1;
+static unsigned int audioall = -1;
+static unsigned int audiomux[5] = { -1, -1, -1, -1, -1 };
+
+
/* ----------------------------------------------------------------------- */
/* list of card IDs for bt878+ cards */
{ 0x00011002, BTTV_HAUPPAUGE878, "ATI TV Wonder" },
{ 0x00011461, BTTV_AVPHONE98, "AVerMedia TVPhone98" },
{ 0x00021461, BTTV_AVERMEDIA98, "Avermedia TVCapture 98" },
+ { 0x00031002, BTTV_HAUPPAUGE878, "ATI TV Wonder/VE" },
{ 0x00031461, BTTV_AVPHONE98, "AVerMedia TVPhone98" },
- { 0x00041461, BTTV_AVPHONE98, "AVerMedia TVPhone98" },
+ { 0x00041461, BTTV_AVERMEDIA98, "AVerMedia TVCapture 98" },
{ 0x10b42636, BTTV_HAUPPAUGE878, "STB ???" },
{ 0x1118153b, BTTV_TERRATVALUE, "Terratec TV Value" },
{ 0x1123153b, BTTV_TERRATVRADIO, "Terratec TV/Radio+" },
{ 0x18521852, BTTV_TYPHOON_TVIEW, "Typhoon TView TV/FM Tuner" },
{ 0x217d6606, BTTV_WINFAST2000, "Leadtek WinFast TV 2000" },
{ 0x263610b4, BTTV_STB2, "STB TV PCI FM, P/N 6000704" },
+ { 0x3000121a, 0/* no entry yet */,"VoodooTV 200" },
{ 0x3000144f, BTTV_MAGICTVIEW063, "TView 99 (CPH063)" },
{ 0x300014ff, BTTV_MAGICTVIEW061, "TView 99 (CPH061)" },
{ 0x3002144f, BTTV_MAGICTVIEW061, "Askey Magic TView" },
{ 0x300214ff, BTTV_PHOEBE_TVMAS, "Phoebe TV Master" },
+ { 0x39000070, BTTV_HAUPPAUGE878, "Hauppauge WinTV-D" },
{ 0x400a15b0, BTTV_ZOLTRIX_GENIE, "Zoltrix Genie TV" },
- { 0x402010fc, 0 /* no tvcards entry yet */, "I-O Data Co. GV-BCV3/PCI" },
+ { 0x401015b0, BTTV_ZOLTRIX_GENIE, "Zoltrix Genie TV / Radio" },
+ { 0x402010fc, BTTV_GVBCTV3PCI, "I-O Data Co. GV-BCV3/PCI" },
+ { 0xff000070, BTTV_HAUPPAUGE878, "Osprey-100" },
+ { 0xff010070, BTTV_HAUPPAUGE878, "Osprey-200" },
#if 0 /* probably wrong */
{ 0x14610002, BTTV_AVERMEDIA98, "Avermedia TVCapture 98" },
{ 0x6606217d, BTTV_WINFAST2000, "Leadtek WinFast TV 2000" },
/* ----------------------------------------------------------------------- */
/* array with description for bt848 / bt878 tv/grabber cards */
-struct tvcard bttv_tvcards[] =
+struct tvcard bttv_tvcards[] = {
{
- /* 0x00 */
- { " *** UNKNOWN *** ",
- 3, 1, 0, 2, 0, { 2, 3, 1, 1}, { 0, 0, 0, 0, 0},0,
- 1,1,1,1,0,0,0,1, PLL_NONE, -1 },
- { "MIRO PCTV",
- 4, 1, 0, 2,15, { 2, 3, 1, 1}, { 2, 0, 0, 0,10},0,
- 1,1,1,1,0,0,0,1, PLL_NONE, -1 },
- { "Hauppauge old",
- 4, 1, 0, 2, 7, { 2, 3, 1, 1}, { 0, 1, 2, 3, 4},0,
- 1,1,0,1,0,0,0,1, PLL_NONE, -1 },
- { "STB",
- 3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 4, 0, 2, 3, 1},0,
- 0,1,1,1,1,0,0,1, PLL_NONE, -1 },
-
- { "Intel",
- 3, 1, 0, -1, 7, { 2, 3, 1, 1}, { 0, 1, 2, 3, 4},0,
- 1,1,1,1,0,0,0,1, PLL_NONE, -1 },
- { "Diamond DTV2000",
- 3, 1, 0, 2, 3, { 2, 3, 1, 1}, { 0, 1, 0, 1, 3},0,
- 1,1,1,1,0,0,0,1, PLL_NONE, -1 },
- { "AVerMedia TVPhone",
- 3, 1, 0, 3,15, { 2, 3, 1, 1}, {12, 4,11,11, 0},0,
- 1,1,1,1,0,0,0,1, PLL_28, -1 },
- { "MATRIX-Vision MV-Delta",
- 5, 1, -1, 3, 0, { 2, 3, 1, 0, 0},{0 }, 0,
- 1,1,1,1,0,0,0,1, PLL_NONE, -1 },
-
- /* 0x08 */
- { "Fly Video II",
- 3, 1, 0, 2, 0xc00, { 2, 3, 1, 1},
- { 0, 0xc00, 0x800, 0x400, 0xc00, 0},0,
- 1,1,1,1,0,0,0,1, PLL_NONE, -1 },
- { "TurboTV",
- 3, 1, 0, 2, 3, { 2, 3, 1, 1}, { 1, 1, 2, 3, 0},0,
- 1,1,1,1,0,0,0,1, PLL_NONE, -1 },
- { "Hauppauge new (bt878)",
- 4, 1, 0, 2, 7, { 2, 0, 1, 1}, { 0, 1, 2, 3, 4},0,
- 1,1,0,1,0,0,0,1, PLL_28, -1 },
- { "MIRO PCTV pro",
- 3, 1, 0, 2, 65551, { 2, 3, 1, 1}, {1,65537, 0, 0,10},0,
- /* 3, 1, 0, 2, 0x3004F, { 2, 3, 1, 1}, {1, 0x10011, 5, 0,10}, 0x3004F, */
- 1,1,1,1,0,0,0,1, PLL_NONE, -1 },
-
- { "ADS Technologies Channel Surfer TV",
- 3, 1, 2, 2, 15, { 2, 3, 1, 1}, { 13, 14, 11, 7, 0, 0},0,
- 1,1,1,1,0,0,0,1, PLL_NONE, -1 },
- { "AVerMedia TVCapture 98",
- 3, 4, 0, 2, 15, { 2, 3, 1, 1}, { 13, 14, 11, 7, 0, 0},0,
- 1,1,1,1,0,0,0,1, PLL_28, -1 },
- { "Aimslab VHX",
- 3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 0, 1, 2, 3, 4},0,
- 1,1,1,1,0,0,0,1, PLL_NONE, -1 },
- { "Zoltrix TV-Max",
- 3, 1, 0, 2,15, { 2, 3, 1, 1}, {0 , 0, 1 , 0, 10},0,
- 1,1,1,1,0,0,0,1, PLL_NONE, -1 },
-
- /* 0x10 */
- { "Pixelview PlayTV (bt878)",
- 3, 1, 0, 2, 0x01fe00, { 2, 3, 1, 1},
- { 0x01c000, 0, 0x018000, 0x014000, 0x002000, 0 },0,
- 1,1,1,1,0,0,0,1, PLL_28, -1 },
- { "Leadtek WinView 601",
- 3, 1, 0, 2, 0x8300f8, { 2, 3, 1, 1,0},
- { 0x4fa007,0xcfa007,0xcfa007,0xcfa007,0xcfa007,0xcfa007},0,
- 1,1,1,1,0,0,0,1, PLL_NONE, -1 },
- { "AVEC Intercapture",
- 3, 2, 0, 2, 0, {2, 3, 1, 1}, {1, 0, 0, 0, 0},0,
- 1,1,1,1,0,0,0,1, PLL_NONE, -1 },
- { "LifeView FlyKit w/o Tuner",
- 3, 1, -1, -1, 0x8dff00, { 2, 3, 1, 1}, { 0 },0,
- 0,0,0,0,0,0,0,1, PLL_NONE, -1 },
-
- { "CEI Raffles Card",
- 3, 3, 0, 2, 0, {2, 3, 1, 1}, {0, 0, 0, 0 ,0},0,
- 1,1,1,1,0,0,0,1, PLL_NONE, -1 },
- { "Lucky Star Image World ConferenceTV",
- 3, 1, 0, 2, 0x00fffe07, { 2, 3, 1, 1}, { 131072, 1, 1638400, 3, 4},0,
- 1,1,1,1,0,0,0,1, PLL_28, TUNER_PHILIPS_PAL_I },
- { "Phoebe Tv Master + FM",
- 3, 1, 0, 2, 0xc00, { 2, 3, 1, 1},{0, 1, 0x800, 0x400, 0xc00, 0},0,
- 1,1,1,1,0,0,0,1, PLL_NONE, -1 },
- { "Modular Technology MM205 PCTV, bt878",
- 2, 1, 0, -1, 7, { 2, 3 }, { 0, 0, 0, 0, 0 },0,
- 1,1,1,1,0,0,0,1, PLL_NONE, -1 },
-
- /* 0x18 */
- { "Askey/Typhoon/Anubis Magic TView CPH051/061 (bt878)",
- 3, 1, 0, 2, 0xe00, { 2, 3, 1, 1}, {0x400, 0x400, 0x400, 0x400, 0},0,
- 1,1,1,1,0,0,0,1, PLL_28, -1 },
- { "Terratec/Vobis TV-Boostar",
- 3, 1, 0, 2, 16777215 , { 2, 3, 1, 1}, { 131072, 1, 1638400, 3,4},0,
- 1,1,1,1,0,0,0,1, PLL_NONE, -1 },
- { "Newer Hauppauge WinCam (bt878)",
- 4, 1, 0, 3, 7, { 2, 0, 1, 1}, { 0, 1, 2, 3, 4},0,
- 1,1,1,1,0,0,0,1, PLL_NONE, -1 },
- { "MAXI TV Video PCI2",
- 3, 1, 0, 2, 0xffff, { 2, 3, 1, 1}, { 0, 1, 2, 3, 0xc00},0,
- 1,1,1,1,0,0,0,1, PLL_NONE, TUNER_PHILIPS_SECAM },
-
- { "Terratec TerraTV+",
- 3, 1, 0, 2, 0x70000, { 2, 3, 1, 1},
- { 0x20000, 0x30000, 0x00000, 0x10000, 0x40000},0,
- 1,1,1,1,0,0,0,1, PLL_NONE, -1 },
- { "Imagenation PXC200",
- 5, 1, -1, 4, 0, { 2, 3, 1, 0, 0}, { 0 }, 0,
- 1,1,1,1,0,0,0,1, PLL_NONE, -1 },
- { "FlyVideo 98",
- 3, 1, 0, 2, 0x8dff00, {2, 3, 1, 1},
- { 0, 0x8dff00, 0x8df700, 0x8de700, 0x8dff00, 0 },0,
- 1,1,1,1,0,0,0,1, PLL_NONE, -1 },
- { "iProTV",
- 3, 1, 0, 2, 1, { 2, 3, 1, 1}, { 1, 0, 0, 0, 0 },0,
- 1,1,1,1,0,0,0,1, PLL_NONE, -1 },
-
- /* 0x20 */
- { "Intel Create and Share PCI",
- 4, 1, 0, 2, 7, { 2, 3, 1, 1}, { 4, 4, 4, 4, 4},0,
- 1,1,1,1,0,0,0,1, PLL_NONE, -1 },
- { "Terratec TerraTValue",
- 3, 1, 0, 2, 0xffff00, { 2, 3, 1, 1},
- { 0x500, 0, 0x300, 0x900, 0x900},0,
- 1,1,1,1,0,0,0,1, PLL_NONE, -1 },
- { "Leadtek WinFast 2000",
- 3, 1, 0, 2, 0xfff000, { 2, 3, 1, 1,0},
- { 0x621000,0x6ddf07,0x621100,0x620000,0xE210000,0x620000},0,
- 1,1,1,1,1,0,0,1, PLL_28, -1 },
- { "Chronos Video Shuttle II",
- 3, 3, 0, 2, 0x1800, { 2, 3, 1, 1}, { 0, 0, 0x1000, 0x1000, 0x0800},0,
- 1,1,1,1,0,0,0,1, PLL_28, -1 },
-
- { "Typhoon TView TV/FM Tuner",
- 3, 3, 0, 2, 0x1800, { 2, 3, 1, 1}, { 0, 0x800, 0, 0, 0x1800, 0 },0,
- 1,1,1,1,0,0,0,1, PLL_28, -1 },
- { "PixelView PlayTV pro",
- 3, 1, 0, 2, 0xff, { 2, 3, 1, 1 },
- { 0x21, 0x20, 0x24, 0x2c, 0x29, 0x29 }, 0,
- 0,0,0,0,0,0,0,1, PLL_28, -1 },
- { "TView99 CPH063",
- 3, 1, 0, 2, 0x551e00, { 2, 3, 1, 1},
- { 0x551400, 0x551200, 0, 0, 0, 0x551200 }, 0,
- 1,1,1,1,0,0,0,1, PLL_28, -1 },
- { "Pinnacle PCTV Rave",
- 3, 1, 0, 2, 0x03000F, { 2, 3, 1, 1}, { 2, 0, 0, 0, 1},0,
- 1,1,1,1,0,0,0,1, PLL_28, -1 },
-
- /* 0x28 */
- { "STB2",
- 3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 4, 0, 2, 3, 1},0,
- 0,1,1,1,0,1,1,1, PLL_NONE, -1 },
- { "AVerMedia TVPhone 98",
- 3, 4, 0, 2, 4, { 2, 3, 1, 1}, { 13, 14, 11, 7, 0, 0},0,
- 1,1,1,1,0,0,0,1, PLL_28, 5 },
- { "ProVideo PV951", /* pic16c54 */
- 3, 1, 0, 2, 0, { 2, 3, 1, 1}, { 0, 0, 0, 0, 0},0,
- 0,0,0,0,0,0,0,0, PLL_28, 1 },
- { "Little OnAir TV",
- 3, 1, 0, 2, 0xe00b, {2, 3, 1, 1},
- {0xff9ff6, 0xff9ff6, 0xff1ff7, 0, 0xff3ffc},0,
- 0,0,0,0,0,0,0,0, PLL_NONE, -1 },
-
- { "Sigma TVII-FM",
- 2, 1, 0, -1, 3, {2, 3, 1, 1}, {1, 1, 0, 2, 3},0,
- 0,0,0,0,0,0,0,0, PLL_NONE, -1 },
- { "MATRIX-Vision MV-Delta 2",
- 5, 1, -1, 3, 0, { 2, 3, 1, 0, 0},{0 }, 0,
- 0,0,0,0,0,0,0,0, PLL_28, -1 },
- { "Zoltrix Genie TV",
- 3, 1, 0, 2, 0xbcf03f, { 2, 3, 1, 1},
- { 0xbc803f, 0, 0xbcb03f, 0, 0xbcb03f}, 0,
- 0,0,0,0,0,0,0,0, PLL_28, 5 },
- { "Terratec TV/Radio+", /* Radio ?? */
- 3, 1, 0, 2, 0x1f0000, { 2, 3, 1, 1},
- { 0xe2ffff, 0xebffff, 0, 0, 0xe0ffff, 0xe2ffff },0,
- 0,0,0,0,0,0,0,0, PLL_35, 1 },
-
- /* 0x30 */
- { "Dynalink Magic TView ",
- 3, 1, 0, 2, 15, { 2, 3, 1, 1}, {2,0,0,0,1},0,
- 1,1,1,1,0,0,0,1, PLL_28, -1 },
-};
+/* ---- card 0x00 ---------------------------------- */
+ name: " *** UNKNOWN *** ",
+ video_inputs: 4,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ muxsel: { 2, 3, 1, 0},
+ tuner_type: -1,
+},{
+ name: "MIRO PCTV",
+ video_inputs: 4,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 15,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 2, 0, 0, 0, 10},
+ needs_tvaudio: 1,
+ tuner_type: -1,
+},{
+ name: "Hauppauge old",
+ video_inputs: 4,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 7,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 0, 1, 2, 3, 4},
+ needs_tvaudio: 1,
+ tuner_type: -1,
+},{
+ name: "STB",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 7,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 4, 0, 2, 3, 1},
+ no_msp34xx: 1,
+ needs_tvaudio: 1,
+ tuner_type: -1,
+},{
+
+/* ---- card 0x04 ---------------------------------- */
+ name: "Intel",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: -1,
+ gpiomask: 7,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 0, 1, 2, 3, 4},
+ needs_tvaudio: 1,
+ tuner_type: -1,
+},{
+ name: "Diamond DTV2000",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 3,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 0, 1, 0, 1, 3},
+ needs_tvaudio: 1,
+ tuner_type: -1,
+},{
+ name: "AVerMedia TVPhone",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 3,
+ gpiomask: 15,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: {12, 4,11,11, 0},
+ needs_tvaudio: 1,
+ pll: PLL_28,
+ tuner_type: -1,
+},{
+ name: "MATRIX-Vision MV-Delta",
+ video_inputs: 5,
+ audio_inputs: 1,
+ tuner: -1,
+ svhs: 3,
+ gpiomask: 0,
+ muxsel: { 2, 3, 1, 0, 0},
+ audiomux: {0 },
+ needs_tvaudio: 1,
+ tuner_type: -1,
+},{
+
+/* ---- card 0x08 ---------------------------------- */
+ name: "Fly Video II",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 0xc00,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 0, 0xc00, 0x800, 0x400, 0xc00, 0},
+ needs_tvaudio: 1,
+ tuner_type: -1,
+},{
+ name: "TurboTV",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 3,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 1, 1, 2, 3, 0},
+ needs_tvaudio: 1,
+ tuner_type: -1,
+},{
+ name: "Hauppauge new (bt878)",
+ video_inputs: 4,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 7,
+ muxsel: { 2, 0, 1, 1},
+ audiomux: { 0, 1, 2, 3, 4},
+ needs_tvaudio: 1,
+ pll: PLL_28,
+ tuner_type: -1,
+},{
+ name: "MIRO PCTV pro",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 65551,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: {1,65537, 0, 0,10},
+ needs_tvaudio: 1,
+ tuner_type: -1,
+},{
+
+/* ---- card 0x0c ---------------------------------- */
+ name: "ADS Technologies Channel Surfer TV",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 15,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 13, 14, 11, 7, 0, 0},
+ needs_tvaudio: 1,
+ tuner_type: -1,
+},{
+ name: "AVerMedia TVCapture 98",
+ video_inputs: 3,
+ audio_inputs: 4,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 15,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 13, 14, 11, 7, 0, 0},
+ needs_tvaudio: 1,
+ pll: PLL_28,
+ tuner_type: 5,
+},{
+ name: "Aimslab VHX",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 7,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 0, 1, 2, 3, 4},
+ needs_tvaudio: 1,
+ tuner_type: -1,
+},{
+ name: "Zoltrix TV-Max",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 15,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: {0 , 0, 1 , 0, 10},
+ needs_tvaudio: 1,
+ tuner_type: -1,
+},{
+
+/* ---- card 0x10 ---------------------------------- */
+ name: "Pixelview PlayTV (bt878)",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 0x01fe00,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 0x01c000, 0, 0x018000, 0x014000, 0x002000, 0 },
+ needs_tvaudio: 1,
+ pll: PLL_28,
+ tuner_type: -1,
+},{
+ name: "Leadtek WinView 601",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 0x8300f8,
+ muxsel: { 2, 3, 1, 1,0},
+ audiomux: { 0x4fa007,0xcfa007,0xcfa007,0xcfa007,0xcfa007,0xcfa007},
+ needs_tvaudio: 1,
+ tuner_type: -1,
+ audio_hook: winview_audio,
+},{
+ name: "AVEC Intercapture",
+ video_inputs: 3,
+ audio_inputs: 2,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 0,
+ muxsel: {2, 3, 1, 1},
+ audiomux: {1, 0, 0, 0, 0},
+ needs_tvaudio: 1,
+ tuner_type: -1,
+},{
+ name: "LifeView FlyKit w/o Tuner",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: -1,
+ svhs: -1,
+ gpiomask: 0x8dff00,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 0 },
+ no_msp34xx: 1,
+ tuner_type: -1,
+},{
+
+/* ---- card 0x14 ---------------------------------- */
+ name: "CEI Raffles Card",
+ video_inputs: 3,
+ audio_inputs: 3,
+ tuner: 0,
+ svhs: 2,
+ muxsel: {2, 3, 1, 1},
+ tuner_type: -1,
+},{
+ name: "Lucky Star Image World ConferenceTV",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 0x00fffe07,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 131072, 1, 1638400, 3, 4},
+ needs_tvaudio: 1,
+ pll: PLL_28,
+ tuner_type: TUNER_PHILIPS_PAL_I,
+},{
+ name: "Phoebe Tv Master + FM",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 0xc00,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: {0, 1, 0x800, 0x400, 0xc00, 0},
+ needs_tvaudio: 1,
+ tuner_type: -1,
+},{
+ name: "Modular Technology MM205 PCTV, bt878",
+ video_inputs: 2,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: -1,
+ gpiomask: 7,
+ muxsel: { 2, 3 },
+ audiomux: { 0, 0, 0, 0, 0 },
+ no_msp34xx: 1,
+ needs_tvaudio: 1,
+ tuner_type: -1,
+},{
+
+/* ---- card 0x18 ---------------------------------- */
+ name: "Askey/Typhoon/Anubis Magic TView CPH051/061 (bt878)",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 0xe00,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: {0x400, 0x400, 0x400, 0x400, 0},
+ needs_tvaudio: 1,
+ pll: PLL_28,
+ tuner_type: -1,
+},{
+ name: "Terratec/Vobis TV-Boostar",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 16777215,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 131072, 1, 1638400, 3,4},
+ needs_tvaudio: 1,
+ tuner_type: -1,
+},{
+ name: "Newer Hauppauge WinCam (bt878)",
+ video_inputs: 4,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 3,
+ gpiomask: 7,
+ muxsel: { 2, 0, 1, 1},
+ audiomux: { 0, 1, 2, 3, 4},
+ needs_tvaudio: 1,
+ tuner_type: -1,
+},{
+ name: "MAXI TV Video PCI2",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 0xffff,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 0, 1, 2, 3, 0xc00},
+ needs_tvaudio: 1,
+ tuner_type: TUNER_PHILIPS_SECAM,
+},{
+
+/* ---- card 0x1c ---------------------------------- */
+ name: "Terratec TerraTV+",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 0x70000,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 0x20000, 0x30000, 0x00000, 0x10000, 0x40000},
+ needs_tvaudio: 1,
+ tuner_type: -1,
+ audio_hook: terratv_audio,
+},{
+ name: "Imagenation PXC200",
+ video_inputs: 5,
+ audio_inputs: 1,
+ tuner: -1,
+ svhs: 4,
+ gpiomask: 0,
+ muxsel: { 2, 3, 1, 0, 0},
+ audiomux: { 0 },
+ needs_tvaudio: 1,
+ tuner_type: -1,
+},{
+ name: "FlyVideo 98",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 0x8dfe00,
+ muxsel: {2, 3, 1, 1},
+ audiomux: { 0, 0x8dff00, 0x8df700, 0x8de700, 0x8dff00, 0 },
+ needs_tvaudio: 1,
+ tuner_type: -1,
+},{
+ name: "iProTV",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 1,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 1, 0, 0, 0, 0 },
+ tuner_type: -1,
+},{
+
+/* ---- card 0x20 ---------------------------------- */
+ name: "Intel Create and Share PCI",
+ video_inputs: 4,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 7,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 4, 4, 4, 4, 4},
+ needs_tvaudio: 1,
+ tuner_type: -1,
+},{
+ name: "Terratec TerraTValue",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 0xffff00,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 0x500, 0, 0x300, 0x900, 0x900},
+ needs_tvaudio: 1,
+ tuner_type: -1,
+},{
+ name: "Leadtek WinFast 2000",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 0xfff000,
+ muxsel: { 2, 3, 1, 1,0},
+ audiomux: { 0x621000,0x6ddf07,0x621100,0x620000,0xE210000,0x620000},
+ needs_tvaudio: 1,
+ pll: PLL_28,
+ tuner_type: -1,
+},{
+ name: "Chronos Video Shuttle II",
+ video_inputs: 3,
+ audio_inputs: 3,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 0x1800,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 0, 0, 0x1000, 0x1000, 0x0800},
+ needs_tvaudio: 1,
+ pll: PLL_28,
+ tuner_type: -1,
+},{
+
+/* ---- card 0x24 ---------------------------------- */
+ name: "Typhoon TView TV/FM Tuner",
+ video_inputs: 3,
+ audio_inputs: 3,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 0x1800,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 0, 0x800, 0, 0, 0x1800, 0 },
+ needs_tvaudio: 1,
+ pll: PLL_28,
+ tuner_type: -1,
+},{
+ name: "PixelView PlayTV pro",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 0xff,
+ muxsel: { 2, 3, 1, 1 },
+ audiomux: { 0x21, 0x20, 0x24, 0x2c, 0x29, 0x29 },
+ no_msp34xx: 1,
+ pll: PLL_28,
+ tuner_type: -1,
+},{
+ name: "TView99 CPH063",
+ video_inputs: 4,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 0x551e00,
+ muxsel: { 2, 3, 1, 0},
+ audiomux: { 0x551400, 0x551200, 0, 0, 0, 0x551200 },
+ needs_tvaudio: 1,
+ pll: PLL_28,
+ tuner_type: -1,
+},{
+ name: "Pinnacle PCTV Rave",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 0x03000F,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 2, 0, 0, 0, 1},
+ needs_tvaudio: 1,
+ pll: PLL_28,
+ tuner_type: -1,
+},{
+
+/* ---- card 0x28 ---------------------------------- */
+ name: "STB2",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 7,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 4, 0, 2, 3, 1},
+ no_msp34xx: 1,
+ needs_tvaudio: 1,
+ tuner_type: -1,
+},{
+ name: "AVerMedia TVPhone 98",
+ video_inputs: 3,
+ audio_inputs: 4,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 4,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 13, 14, 11, 7, 0, 0},
+ needs_tvaudio: 1,
+ pll: PLL_28,
+ tuner_type: 5,
+ audio_hook: avermedia_tvphone_audio,
+},{
+ name: "ProVideo PV951", /* pic16c54 */
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 0,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 0, 0, 0, 0, 0},
+ no_msp34xx: 1,
+ pll: PLL_28,
+ tuner_type: 1,
+},{
+ name: "Little OnAir TV",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 0xe00b,
+ muxsel: {2, 3, 1, 1},
+ audiomux: {0xff9ff6, 0xff9ff6, 0xff1ff7, 0, 0xff3ffc},
+ no_msp34xx: 1,
+ tuner_type: -1,
+},{
+
+/* ---- card 0x2c ---------------------------------- */
+ name: "Sigma TVII-FM",
+ video_inputs: 2,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: -1,
+ gpiomask: 3,
+ muxsel: {2, 3, 1, 1},
+ audiomux: {1, 1, 0, 2, 3},
+ no_msp34xx: 1,
+ pll: PLL_NONE,
+ tuner_type: -1,
+},{
+ name: "MATRIX-Vision MV-Delta 2",
+ video_inputs: 5,
+ audio_inputs: 1,
+ tuner: -1,
+ svhs: 3,
+ gpiomask: 0,
+ muxsel: { 2, 3, 1, 0, 0},
+ audiomux: {0 },
+ no_msp34xx: 1,
+ pll: PLL_28,
+ tuner_type: -1,
+},{
+ name: "Zoltrix Genie TV",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 0xbcf03f,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 0xbc803f, 0, 0xbcb03f, 0, 0xbcb03f},
+ no_msp34xx: 1,
+ pll: PLL_28,
+ tuner_type: 5,
+},{
+ name: "Terratec TV/Radio+",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 0x1f0000,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 0xe2ffff, 0xebffff, 0, 0, 0xe0ffff, 0xe2ffff },
+ no_msp34xx: 1,
+ pll: PLL_35,
+ tuner_type: 1,
+},{
+
+/* ---- card 0x30 ---------------------------------- */
+ name: "Dynalink Magic TView ",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 15,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: {2,0,0,0,1},
+ needs_tvaudio: 1,
+ pll: PLL_28,
+ tuner_type: -1,
+},{
+ name: "GV-BCTV3",
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 0x010f00,
+ muxsel: {2, 3, 0, 0},
+ audiomux: {0x10000, 0, 0x10000, 0, 0, 0},
+ no_msp34xx: 1,
+ pll: PLL_28,
+ tuner_type: TUNER_ALPS_TSHC6_NTSC,
+ audio_hook: gvbctv3pci_audio,
+},{
+ name: "Prolink PV-BT878P+4E (PixelView PlayTV PAK)",
+ video_inputs: 4,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 0xAA0000,
+ muxsel: { 2,3,1,1 },
+ audiomux: { 0x20000, 0, 0x80000, 0x80000, 0xa8000, 0x46000 },
+ no_msp34xx: 1,
+ pll: PLL_28,
+ tuner_type: TUNER_PHILIPS_PAL_I,
+},{
+ name: "Eagle Wireless Capricorn2 (bt878A)",
+ video_inputs: 4,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 7,
+ muxsel: { 2, 0, 1, 1},
+ audiomux: { 0, 1, 2, 3, 4},
+ pll: PLL_28,
+ tuner_type: -1 /* TUNER_ALPS_TMDH2_NTSC */,
+},{
+
+/* ---- card 0x34 ---------------------------------- */
+ name: "Pinnacle Studio PCTV Pro", /* David Härdeman <david@2gen.com> */
+ video_inputs: 3,
+ audio_inputs: 1,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 0x03000F,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 1, 65537, 0, 0, 10},
+ needs_tvaudio: 1,
+ pll: PLL_28,
+ tuner_type: -1,
+},{
+ name: "Typhoon TView RDS", /* Claas Langbehn <claas@bigfoot.com> */
+ video_inputs: 3,
+ audio_inputs: 3,
+ tuner: 0,
+ svhs: 2,
+ gpiomask: 0xffff,
+ muxsel: { 2, 3, 1, 1},
+ audiomux: { 0xb002, 0, 0x12, 0x12, 0x3007 },
+ needs_tvaudio: 1,
+ pll: PLL_28,
+ tuner_type: TUNER_PHILIPS_PAL_I,
+}};
+
const int bttv_num_tvcards = (sizeof(bttv_tvcards)/sizeof(struct tvcard));
/* ----------------------------------------------------------------------- */
static unsigned char eeprom_data[256];
-static void __devinit bttv_dump_eeprom(struct bttv *btv,int addr)
-{
- int i;
-
- if (bttv_verbose < 2)
- return;
- /* for debugging: dump eeprom to syslog */
- printk(KERN_DEBUG "bttv%d: dump eeprom @ 0x%02x\n",btv->nr,addr);
- for (i = 0; i < 256;) {
- printk(KERN_DEBUG " %02x:",i);
- do {
- printk(" %02x",eeprom_data[i++]);
- } while (i % 16);
- printk("\n");
- }
-}
-
-static int __devinit bttv_idcard_eeprom(struct bttv *btv)
-{
- unsigned id;
- int i,n;
-
- id = (eeprom_data[252] << 24) |
- (eeprom_data[253] << 16) |
- (eeprom_data[254] << 8) |
- (eeprom_data[255]);
- if (id == 0 || id == 0xffffffff)
- return -1;
-
- /* look for the card */
- btv->cardid = id;
- for (n = -1, i = 0; cards[i].id != 0; i++)
- if (cards[i].id == id)
- n = i;
-
- if (n != -1) {
- /* found it */
- printk(KERN_INFO "bttv%d: card id: %s (0x%08x) => card=%d\n",
- btv->nr,cards[n].name,id,cards[n].cardnr);
- return cards[n].cardnr;
- } else {
- /* 404 */
- printk(KERN_INFO "bttv%d: id: unknown (0x%08x)\n",
- btv->nr, id);
- printk(KERN_INFO "please mail id, board name and "
- "the correct card= insmod option to "
- "kraxel@goldbach.in-berlin.de\n");
- return -1;
- }
-}
-
-#ifndef HAVE_TVAUDIO
-/* can tda9855.c handle this too maybe? */
-static void __devinit init_tda9840(struct bttv *btv)
-{
- /* Horrible Hack */
- bttv_I2CWrite(btv, I2C_TDA9840, TDA9840_SW, 0x2a, 1); /* sound mode switching */
- /* 00 - mute
- 10 - mono / averaged stereo
- 2a - stereo
- 12 - dual A
- 1a - dual AB
- 16 - dual BA
- 1e - dual B
- 7a - external */
-}
-#endif
-
+/*
+ * identify card
+ */
void __devinit bttv_idcard(struct bttv *btv)
{
- int type,eeprom = 0;
-
- btwrite(0, BT848_GPIO_OUT_EN);
-
- /* try to autodetect the card */
- /* many bt878 cards have a eeprom @ 0xa0 => read ID
- and try to identify it */
- if (bttv_I2CRead(btv, I2C_HAUPEE, "eeprom") >= 0) {
- eeprom = 0xa0;
- bttv_readee(btv,eeprom_data,0xa0);
- bttv_dump_eeprom(btv,0xa0); /* DEBUG */
- type = bttv_idcard_eeprom(btv);
- if (-1 != type) {
- btv->type = type;
- } else if (btv->id <= 849) {
- /* for unknown bt848, assume old Hauppauge */
- btv->type=BTTV_HAUPPAUGE;
- }
+ unsigned int gpiobits;
+ int i,type;
+ unsigned short tmp;
+
+ /* read PCI subsystem ID */
+ pci_read_config_word(btv->dev, PCI_SUBSYSTEM_ID, &tmp);
+ btv->cardid = tmp << 16;
+ pci_read_config_word(btv->dev, PCI_SUBSYSTEM_VENDOR_ID, &tmp);
+ btv->cardid |= tmp;
+
+ if (0 != btv->cardid && 0xffffffff != btv->cardid) {
+ /* look for the card */
+ for (type = -1, i = 0; cards[i].id != 0; i++)
+ if (cards[i].id == btv->cardid)
+ type = i;
- /* STB cards have a eeprom @ 0xae (old bt848) */
- } else if (bttv_I2CRead(btv, I2C_STBEE, "eeprom")>=0) {
- btv->type=BTTV_STB;
+ if (type != -1) {
+ /* found it */
+ printk(KERN_INFO "bttv%d: subsystem: %04x:%04x => %s => card=%d\n",
+ btv->nr, btv->cardid & 0xffff, btv->cardid >> 16,
+ cards[type].name,cards[type].cardnr);
+ btv->type = cards[type].cardnr;
+ } else {
+ /* 404 */
+ printk(KERN_INFO "bttv%d: subsystem: %04x:%04x (UNKNOWN)\n",
+ btv->nr, btv->cardid&0xffff, btv->cardid>>16);
+ printk(KERN_DEBUG "please mail id, board name and "
+ "the correct card= insmod option to "
+ "kraxel@goldbach.in-berlin.de\n");
+ }
}
/* let the user override the autodetected type */
printk(KERN_INFO "bttv%d: model: %s [%s]\n",btv->nr,btv->video_dev.name,
(card[btv->nr] >= 0 && card[btv->nr] < bttv_num_tvcards) ?
"insmod option" : "autodetected");
-
- /* board specific initialisations */
+
+ /* overwrite gpio stuff ?? */
+ if (-1 == audioall && -1 == audiomux[0])
+ return;
+
+ if (-1 != audiomux[0]) {
+ gpiobits = 0;
+ for (i = 0; i < 5; i++) {
+ bttv_tvcards[btv->type].audiomux[i] = audiomux[i];
+ gpiobits |= audiomux[i];
+ }
+ } else {
+ gpiobits = audioall;
+ for (i = 0; i < 5; i++) {
+ bttv_tvcards[btv->type].audiomux[i] = audioall;
+ }
+ }
+ bttv_tvcards[btv->type].gpiomask = (-1 != gpiomask) ? gpiomask : gpiobits;
+ printk(KERN_INFO "bttv%d: gpio config override: mask=0x%x, mux=",
+ btv->nr,bttv_tvcards[btv->type].gpiomask);
+ for (i = 0; i < 5; i++) {
+ printk("%s0x%x", i ? "," : "", bttv_tvcards[btv->type].audiomux[i]);
+ }
+ printk("\n");
+}
+
+/*
+ * (most) board specific initialisations goes here
+ */
+void __devinit bttv_init_card(struct bttv *btv)
+{
+ int eeprom = 0;
+
if (btv->type == BTTV_MIRO || btv->type == BTTV_MIROPRO) {
/* auto detect tuner for MIRO cards */
btv->tuner_type=((btread(BT848_GPIO_DATA)>>10)-1)&7;
bttv_readee(btv,eeprom_data,0xa0);
}
hauppauge_eeprom(btv);
- hauppauge_boot_msp34xx(btv);
}
if (btv->type == BTTV_PXC200)
init_PXC200(btv);
btv->pll.pll_ifreq=28636363;
btv->pll.pll_crystal=BT848_IFORM_XT0;
}
+ if (PLL_35 == bttv_tvcards[btv->type].pll) {
+ btv->pll.pll_ifreq=35468950;
+ btv->pll.pll_crystal=BT848_IFORM_XT1;
+ }
/* insmod options can override */
switch (pll[btv->nr]) {
case 0: /* none */
btv->pll.pll_ofreq = 0;
break;
case 1: /* 28 MHz */
+ case 28:
btv->pll.pll_ifreq = 28636363;
btv->pll.pll_ofreq = 0;
- btv->pll.pll_crystal=BT848_IFORM_XT0;
+ btv->pll.pll_crystal = BT848_IFORM_XT0;
break;
case 2: /* 35 MHz */
+ case 35:
btv->pll.pll_ifreq = 35468950;
btv->pll.pll_ofreq = 0;
- btv->pll.pll_crystal=BT848_IFORM_XT1;
+ btv->pll.pll_crystal = BT848_IFORM_XT1;
break;
}
}
-
/* tuner configuration */
if (-1 != bttv_tvcards[btv->type].tuner_type)
bttv_call_i2c_clients(btv,TUNER_SET_TYPE,&btv->tuner_type);
/* try to detect audio/fader chips */
- if (bttv_tvcards[btv->type].msp34xx &&
+ if (!bttv_tvcards[btv->type].no_msp34xx &&
bttv_I2CRead(btv, I2C_MSP3400, "MSP34xx") >=0) {
if (autoload)
request_module("msp3400");
}
-#ifndef HAVE_TVAUDIO
- if (bttv_tvcards[btv->type].tda8425 &&
- bttv_I2CRead(btv, I2C_TDA8425, "TDA8425") >=0) {
- if (autoload)
- request_module("tda8425");
- }
-
- if (bttv_tvcards[btv->type].tda9840 &&
- bttv_I2CRead(btv, I2C_TDA9840, "TDA9840") >=0) {
- init_tda9840(btv);
- btv->audio_chip = TDA9840;
- /* move this to a module too? */
- init_tda9840(btv);
- }
-
- if (bttv_tvcards[btv->type].tda985x &&
- bttv_I2CRead(btv, I2C_TDA9850, "TDA985x") >=0) {
- if (autoload)
- request_module("tda985x");
- }
- if (bttv_tvcards[btv->type].tea63xx) {
- if (autoload)
- request_module("tea6300");
- }
-#else
- if (bttv_tvcards[btv->type].tda8425 ||
- bttv_tvcards[btv->type].tda9840 ||
- bttv_tvcards[btv->type].tda985x ||
- bttv_tvcards[btv->type].tea63xx) {
- if (autoload)
- request_module("tvaudio");
- }
-#endif
-
- if (bttv_tvcards[btv->type].tda9875 &&
- bttv_I2CRead(btv, I2C_TDA9875, "TDA9875") >=0) {
+ if (bttv_I2CRead(btv, I2C_TDA9875, "TDA9875") >=0) {
if (autoload)
request_module("tda9875");
}
- if (bttv_tvcards[btv->type].tda7432 &&
- bttv_I2CRead(btv, I2C_TDA7432, "TDA7432") >=0) {
+ if (bttv_I2CRead(btv, I2C_TDA7432, "TDA7432") >=0) {
if (autoload)
request_module("tda7432");
}
-
- if (bttv_tvcards[btv->type].tea64xx) {
+ if (bttv_tvcards[btv->type].needs_tvaudio) {
if (autoload)
- request_module("tea6420");
+ request_module("tvaudio");
}
if (bttv_tvcards[btv->type].tuner != -1) {
{
btv->tuner_type = hauppauge_tuner[eeprom_data[9]].id;
if (bttv_verbose)
- printk("bttv%d: Hauppauge eeprom: tuner=%s (%d)\n",btv->nr,
+ printk("bttv%d: Hauppauge eeprom: model=%d, tuner=%s (%d)\n",btv->nr,
+ eeprom_data[12] << 8 | eeprom_data[11],
hauppauge_tuner[eeprom_data[9]].name,btv->tuner_type);
}
}
-static void __devinit hauppauge_boot_msp34xx(struct bttv *btv)
+void __devinit bttv_hauppauge_boot_msp34xx(struct bttv *btv)
{
- int i;
-
/* reset/enable the MSP on some Hauppauge cards */
/* Thanks to Kyösti Mälkki (kmalkki@cc.hut.fi)! */
btaor(32, ~32, BT848_GPIO_OUT_EN);
btaor(0, ~32, BT848_GPIO_DATA);
udelay(2500);
btaor(32, ~32, BT848_GPIO_DATA);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"msp34xx");
if (bttv_verbose)
printk("bttv%d: Hauppauge msp34xx: reset line init\n",btv->nr);
-
- /* look if the msp3400 driver is already registered */
- for (i = 0; i < I2C_CLIENTS_MAX; i++) {
- if (btv->i2c_clients[i] != NULL &&
- btv->i2c_clients[i]->driver->id == I2C_DRIVERID_MSP3400) {
- return;
- }
- }
-
- /* if not: look for the chip ... */
- if (bttv_I2CRead(btv, I2C_MSP3400, "MSP34xx")) {
- /* ... if found re-register to trigger a i2c bus rescan, */
- /* this time with the msp34xx chip activated */
- i2c_bit_del_bus(&btv->i2c_adap);
- i2c_bit_add_bus(&btv->i2c_adap);
- }
}
/* GPIO inputs are pulled up, so no need to drive
* reset pin any longer */
btwrite(0,BT848_GPIO_OUT_EN);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"pxc200");
/* we could/should try and reset/control the AD pots? but
right now we simply turned off the crushing. Without
/* better safe than sorry */
btaor((1<<TEA_CLK) | (1<<TEA_WE), ~((1<<TEA_CLK) | (1<<TEA_DATA) | (1<<TEA_WE) | (1<<TEA_MOST)), BT848_GPIO_OUT_EN);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"miro tea read");
BUS_LOW(WE);
BUS_LOW(CLK);
int reg = value;
btaor((1<<TEA_CLK) | (1<<TEA_WE) | (1<<TEA_DATA), ~((1<<TEA_CLK) | (1<<TEA_DATA) | (1<<TEA_WE) | (1<<TEA_MOST)), BT848_GPIO_OUT_EN);
- if (bttv_debug) printk("tea5757: write 0x%X\n", value);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"miro tea write");
+ if (bttv_debug)
+ printk("tea5757: write 0x%X\n", value);
BUS_LOW(CLK);
BUS_HIGH(WE);
for(i = 0; i < 25; i++)
if (bttv_debug) tea_read(btv);
}
+#if 0
void init_tea5757(struct bttv *btv)
{
BUS_LOW(CLK);
/* normal mode for GPIO */
btaor(0, BT848_GPIO_DMA_CTL_GPIOMODE, BT848_GPIO_DMA_CTL);
}
+#endif
/* ----------------------------------------------------------------------- */
/* winview */
-void winview_setvol(struct bttv *btv, struct video_audio *v)
+void winview_audio(struct bttv *btv, struct video_audio *v, int set)
{
/* PT2254A programming Jon Tombs, jon@gte.esi.us.es */
int bits_out, loops, vol, data;
+
+ if (!set) {
+ v->mode |= VIDEO_AUDIO_VOLUME;
+ return;
+ }
/* 32 levels logarithmic */
vol = 32 - ((v->volume>>11));
btwrite(data, BT848_GPIO_DATA);
}
+/* ----------------------------------------------------------------------- */
+/* mono/stereo control for various cards (which don't use i2c chips but */
+/* connect something to the GPIO pins */
+
+static void
+gvbctv3pci_audio(struct bttv *btv, struct video_audio *v, int set)
+{
+ unsigned int con = 0;
+
+ if (set) {
+ btor(0x100, BT848_GPIO_OUT_EN);
+ if (v->mode & VIDEO_SOUND_LANG1)
+ con = 0x000;
+ if (v->mode & VIDEO_SOUND_LANG2)
+ con = 0x300;
+ if (v->mode & VIDEO_SOUND_STEREO)
+ con = 0x200;
+// if (v->mode & VIDEO_SOUND_MONO)
+// con = 0x100;
+ btaor(con, ~0x300, BT848_GPIO_DATA);
+ } else {
+ v->mode = VIDEO_SOUND_STEREO |
+ VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
+ }
+}
+
+/*
+ * Mario Medina Nussbaum <medisoft@alohabbs.org.mx>
+ * I discover that on BT848_GPIO_DATA address a byte 0xcce enable stereo,
+ * 0xdde enables mono and 0xccd enables sap
+ *
+ * Petr Vandrovec <VANDROVE@vc.cvut.cz>
+ * P.S.: At least mask in line above is wrong - GPIO pins 3,2 select
+ * input/output sound connection, so both must be set for output mode.
+ *
+ */
+static void
+avermedia_tvphone_audio(struct bttv *btv, struct video_audio *v, int set)
+{
+ /* TODO */
+}
+
+static void
+terratv_audio(struct bttv *btv, struct video_audio *v, int set)
+{
+ unsigned int con = 0;
+
+ if (set) {
+ btor(0x180000, BT848_GPIO_OUT_EN);
+ if (v->mode & VIDEO_SOUND_LANG2)
+ con = 0x080000;
+ if (v->mode & VIDEO_SOUND_STEREO)
+ con = 0x180000;
+ btaor(con, ~0x180000, BT848_GPIO_DATA);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"terratv");
+ } else {
+ v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
+ VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
+ }
+}
+
/*
* Local variables:
* c-basic-offset: 8
#include <linux/kmod.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
-#ifdef HACKING
-# include <linux/iobuf.h>
-#endif
-#include "bttv.h"
+#include "bttvp.h"
#include "tuner.h"
#define DEBUG(x) /* Debug driver */
-#define IDEBUG(x) /* Debug interrupt handler */
#define MIN(a,b) (((a)>(b))?(b):(a))
#define MAX(a,b) (((a)>(b))?(a):(b))
-
static void bt848_set_risc_jmps(struct bttv *btv, int state);
int bttv_num; /* number of Bt848s in use */
struct bttv bttvs[BTTV_MAX];
-
/* insmod args */
MODULE_PARM(triton1,"i");
MODULE_PARM(radio,"1-4i");
+MODULE_PARM_DESC(radio,"The TV card supports radio, default is 0 (no)");
MODULE_PARM(bigendian,"i");
+MODULE_PARM_DESC(bigendian,"byte order of the framebuffer, default is native endian");
MODULE_PARM(fieldnr,"i");
+MODULE_PARM_DESC(fieldnr,"count fields, default is 0 (no)");
MODULE_PARM(bttv_verbose,"i");
+MODULE_PARM_DESC(bttv_verbose,"verbose startup messages, default is 1 (yes)");
+MODULE_PARM(bttv_gpio,"i");
+MODULE_PARM_DESC(bttv_gpio,"log gpio changes, default is 0 (no)");
MODULE_PARM(bttv_debug,"i");
+MODULE_PARM_DESC(bttv_debug,"debug messages, default is 0 (no)");
+MODULE_PARM(irq_debug,"i");
+MODULE_PARM_DESC(irq_debug,"irq handler debug messages, default is 0 (no)");
MODULE_PARM(gbuffers,"i");
+MODULE_PARM_DESC(gbuffers,"number of capture buffers, default is 2 (64 max)");
MODULE_PARM(gbufsize,"i");
+MODULE_PARM_DESC(gbufsize,"size of the capture buffers, default is 0x208000");
+MODULE_PARM(combfilter,"i");
MODULE_DESCRIPTION("bttv - v4l driver module for bt848/878 based cards");
MODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr");
static int triton1=0;
static unsigned int radio[BTTV_MAX];
static unsigned int fieldnr = 0;
+static unsigned int irq_debug = 0;
static unsigned int gbuffers = 2;
static unsigned int gbufsize = BTTV_MAX_FBUF;
+static unsigned int combfilter = 0;
unsigned int bttv_debug = 0;
unsigned int bttv_verbose = 1;
+unsigned int bttv_gpio = 0;
#define I2C_TIMING (0x7<<4)
#define I2C_DELAY 10
ptep = pte_offset(pmd, adr);
pte = *ptep;
if(pte_present(pte)) {
- ret = (unsigned long) page_address(pte_page(pte));
- ret |= (adr & (PAGE_SIZE - 1));
+ ret = (unsigned long) page_address(pte_page(pte));
+ ret |= (adr & (PAGE_SIZE - 1));
+
}
}
}
}
+/* init + register i2c algo-bit adapter */
static int __devinit init_bttv_i2c(struct bttv *btv)
{
- /* i2c bit_adapter */
memcpy(&btv->i2c_adap, &bttv_i2c_adap_template, sizeof(struct i2c_adapter));
memcpy(&btv->i2c_algo, &bttv_i2c_algo_template, sizeof(struct i2c_algo_bit_data));
memcpy(&btv->i2c_client, &bttv_i2c_client_template, sizeof(struct i2c_client));
bttv_bit_setscl(btv,1);
bttv_bit_setsda(btv,1);
- btv->i2c_ok = i2c_bit_add_bus(&btv->i2c_adap);
- return btv->i2c_ok;
+ btv->i2c_rc = i2c_bit_add_bus(&btv->i2c_adap);
+ return btv->i2c_rc;
}
/* ----------------------------------------------------------------------- */
+void bttv_gpio_tracking(struct bttv *btv, char *comment)
+{
+ unsigned int outbits, data;
+ outbits = btread(BT848_GPIO_OUT_EN);
+ data = btread(BT848_GPIO_DATA);
+ printk(KERN_DEBUG "bttv%d: gpio: en=%08x, out=%08x in=%08x [%s]\n",
+ btv->nr,outbits,data & outbits, data & ~outbits, comment);
+}
+
+static char *audio_modes[] = { "audio: tuner", "audio: radio", "audio: extern",
+ "audio: intern", "audio: off" };
+
static void audio(struct bttv *btv, int mode, int no_irq_context)
{
btaor(bttv_tvcards[btv->type].gpiomask, ~bttv_tvcards[btv->type].gpiomask,
mode = AUDIO_RADIO;
btaor(bttv_tvcards[btv->type].audiomux[mode],
~bttv_tvcards[btv->type].gpiomask, BT848_GPIO_DATA);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,audio_modes[mode]);
if (no_irq_context)
bttv_call_i2c_clients(btv,AUDC_SET_INPUT,&(mode));
}
static void bt848_muxsel(struct bttv *btv, unsigned int input)
{
+
+#if 0 /* seems no card uses this ... */
btaor(bttv_tvcards[btv->type].gpiomask2,~bttv_tvcards[btv->type].gpiomask2,
BT848_GPIO_OUT_EN);
+#endif
/* This seems to get rid of some synchronization problems */
btand(~(3<<5), BT848_IFORM);
mdelay(10);
-
input %= bttv_tvcards[btv->type].video_inputs;
if (input==bttv_tvcards[btv->type].svhs)
{
btaor((bttv_tvcards[btv->type].muxsel[input&7]&3)<<5, ~(3<<5), BT848_IFORM);
audio(btv, (input!=bttv_tvcards[btv->type].tuner) ?
AUDIO_EXTERN : AUDIO_TUNER, 1);
+
+#if 0 /* seems no card uses this ... */
btaor(bttv_tvcards[btv->type].muxsel[input]>>4,
~bttv_tvcards[btv->type].gpiomask2, BT848_GPIO_DATA);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"muxsel");
+#endif
}
lastbit=cbit;
sx += dx;
dx = 1;
- if (ro - btv->risc_scr_odd>RISCMEM_LEN/2 - 16)
+ if (ro - btv->risc_scr_odd>(RISCMEM_LEN>>3) - 16)
outofmem++;
- if (re - btv->risc_scr_even>RISCMEM_LEN/2 - 16)
+ if (re - btv->risc_scr_even>(RISCMEM_LEN>>3) - 16)
outofmem++;
}
x++;
((tvn->sheight>>4)&0x30)|((tvn->vdelay>>2)&0xc0);
vscale |= inter ? (BT848_VSCALE_INT<<8) : 0;
-#if 0
- /* Some people say interpolation looks bad ... */
- vtc = (width < 193) ? 2 : ((width < 385) ? 1 : 0);
- if (width < 767)
- btor(BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off);
- else
+ if (combfilter) {
+ /* Some people say interpolation looks bad ... */
+ vtc = (width < 193) ? 2 : ((width < 385) ? 1 : 0);
+ if (width < 769)
+ btor(BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off);
+ else
+ btand(~BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off);
+ } else {
+ vtc = 0;
btand(~BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off);
-#else
- vtc = 0;
- btand(~BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off);
-#endif
+ }
btwrite(vtc, BT848_E_VTC+off);
btwrite(hscale>>8, BT848_E_HSCALE_HI+off);
bt848_set_geo(btv,1);
}
-#ifdef HACKING
-/* playing with kiobufs and dma-to-userspace. 2.4.x only
- Yes, I know: cut+paste programming is ugly.
- will fix later, this is proof-of-concept right now. */
-static int make_vrisctab_kiobuf(struct bttv *btv, unsigned int *ro,
- unsigned int *re, struct kiobuf *iobuf,
- unsigned short width, unsigned short height,
- unsigned short palette)
-{
- unsigned long bpl; /* bytes per line */
- unsigned long bl;
- unsigned long todo;
- unsigned long pageaddr;
- unsigned int **rp;
- unsigned long line,inter,offset,page;
-
- inter = (height>tvnorms[btv->win.norm].sheight/2) ? 1 : 0;
- bpl=width*fmtbppx2[palette2fmt[palette]&0xf]/2;
-
- *(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
- *(ro++)=cpu_to_le32(0);
- *(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
- *(re++)=cpu_to_le32(0);
-
- offset = iobuf->offset;
- page = 0;
- pageaddr = virt_to_bus(page_address(iobuf->maplist[page]));
- for (line=0; line < (height<<(1^inter)); line++)
- {
- if (inter)
- rp= (line&1) ? &re : &ro;
- else
- rp= (line>=height) ? &ro : &re;
-
- bl = PAGE_SIZE - offset;
- if (bpl <= bl) {
- *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL|
- BT848_RISC_EOL|bpl);
- *((*rp)++)=cpu_to_le32(pageaddr+offset);
- offset+=bpl;
- } else {
- todo = bpl;
- *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL|bl);
- *((*rp)++)=cpu_to_le32(pageaddr+offset);
- todo -= bl;
- offset = 0;
- page++;
- pageaddr = virt_to_bus(page_address(iobuf->maplist[page]));
- while (todo>PAGE_SIZE)
- {
- *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|PAGE_SIZE);
- *((*rp)++)=cpu_to_le32(pageaddr);
- page++;
- pageaddr = virt_to_bus(page_address(iobuf->maplist[page]));
- }
- *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_EOL|todo);
- *((*rp)++)=cpu_to_le32(pageaddr);
- offset += todo;
- }
- }
-
- *(ro++)=cpu_to_le32(BT848_RISC_JUMP);
- *(ro++)=cpu_to_le32(btv->bus_vbi_even);
- *(re++)=cpu_to_le32(BT848_RISC_JUMP|BT848_RISC_IRQ|(2<<16));
- *(re++)=cpu_to_le32(btv->bus_vbi_odd);
-
- return 0;
-}
-
-static int vgrab_kiobuf(struct bttv *btv, struct bttv_just_hacking *mp,
- struct kiobuf *iobuf)
-{
- unsigned int *ro, *re;
- unsigned long flags;
-
- if(btv->gbuf[0].stat != GBUFFER_UNUSED)
- return -EBUSY;
-
- if(mp->height < 32 || mp->width < 32)
- return -EINVAL;
- if (mp->format >= 12 /* more is'nt yet ... PALETTEFMT_MAX */)
- return -EINVAL;
-
- if(-1 == palette2fmt[mp->format])
- return -EINVAL;
-
- /*
- * Ok load up the BT848
- */
-
- ro=btv->gbuf[0].risc;
- re=ro+2048;
- make_vrisctab_kiobuf(btv, ro, re, iobuf, mp->width, mp->height, mp->format);
-
- if (bttv_debug)
- printk("bttv%d: cap vgrab_kiobuf: queue %d (%d:%dx%d)\n",
- btv->nr,0,mp->format,mp->width,mp->height);
- spin_lock_irqsave(&btv->s_lock, flags);
- btv->gbuf[0].stat = GBUFFER_GRABBING;
- btv->gbuf[0].fmt = palette2fmt[mp->format];
- btv->gbuf[0].width = mp->width;
- btv->gbuf[0].height = mp->height;
- btv->gbuf[0].ro = virt_to_bus(ro);
- btv->gbuf[0].re = virt_to_bus(re);
-
-#if 1
- if (mp->height <= tvnorms[btv->win.norm].sheight/2 &&
- mp->format != VIDEO_PALETTE_RAW)
- btv->gbuf[0].ro = 0;
-#endif
-
- if (-1 == btv->gq_grab && btv->gq_in == btv->gq_out) {
- btv->gq_start = 1;
- btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP|(0x8<<16)|BT848_RISC_IRQ);
- }
- btv->gqueue[btv->gq_in++] = 0;
- btv->gq_in = btv->gq_in % MAX_GBUFFERS;
-
- btor(3, BT848_CAP_CTL);
- btor(3, BT848_GPIO_DMA_CTL);
- spin_unlock_irqrestore(&btv->s_lock, flags);
- return 0;
-}
-#endif
-
/*
* Grab into virtual memory.
*/
tvnorms[2].hdelayx1 = 186 - (on?BURSTOFFSET :0);
}
+/*
+ * called from irq handler on fatal errors. Takes the grabber chip
+ * offline, flag it needs a reinitialization (which can't be done
+ * from irq context) and wake up all sleeping proccesses. They would
+ * block forever else. We also need someone who actually does the
+ * reinitialization from process context...
+ */
+static void bt848_offline(struct bttv *btv)
+{
+ int i;
+ spin_lock(&btv->s_lock);
+
+ /* cancel all outstanding grab requests */
+ btv->gq_in = 0;
+ btv->gq_out = 0;
+ btv->gq_grab = -1;
+ for (i = 0; i < gbuffers; i++)
+ if (btv->gbuf[i].stat == GBUFFER_GRABBING)
+ btv->gbuf[i].stat = GBUFFER_ERROR;
+
+ /* disable screen overlay and DMA */
+ btv->risc_cap_odd = 0;
+ btv->risc_cap_even = 0;
+ bt848_set_risc_jmps(btv,0);
+
+ /* flag the chip needs a restart */
+ btv->needs_restart = 1;
+ spin_unlock(&btv->s_lock);
+
+ wake_up_interruptible(&btv->vbiq);
+ wake_up_interruptible(&btv->capq);
+}
+
static void bt848_restart(struct bttv *btv)
{
unsigned long irq_flags;
int i,ret;
ret = -EBUSY;
+ if (bttv_debug)
+ printk("bttv%d: open called\n",btv->nr);
MOD_INC_USE_COUNT;
down(&btv->lock);
btv->scr_on = 0;
bt848_set_risc_jmps(btv,-1);
spin_unlock_irqrestore(&btv->s_lock, irq_flags);
+ up(&btv->lock);
return -EINVAL;
}
if (btv->win.bpp < 4)
* Do any clips.
*/
if(vw.clipcount<0) {
- if((vcp=vmalloc(VIDEO_CLIPMAP_SIZE))==NULL)
+ if((vcp=vmalloc(VIDEO_CLIPMAP_SIZE))==NULL) {
+ up(&btv->lock);
return -ENOMEM;
+ }
if(copy_from_user(vcp, vw.clips,
VIDEO_CLIPMAP_SIZE)) {
+ up(&btv->lock);
vfree(vcp);
return -EFAULT;
}
} else if (vw.clipcount) {
if((vcp=vmalloc(sizeof(struct video_clip)*
- (vw.clipcount))) == NULL)
+ (vw.clipcount))) == NULL) {
+ up(&btv->lock);
return -ENOMEM;
+ }
if(copy_from_user(vcp,vw.clips,
sizeof(struct video_clip)*
vw.clipcount)) {
+ up(&btv->lock);
vfree(vcp);
return -EFAULT;
}
v.mode = VIDEO_SOUND_MONO;
bttv_call_i2c_clients(btv,cmd,&v);
- if (btv->type == BTTV_TERRATV) {
- v.mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
- VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
- }
-#ifndef HAVE_TVAUDIO
- else if (btv->audio_chip == TDA9840) {
- /* begin of Horrible Hack <grin@tolna.net> */
- v.flags|=VIDEO_AUDIO_VOLUME;
- v.mode = VIDEO_SOUND_MONO;
- v.mode |= VIDEO_SOUND_STEREO;
- v.mode |= VIDEO_SOUND_LANG1|VIDEO_SOUND_LANG2;
- v.volume = 32768; /* fixme */
- v.step = 4096;
- }
-#endif
+ /* card specific hooks */
+ if (bttv_tvcards[btv->type].audio_hook)
+ bttv_tvcards[btv->type].audio_hook(btv,&v,0);
if(copy_to_user(arg,&v,sizeof(v)))
return -EFAULT;
bttv_call_i2c_clients(btv,cmd,&v);
- if (btv->type == BTTV_TERRATV) {
- unsigned int con = 0;
- btor(0x180000, BT848_GPIO_OUT_EN);
- if (v.mode & VIDEO_SOUND_LANG2)
- con = 0x080000;
- if (v.mode & VIDEO_SOUND_STEREO)
- con = 0x180000;
- btaor(con, ~0x180000, BT848_GPIO_DATA);
-
- } else if (btv->type == BTTV_WINVIEW_601)
- winview_setvol(btv,&v);
+ /* card specific hooks */
+ if (bttv_tvcards[btv->type].audio_hook)
+ bttv_tvcards[btv->type].audio_hook(btv,&v,0);
btv->audio_dev=v;
up(&btv->lock);
return 0;
}
-#ifdef HACKING
- /* playing with kiobufs and dma-to-userspace */
- case BTTV_JUST_HACKING:
- {
- DECLARE_WAITQUEUE(wait, current);
- struct bttv_just_hacking hack;
- struct kiobuf *iobuf;
- int err;
-
- if(copy_from_user((void *) &hack, (void *) arg, sizeof(hack)))
- return -EFAULT;
- printk("bttv%d: hack: userland args: %dx%d, fmt=%d, buf=%lx, len=%d\n",
- btv->nr,hack.width,hack.height,hack.format,
- hack.buf,hack.len);
-
- /* pin down */
- err = alloc_kiovec(1,&iobuf);
- if (err)
- goto hack_oops;
- err = map_user_kiobuf(READ, iobuf, hack.buf, hack.len);
- if (err)
- goto hack_oops;
- err = lock_kiovec(1,&iobuf,1);
- if (err)
- goto hack_oops;
-
- /* have a look */
- printk("bttv%d: hack: kiobuf: nr_pages=%d, offset=%d, length=%d, locked=%d\n",
- btv->nr,iobuf->nr_pages,iobuf->offset,iobuf->length,
- iobuf->locked);
- printk("bttv%d: hack: pages (bus addr):",btv->nr);
- for (i = 0; i < iobuf->nr_pages; i++) {
- printk(" %lx", virt_to_bus(page_address(iobuf->maplist[i])));
- }
- printk("\n");
-
- /* start capture */
- err = -EINVAL;
- if (hack.height * hack.width * 2 * /* fixme: *2 */
- fmtbppx2[palette2fmt[hack.format]&0x0f]/2 > hack.len)
- goto hack_oops;
- err = vgrab_kiobuf(btv,&hack,iobuf);
- if (err)
- goto hack_oops;
- printk("bttv%d: hack: capture started\n",btv->nr);
-
- /* wait */
- add_wait_queue(&btv->capq, &wait);
- current->state = TASK_INTERRUPTIBLE;
- while(btv->gbuf[0].stat==GBUFFER_GRABBING) {
- if (bttv_debug)
- printk("bttv%d: hack: cap sync: sleep on %d\n",btv->nr,0);
- schedule();
-#if 0
- if(signal_pending(current)) {
- remove_wait_queue(&btv->capq, &wait);
- current->state = TASK_RUNNING;
- return -EINTR;
- }
-#endif
- }
- remove_wait_queue(&btv->capq, &wait);
- current->state = TASK_RUNNING;
- printk("bttv%d: hack: capture done\n",btv->nr);
-
- /* release */
- err = 0;
- hack_oops:
- free_kiovec(1,&iobuf);
- return 0;
- }
-#endif
-
default:
return -ENOIOCTLCMD;
}
}
case VIDIOCGFREQ:
case VIDIOCSFREQ:
- return bttv_ioctl((struct video_device *)btv,cmd,arg);
+ case BTTV_VERSION:
+ return bttv_ioctl(dev,cmd,arg);
case BTTV_VBISIZE:
/* make alevt happy :-) */
return VBIBUF_SIZE;
v.rangehigh=(int)(108*16); /* eu: 87.5MHz - 108.0MHz */
v.flags= 0; /* XXX */
v.mode = 0; /* XXX */
+ bttv_call_i2c_clients(btv,cmd,&v);
if(copy_to_user(arg,&v,sizeof(v)))
return -EFAULT;
return 0;
printk(" e%d=%08x",btv->gq_grab,btv->risc_cap_odd);
flags |= 3;
btv->risc_jmp[5]=cpu_to_le32(btv->risc_cap_odd);
- } else if (flags&2) {
+ } else if ((flags&2) &&
+ (!btv->win.interlace || 0 == btv->risc_cap_even)) {
if (bttv_debug > 1)
printk(" eo=%08lx",virt_to_bus(btv->risc_scr_odd));
btv->risc_jmp[5]=cpu_to_le32(virt_to_bus(btv->risc_scr_odd));
printk(" o%d=%08x",btv->gq_grab,btv->risc_cap_even);
flags |= 3;
btv->risc_jmp[11]=cpu_to_le32(btv->risc_cap_even);
- } else if (flags&1) {
+ } else if ((flags&1) &&
+ btv->win.interlace) {
if (bttv_debug > 1)
printk(" oo=%08lx",virt_to_bus(btv->risc_scr_even));
btv->risc_jmp[11]=cpu_to_le32(virt_to_bus(btv->risc_scr_even));
memcpy(&btv->vbi_dev,&vbi_template, sizeof(vbi_template));
memcpy(&btv->radio_dev,&radio_template,sizeof(radio_template));
- bttv_idcard(btv);
audio(btv, AUDIO_MUTE, 1);
if(video_register_device(&btv->video_dev,VFL_TYPE_GRABBER)<0)
/* dump current state of the gpio registers before changing them,
* might help to make a new card work */
- if (bttv_verbose >= 2)
- printk("bttv%d: gpio: out_enable=0x%x, data=0x%x, in=0x%x\n",
- btv->nr,
- btread(BT848_GPIO_OUT_EN),
- btread(BT848_GPIO_DATA),
- btread(BT848_GPIO_REG_INP));
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"init #1");
/* reset the bt848 */
btwrite(0, BT848_SRESET);
btv->errors=0;
btv->needs_restart=0;
- /* i2c */
- btv->tuner_type=-1;
- init_bttv_i2c(btv);
-
if (!(btv->risc_scr_odd=(unsigned int *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL)))
return -1;
if (!(btv->risc_scr_even=(unsigned int *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL)))
/* select direct input */
btwrite(0x00, BT848_GPIO_REG_INP);
+ btwrite(0x00, BT848_GPIO_OUT_EN);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"init #2");
btwrite(BT848_IFORM_MUX1 | BT848_IFORM_XTAUTO | BT848_IFORM_AUTO,
BT848_IFORM);
bt848_set_risc_jmps(btv,-1);
spin_unlock_irqrestore(&btv->s_lock, irq_flags);
+ /* needs to be done before i2c is registered */
+ if (btv->type == BTTV_HAUPPAUGE || btv->type == BTTV_HAUPPAUGE878)
+ bttv_hauppauge_boot_msp34xx(btv);
+
+ /* register i2c */
+ btv->tuner_type=-1;
+ init_bttv_i2c(btv);
+
+ /* some card-specific stuff (needs working i2c) */
+ bttv_init_card(btv);
+
/*
* Now add the template and register the device unit.
*/
return 0;
}
+/* ----------------------------------------------------------------------- */
+
+static char *irq_name[] = { "FMTCHG", "VSYNC", "HSYNC", "OFLOW", "HLOCK",
+ "VPRES", "6", "7", "I2CDONE", "GPINT", "10",
+ "RISCI", "FBUS", "FTRGT", "FDSR", "PPERR",
+ "RIPERR", "PABORT", "OCERR", "SCERR" };
+
static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs)
{
u32 stat,astat;
u32 dstat;
- int count,i;
+ int count;
struct bttv *btv;
btv=(struct bttv *)dev_id;
astat=stat&btread(BT848_INT_MASK);
if (!astat)
return;
- btwrite(astat,BT848_INT_STAT);
- IDEBUG(printk ("bttv%d: astat=%08x\n", btv->nr, astat));
- IDEBUG(printk ("bttv%d: stat=%08x\n", btv->nr, stat));
+ btwrite(stat,BT848_INT_STAT);
/* get device status bits */
dstat=btread(BT848_DSTATUS);
-
- if (astat&BT848_INT_GPINT) {
- IDEBUG(printk ("bttv%d: IRQ_GPINT\n", btv->nr));
- wake_up_interruptible(&btv->gpioq);
- }
- if (astat&BT848_INT_FMTCHG)
- {
- IDEBUG(printk ("bttv%d: IRQ_FMTCHG\n", btv->nr));
- /*btv->win.norm&=
- (dstat&BT848_DSTATUS_NUML) ? (~1) : (~0); */
- }
- if (astat&BT848_INT_VPRES)
- {
- IDEBUG(printk ("bttv%d: IRQ_VPRES\n", btv->nr));
+ if (irq_debug) {
+ int i;
+ printk(KERN_DEBUG "bttv%d: irq loop=%d risc=%x, bits:",
+ btv->nr, count, stat>>28);
+ for (i = 0; i < (sizeof(irq_name)/sizeof(char*)); i++) {
+ if (stat & (1 << i))
+ printk(" %s",irq_name[i]);
+ if (astat & (1 << i))
+ printk("*");
+ }
+ if (stat & BT848_INT_HLOCK)
+ printk(" HLOC => %s", (dstat & BT848_DSTATUS_HLOC)
+ ? "yes" : "no");
+ if (stat & BT848_INT_VPRES)
+ printk(" PRES => %s", (dstat & BT848_DSTATUS_PRES)
+ ? "yes" : "no");
+ if (stat & BT848_INT_FMTCHG)
+ printk(" NUML => %s", (dstat & BT848_DSTATUS_PRES)
+ ? "625" : "525");
+ printk("\n");
}
+
+ if (astat&BT848_INT_GPINT)
+ wake_up_interruptible(&btv->gpioq);
+
if (astat&BT848_INT_VSYNC)
- {
- IDEBUG(printk ("bttv%d: IRQ_VSYNC\n", btv->nr));
btv->field++;
- }
+
if (astat&(BT848_INT_SCERR|BT848_INT_OCERR)) {
if (bttv_verbose)
printk("bttv%d: irq:%s%s risc_count=%08x\n",
} else {
if (bttv_verbose)
printk("bttv%d: aiee: error loops\n",btv->nr);
- /* cancel all outstanding grab requests */
- spin_lock(&btv->s_lock);
- btv->gq_in = 0;
- btv->gq_out = 0;
- btv->gq_grab = -1;
- for (i = 0; i < gbuffers; i++)
- if (btv->gbuf[i].stat == GBUFFER_GRABBING)
- btv->gbuf[i].stat = GBUFFER_ERROR;
- /* disable DMA */
- btv->risc_cap_odd = 0;
- btv->risc_cap_even = 0;
- bt848_set_risc_jmps(btv,0);
- btv->needs_restart = 1;
- spin_unlock(&btv->s_lock);
-
- wake_up_interruptible(&btv->vbiq);
- wake_up_interruptible(&btv->capq);
+ bt848_offline(btv);
}
}
if (astat&BT848_INT_RISCI)
spin_unlock(&btv->s_lock);
}
}
- if (astat&BT848_INT_OCERR)
- {
- IDEBUG(printk ("bttv%d: IRQ_OCERR\n", btv->nr));
- }
- if (astat&BT848_INT_PABORT)
- {
- IDEBUG(printk ("bttv%d: IRQ_PABORT\n", btv->nr));
- }
- if (astat&BT848_INT_RIPERR)
- {
- IDEBUG(printk ("bttv%d: IRQ_RIPERR\n", btv->nr));
- }
- if (astat&BT848_INT_PPERR)
- {
- IDEBUG(printk ("bttv%d: IRQ_PPERR\n", btv->nr));
- }
- if (astat&BT848_INT_FDSR)
- {
- IDEBUG(printk ("bttv%d: IRQ_FDSR\n", btv->nr));
- }
- if (astat&BT848_INT_FTRGT)
- {
- IDEBUG(printk ("bttv%d: IRQ_FTRGT\n", btv->nr));
- }
- if (astat&BT848_INT_FBUS)
- {
- IDEBUG(printk ("bttv%d: IRQ_FBUS\n", btv->nr));
- }
- if (astat&BT848_INT_HLOCK)
- {
+
+ if (astat&BT848_INT_HLOCK) {
if ((dstat&BT848_DSTATUS_HLOC) || (btv->radio))
audio(btv, AUDIO_ON,0);
else
audio(btv, AUDIO_OFF,0);
}
- if (astat&BT848_INT_I2CDONE)
- {
- IDEBUG(printk ("bttv%d: IRQ_I2CDONE\n", btv->nr));
- }
count++;
- if (count > 10)
- printk (KERN_WARNING "bttv%d: irq loop %d\n",
- btv->nr,count);
- if (count > 20)
- {
+ if (count > 20) {
btwrite(0, BT848_INT_MASK);
printk(KERN_ERR
"bttv%d: IRQ lockup, cleared int mask\n", btv->nr);
+ bt848_offline(btv);
}
}
}
int j;
struct bttv *btv = pci_get_drvdata(pci_dev);
+ if (bttv_verbose)
+ printk("bttv%d: unloading\n",btv->nr);
+
/* unregister i2c_bus */
- if (0 == btv->i2c_ok)
+ if (0 == btv->i2c_rc)
i2c_bit_del_bus(&btv->i2c_adap);
/* turn off all capturing, DMA and IRQs */
btwrite(0, BT848_INT_MASK);
btwrite(~0x0UL,BT848_INT_STAT);
btwrite(0x0, BT848_GPIO_OUT_EN);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"cleanup");
/* disable PCI bus-mastering */
pci_read_config_byte(btv->dev, PCI_COMMAND, &command);
if (radio[btv->nr] && btv->radio_dev.minor != -1)
video_unregister_device(&btv->radio_dev);
- release_mem_region(btv->bt848_adr,
+ release_mem_region(pci_resource_start(btv->dev,0),
pci_resource_len(btv->dev,0));
/* wake up any waiting processes
because shutdown flag is set, no new processes (in this queue)
wake_up(&btv->gpioq);
pci_set_drvdata(pci_dev, NULL);
-
return;
}
static int __devinit bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id)
{
int result;
- unsigned char command;
+ unsigned char command,lat;
struct bttv *btv;
#if defined(__powerpc__)
unsigned int cmd;
btv->i2c_command=(I2C_TIMING | BT848_I2C_SCL | BT848_I2C_SDA);
pci_read_config_byte(dev, PCI_CLASS_REVISION, &btv->revision);
- printk(KERN_INFO "bttv%d: Brooktree Bt%d (rev %d) ",
- bttv_num,btv->id, btv->revision);
- printk("bus: %d, devfn: %d, ",dev->bus->number, dev->devfn);
- printk("irq: %d, ",btv->irq);
- printk("memory: 0x%lx.\n", btv->bt848_adr);
+ pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
+ printk(KERN_INFO "bttv%d: Bt%d (rev %d) at %02x:%02x.%x, ",
+ bttv_num,btv->id, btv->revision, dev->bus->number,
+ PCI_SLOT(dev->devfn),PCI_FUNC(dev->devfn));
+ printk("irq: %d, latency: %d, memory: 0x%lx\n",
+ btv->irq, lat, btv->bt848_adr);
+
+ bttv_idcard(btv);
#if defined(__powerpc__)
/* on OpenFirmware machines (PowerMac at least), PCI memory cycle */
all the i2c code is here
also the gpio interface exported by bttv (used by lirc)
-
bttv - Bt848 frame grabber driver
Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
#include <asm/io.h>
-#include "bttv.h"
+#include "bttvp.h"
#include "tuner.h"
btv = &bttvs[card];
btaor(data, ~mask, BT848_GPIO_OUT_EN);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"extern enable");
return 0;
}
/* prior setting BT848_GPIO_REG_INP is (probably) not needed
because direct input is set on init */
btaor(data & mask, ~mask, BT848_GPIO_DATA);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"extern write");
return 0;
}
int i;
for (i = 0; i < I2C_CLIENTS_MAX; i++) {
- if (btv->i2c_clients[i] == NULL ||
- btv->i2c_clients[i]->driver->id == client->driver->id) {
+ if (btv->i2c_clients[i] == NULL) {
btv->i2c_clients[i] = client;
break;
}
if (bttv_verbose)
printk("bttv%d: i2c detach [%s]\n",btv->nr,client->name);
for (i = 0; i < I2C_CLIENTS_MAX; i++) {
- if (NULL != btv->i2c_clients[i] &&
- btv->i2c_clients[i]->driver->id == client->driver->id) {
+ if (btv->i2c_clients[i] == client) {
btv->i2c_clients[i] = NULL;
break;
}
}
struct i2c_algo_bit_data bttv_i2c_algo_template = {
- NULL,
- bttv_bit_setsda,
- bttv_bit_setscl,
- bttv_bit_getsda,
- bttv_bit_getscl,
- 10, 10, 100,
+ setsda: bttv_bit_setsda,
+ setscl: bttv_bit_setscl,
+ getsda: bttv_bit_getsda,
+ getscl: bttv_bit_getscl,
+ udelay: 40,
+ mdelay: 10,
+ timeout: 200,
};
struct i2c_adapter bttv_i2c_adap_template = {
- "bt848",
- I2C_HW_B_BT848,
- NULL,
- NULL,
- bttv_inc_use,
- bttv_dec_use,
- attach_inform,
- detach_inform,
- NULL,
+ name: "bt848",
+ id: I2C_HW_B_BT848,
+ inc_use: bttv_inc_use,
+ dec_use: bttv_dec_use,
+ client_register: attach_inform,
+ client_unregister: detach_inform,
};
struct i2c_client bttv_i2c_client_template = {
- "bttv internal",
- -1,
- 0,
- 0,
- NULL,
- NULL
+ name: "bttv internal use only",
+ id: -1,
};
{
unsigned char buffer = 0;
- if (0 != btv->i2c_ok)
+ if (0 != btv->i2c_rc)
return -1;
if (bttv_verbose && NULL != probe_for)
printk(KERN_INFO "bttv%d: i2c: checking for %s @ 0x%02x... ",
unsigned char buffer[2];
int bytes = both ? 2 : 1;
- if (0 != btv->i2c_ok)
+ if (0 != btv->i2c_rc)
return -1;
btv->i2c_client.addr = addr >> 1;
buffer[0] = b1;
/*
- bttv - Bt848 frame grabber driver
-
- Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de)
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
+ * bttv - Bt848 frame grabber driver
+ *
+ * card ID's and external interfaces of the bttv driver
+ * basically stuff needed by other drivers (i2c, lirc, ...)
+ * and is supported not to change much over time.
+ *
+ * Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de)
+ * (c) 1999,2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+ *
+ */
#ifndef _BTTV_H_
#define _BTTV_H_
-#define BTTV_VERSION_CODE KERNEL_VERSION(0,7,38)
-
-#include <linux/types.h>
-#include <linux/wait.h>
#include <linux/videodev.h>
-#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
-
-#include "audiochip.h"
-#include "bt848.h"
-
-#ifdef __KERNEL__
-
-/* fwd decl */
-struct bttv;
-
/* ---------------------------------------------------------- */
/* exported by bttv-cards.c */
#define BTTV_ZOLTRIX_GENIE 0x2e
#define BTTV_TERRATVRADIO 0x2f
#define BTTV_DYNALINK 0x30
+#define BTTV_GVBCTV3PCI 0x31
+#define BTTV_PXELVWPLTVPAK 0x32
+#define BTTV_EAGLE 0x33
+#define BTTV_PINNACLESTUDIO 0x34
+
+/* i2c address list */
+#define I2C_TSA5522 0xc2
+#define I2C_TDA7432 0x8a
+#define I2C_TDA8425 0x82
+#define I2C_TDA9840 0x84
+#define I2C_TDA9850 0xb6 /* also used by 9855,9873 */
+#define I2C_TDA9875 0xb0
+#define I2C_HAUPEE 0xa0
+#define I2C_STBEE 0xae
+#define I2C_VHX 0xc0
+#define I2C_MSP3400 0x80
+#define I2C_TEA6300 0x80
+#define I2C_DPL3518 0x84
+
+/* more card-specific defines */
+#define PT2254_L_CHANEL 0x10
+#define PT2254_R_CHANEL 0x08
+#define PT2254_DBS_IN_2 0x400
+#define PT2254_DBS_IN_10 0x20000
+#define WINVIEW_PT2254_CLK 0x40
+#define WINVIEW_PT2254_DATA 0x20
+#define WINVIEW_PT2254_STROBE 0x80
+
+struct bttv;
struct tvcard
{
u32 audiomux[6]; /* Tuner, Radio, external, internal, mute, stereo */
u32 gpiomask2; /* GPIO MUX mask */
- /* look for these i2c audio chips */
- int msp34xx:1;
- int tda8425:1;
- int tda9840:1;
- int tda985x:1;
- int tea63xx:1;
- int tea64xx:1;
- int tda7432:1;
- int tda9875:1;
+ /* i2c audio flags */
+ int no_msp34xx:1;
+ int needs_tvaudio:1;
/* other settings */
int pll;
#define PLL_35 2
int tuner_type;
+ void (*audio_hook)(struct bttv *btv, struct video_audio *v, int set);
};
extern struct tvcard bttv_tvcards[];
/* identification / initialization of the card */
extern void bttv_idcard(struct bttv *btv);
+extern void bttv_init_card(struct bttv *btv);
/* card-specific funtions */
extern void tea5757_set_freq(struct bttv *btv, unsigned short freq);
-extern void winview_setvol(struct bttv *btv, struct video_audio *v);
+extern void bttv_hauppauge_boot_msp34xx(struct bttv *btv);
/* ---------------------------------------------------------- */
/* exported by bttv-if.c */
extern wait_queue_head_t* bttv_get_gpio_queue(unsigned int card);
/* i2c */
+#define I2C_CLIENTS_MAX 8
struct i2c_algo_bit_data bttv_i2c_algo_template;
struct i2c_adapter bttv_i2c_adap_template;
struct i2c_client bttv_i2c_client_template;
unsigned char b2, int both);
void bttv_readee(struct bttv *btv, unsigned char *eedata, int addr);
-
-/* ---------------------------------------------------------- */
-/* bttv-driver.c */
-
-/* insmod options */
-extern unsigned int bttv_verbose;
-extern unsigned int bttv_debug;
-
-/* Anybody who uses more than four? */
-#define BTTV_MAX 4
-extern int bttv_num; /* number of Bt848s in use */
-extern struct bttv bttvs[BTTV_MAX];
-
-
-#ifndef O_NONCAP
-#define O_NONCAP O_TRUNC
-#endif
-
-#define MAX_GBUFFERS 64
-#define RISCMEM_LEN (32744*2)
-#define VBI_MAXLINES 16
-#define VBIBUF_SIZE (2048*VBI_MAXLINES*2)
-
-#define BTTV_MAX_FBUF 0x208000
-#define I2C_CLIENTS_MAX 8
-
-struct bttv_window
-{
- int x, y;
- ushort width, height;
- ushort bpp, bpl;
- ushort swidth, sheight;
- unsigned long vidadr;
- ushort freq;
- int norm;
- int interlace;
- int color_fmt;
- ushort depth;
-};
-
-struct bttv_pll_info {
- unsigned int pll_ifreq; /* PLL input frequency */
- unsigned int pll_ofreq; /* PLL output frequency */
- unsigned int pll_crystal; /* Crystal used for input */
- unsigned int pll_current; /* Currently programmed ofreq */
-};
-
-struct bttv_gbuf {
- int stat;
-#define GBUFFER_UNUSED 0
-#define GBUFFER_GRABBING 1
-#define GBUFFER_DONE 2
-#define GBUFFER_ERROR 3
- struct timeval tv;
-
- u16 width;
- u16 height;
- u16 fmt;
-
- u32 *risc;
- unsigned long ro;
- unsigned long re;
-};
-
-struct bttv {
- struct video_device video_dev;
- struct video_device radio_dev;
- struct video_device vbi_dev;
- struct video_picture picture; /* Current picture params */
- struct video_audio audio_dev; /* Current audio params */
-
- spinlock_t s_lock;
- struct semaphore lock;
- int user;
- int capuser;
-
- /* i2c */
- struct i2c_adapter i2c_adap;
- struct i2c_algo_bit_data i2c_algo;
- struct i2c_client i2c_client;
- int i2c_state, i2c_ok;
- struct i2c_client *i2c_clients[I2C_CLIENTS_MAX];
-
- int tuner_type;
- int channel;
-
- unsigned int nr;
- unsigned short id;
- struct pci_dev *dev;
- unsigned int irq; /* IRQ used by Bt848 card */
- unsigned char revision;
- unsigned long bt848_adr; /* bus address of IO mem returned by PCI BIOS */
- unsigned char *bt848_mem; /* pointer to mapped IO memory */
- unsigned long busriscmem;
- u32 *riscmem;
-
- unsigned char *vbibuf;
- struct bttv_window win;
- int fb_color_ctl;
- int type; /* card type */
- int cardid;
- int audio; /* audio mode */
- int audio_chip; /* set to one of the chips supported by bttv.c */
- int radio;
-
- u32 *risc_jmp;
- u32 *vbi_odd;
- u32 *vbi_even;
- u32 bus_vbi_even;
- u32 bus_vbi_odd;
- wait_queue_head_t vbiq;
- wait_queue_head_t capq;
- int vbip;
-
- u32 *risc_scr_odd;
- u32 *risc_scr_even;
- u32 risc_cap_odd;
- u32 risc_cap_even;
- int scr_on;
- int vbi_on;
- struct video_clip *cliprecs;
-
- struct bttv_gbuf *gbuf;
- int gqueue[MAX_GBUFFERS];
- int gq_in,gq_out,gq_grab,gq_start;
- char *fbuffer;
-
- struct bttv_pll_info pll;
- unsigned int Fsc;
- unsigned int field;
- unsigned int last_field; /* number of last grabbed field */
- int i2c_command;
- int triton1;
-
- int errors;
- int needs_restart;
-
- wait_queue_head_t gpioq;
- int shutdown;
-};
-#endif
-
-#if defined(__powerpc__) /* big-endian */
-extern __inline__ void io_st_le32(volatile unsigned *addr, unsigned val)
-{
- __asm__ __volatile__ ("stwbrx %1,0,%2" : \
- "=m" (*addr) : "r" (val), "r" (addr));
- __asm__ __volatile__ ("eieio" : : : "memory");
-}
-
-#define btwrite(dat,adr) io_st_le32((unsigned *)(btv->bt848_mem+(adr)),(dat))
-#define btread(adr) ld_le32((unsigned *)(btv->bt848_mem+(adr)))
-#else
-#define btwrite(dat,adr) writel((dat), (char *) (btv->bt848_mem+(adr)))
-#define btread(adr) readl(btv->bt848_mem+(adr))
-#endif
-
-#define btand(dat,adr) btwrite((dat) & btread(adr), adr)
-#define btor(dat,adr) btwrite((dat) | btread(adr), adr)
-#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr)
-
-/* bttv ioctls */
-
-#define BTTV_READEE _IOW('v', BASE_VIDIOCPRIVATE+0, char [256])
-#define BTTV_WRITEE _IOR('v', BASE_VIDIOCPRIVATE+1, char [256])
-#define BTTV_FIELDNR _IOR('v' , BASE_VIDIOCPRIVATE+2, unsigned int)
-#define BTTV_PLLSET _IOW('v' , BASE_VIDIOCPRIVATE+3, struct bttv_pll_info)
-#define BTTV_BURST_ON _IOR('v' , BASE_VIDIOCPRIVATE+4, int)
-#define BTTV_BURST_OFF _IOR('v' , BASE_VIDIOCPRIVATE+5, int)
-#define BTTV_VERSION _IOR('v' , BASE_VIDIOCPRIVATE+6, int)
-#define BTTV_PICNR _IOR('v' , BASE_VIDIOCPRIVATE+7, int)
-#define BTTV_VBISIZE _IOR('v' , BASE_VIDIOCPRIVATE+8, int)
-
-#define AUDIO_TUNER 0x00
-#define AUDIO_RADIO 0x01
-#define AUDIO_EXTERN 0x02
-#define AUDIO_INTERN 0x03
-#define AUDIO_OFF 0x04
-#define AUDIO_ON 0x05
-#define AUDIO_MUTE 0x80
-#define AUDIO_UNMUTE 0x81
-
-#define TDA9850 0x01
-#define TDA9840 0x02
-#define TDA8425 0x03
-#define TEA6300 0x04
-
-#define I2C_TSA5522 0xc2
-#define I2C_TDA7432 0x8a
-#define I2C_TDA8425 0x82
-#define I2C_TDA9840 0x84
-#define I2C_TDA9850 0xb6 /* also used by 9855,9873 */
-#define I2C_TDA9875 0xb0
-#define I2C_HAUPEE 0xa0
-#define I2C_STBEE 0xae
-#define I2C_VHX 0xc0
-#define I2C_MSP3400 0x80
-#define I2C_TEA6300 0x80
-#define I2C_DPL3518 0x84
-
-#ifndef HAVE_TVAUDIO
-#define TDA9840_SW 0x00
-#define TDA9840_LVADJ 0x02
-#define TDA9840_STADJ 0x03
-#define TDA9840_TEST 0x04
-#endif
-
-#define PT2254_L_CHANEL 0x10
-#define PT2254_R_CHANEL 0x08
-#define PT2254_DBS_IN_2 0x400
-#define PT2254_DBS_IN_10 0x20000
-#define WINVIEW_PT2254_CLK 0x40
-#define WINVIEW_PT2254_DATA 0x20
-#define WINVIEW_PT2254_STROBE 0x80
-
-struct bttv_just_hacking {
- int height,width; /* size */
- unsigned int format; /* should be VIDEO_PALETTE_* */
- long buf;
- int len;
-};
-
-#define BTTV_JUST_HACKING _IOR('v' , BASE_VIDIOCPRIVATE+31,struct bttv_just_hacking)
-
-#endif
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
+#endif /* _BTTV_H_ */
--- /dev/null
+/*
+ bttv - Bt848 frame grabber driver
+
+ bttv's *private* header file -- nobody else than bttv itself
+ should ever include this file.
+
+ Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de)
+ (c) 1999,2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _BTTVP_H_
+#define _BTTVP_H_
+
+#define BTTV_VERSION_CODE KERNEL_VERSION(0,7,48)
+
+
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+
+#include "bt848.h"
+#include "bttv.h"
+#include "audiochip.h"
+
+#ifdef __KERNEL__
+
+/* ---------------------------------------------------------- */
+/* bttv-driver.c */
+
+/* insmod options */
+extern unsigned int bttv_verbose;
+extern unsigned int bttv_debug;
+extern unsigned int bttv_gpio;
+extern void bttv_gpio_tracking(struct bttv *btv, char *comment);
+
+/* Anybody who uses more than four? */
+#define BTTV_MAX 4
+extern int bttv_num; /* number of Bt848s in use */
+extern struct bttv bttvs[BTTV_MAX];
+
+
+#ifndef O_NONCAP
+#define O_NONCAP O_TRUNC
+#endif
+
+#define MAX_GBUFFERS 64
+#define RISCMEM_LEN (32744*2)
+#define VBI_MAXLINES 16
+#define VBIBUF_SIZE (2048*VBI_MAXLINES*2)
+
+#define BTTV_MAX_FBUF 0x208000
+
+struct bttv_window
+{
+ int x, y;
+ ushort width, height;
+ ushort bpp, bpl;
+ ushort swidth, sheight;
+ unsigned long vidadr;
+ ushort freq;
+ int norm;
+ int interlace;
+ int color_fmt;
+ ushort depth;
+};
+
+struct bttv_pll_info {
+ unsigned int pll_ifreq; /* PLL input frequency */
+ unsigned int pll_ofreq; /* PLL output frequency */
+ unsigned int pll_crystal; /* Crystal used for input */
+ unsigned int pll_current; /* Currently programmed ofreq */
+};
+
+struct bttv_gbuf {
+ int stat;
+#define GBUFFER_UNUSED 0
+#define GBUFFER_GRABBING 1
+#define GBUFFER_DONE 2
+#define GBUFFER_ERROR 3
+ struct timeval tv;
+
+ u16 width;
+ u16 height;
+ u16 fmt;
+
+ u32 *risc;
+ unsigned long ro;
+ unsigned long re;
+};
+
+struct bttv {
+ struct video_device video_dev;
+ struct video_device radio_dev;
+ struct video_device vbi_dev;
+ struct video_picture picture; /* Current picture params */
+ struct video_audio audio_dev; /* Current audio params */
+
+ spinlock_t s_lock;
+ struct semaphore lock;
+ int user;
+ int capuser;
+
+ /* i2c */
+ struct i2c_adapter i2c_adap;
+ struct i2c_algo_bit_data i2c_algo;
+ struct i2c_client i2c_client;
+ int i2c_state, i2c_rc;
+ struct i2c_client *i2c_clients[I2C_CLIENTS_MAX];
+
+ int tuner_type;
+ int channel;
+
+ unsigned int nr;
+ unsigned short id;
+ struct pci_dev *dev;
+ unsigned int irq; /* IRQ used by Bt848 card */
+ unsigned char revision;
+ unsigned long bt848_adr; /* bus address of IO mem returned by PCI BIOS */
+ unsigned char *bt848_mem; /* pointer to mapped IO memory */
+ unsigned long busriscmem;
+ u32 *riscmem;
+
+ unsigned char *vbibuf;
+ struct bttv_window win;
+ int fb_color_ctl;
+ int type; /* card type */
+ int cardid;
+ int audio; /* audio mode */
+ int audio_chip; /* set to one of the chips supported by bttv.c */
+ int radio;
+
+ u32 *risc_jmp;
+ u32 *vbi_odd;
+ u32 *vbi_even;
+ u32 bus_vbi_even;
+ u32 bus_vbi_odd;
+ wait_queue_head_t vbiq;
+ wait_queue_head_t capq;
+ int vbip;
+
+ u32 *risc_scr_odd;
+ u32 *risc_scr_even;
+ u32 risc_cap_odd;
+ u32 risc_cap_even;
+ int scr_on;
+ int vbi_on;
+ struct video_clip *cliprecs;
+
+ struct bttv_gbuf *gbuf;
+ int gqueue[MAX_GBUFFERS];
+ int gq_in,gq_out,gq_grab,gq_start;
+ char *fbuffer;
+
+ struct bttv_pll_info pll;
+ unsigned int Fsc;
+ unsigned int field;
+ unsigned int last_field; /* number of last grabbed field */
+ int i2c_command;
+ int triton1;
+
+ int errors;
+ int needs_restart;
+
+ wait_queue_head_t gpioq;
+ int shutdown;
+};
+#endif
+
+#if defined(__powerpc__) /* big-endian */
+extern __inline__ void io_st_le32(volatile unsigned *addr, unsigned val)
+{
+ __asm__ __volatile__ ("stwbrx %1,0,%2" : \
+ "=m" (*addr) : "r" (val), "r" (addr));
+ __asm__ __volatile__ ("eieio" : : : "memory");
+}
+
+#define btwrite(dat,adr) io_st_le32((unsigned *)(btv->bt848_mem+(adr)),(dat))
+#define btread(adr) ld_le32((unsigned *)(btv->bt848_mem+(adr)))
+#else
+#define btwrite(dat,adr) writel((dat), (char *) (btv->bt848_mem+(adr)))
+#define btread(adr) readl(btv->bt848_mem+(adr))
+#endif
+
+#define btand(dat,adr) btwrite((dat) & btread(adr), adr)
+#define btor(dat,adr) btwrite((dat) | btread(adr), adr)
+#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr)
+
+/* bttv ioctls */
+
+#define BTTV_READEE _IOW('v', BASE_VIDIOCPRIVATE+0, char [256])
+#define BTTV_WRITEE _IOR('v', BASE_VIDIOCPRIVATE+1, char [256])
+#define BTTV_FIELDNR _IOR('v' , BASE_VIDIOCPRIVATE+2, unsigned int)
+#define BTTV_PLLSET _IOW('v' , BASE_VIDIOCPRIVATE+3, struct bttv_pll_info)
+#define BTTV_BURST_ON _IOR('v' , BASE_VIDIOCPRIVATE+4, int)
+#define BTTV_BURST_OFF _IOR('v' , BASE_VIDIOCPRIVATE+5, int)
+#define BTTV_VERSION _IOR('v' , BASE_VIDIOCPRIVATE+6, int)
+#define BTTV_PICNR _IOR('v' , BASE_VIDIOCPRIVATE+7, int)
+#define BTTV_VBISIZE _IOR('v' , BASE_VIDIOCPRIVATE+8, int)
+
+#define TDA9850 0x01
+#define TDA9840 0x02
+#define TDA8425 0x03
+#define TEA6300 0x04
+
+#endif /* _BTTVP_H_ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
--- /dev/null
+/* FIXME: this temporarely, until these are included in linux/i2c-id.h */
+
+/* drivers */
+#ifndef I2C_DRIVERID_TVMIXER
+# define I2C_DRIVERID_TVMIXER I2C_DRIVERID_EXP0
+#endif
+#ifndef I2C_DRIVERID_TVAUDIO
+# define I2C_DRIVERID_TVAUDIO I2C_DRIVERID_EXP1
+#endif
+
+/* chips */
+#ifndef I2C_DRIVERID_DPL3518
+# define I2C_DRIVERID_DPL3518 I2C_DRIVERID_EXP2
+#endif
+#ifndef I2C_DRIVERID_TDA9873
+# define I2C_DRIVERID_TDA9873 I2C_DRIVERID_EXP3
+#endif
+#ifndef I2C_DRIVERID_TDA9875
+# define I2C_DRIVERID_TDA9875 I2C_DRIVERID_EXP0+4
+#endif
+#ifndef I2C_DRIVERID_PIC16C54_PV951
+# define I2C_DRIVERID_PIC16C54_PV951 I2C_DRIVERID_EXP0+5
+#endif
+#ifndef I2C_DRIVERID_TDA7432
+# define I2C_DRIVERID_TDA7432 I2C_DRIVERID_EXP0+6
+#endif
int norm;
int stereo;
int nicam_on;
+ int acb;
int main, second; /* sound carrier */
+ int muted;
int left, right; /* volume */
int bass, treble;
#define CARRIER_COUNT(x) (sizeof(x)/sizeof(struct CARRIER_DETECT))
+/* ----------------------------------------------------------------------- */
+
+#define SCART_MASK 0
+#define SCART_IN1 1
+#define SCART_IN2 2
+#define SCART_IN1_DA 3
+#define SCART_IN2_DA 4
+#define SCART_IN3 5
+#define SCART_IN4 6
+#define SCART_MONO 7
+#define SCART_MUTE 8
+
+static int scarts[3][9] = {
+ /* MASK IN1 IN2 IN1_DA IN2_DA IN3 IN4 MONO MUTE */
+ { 0x0320, 0x0000, 0x0200, -1, -1, 0x0300, 0x0020, 0x0100, 0x0320 },
+ { 0x0c40, 0x0440, 0x0400, 0x0c00, 0x0040, 0x0000, 0x0840, 0x0800, 0x0c40 },
+ { 0x3080, 0x1000, 0x1080, 0x0000, 0x0080, 0x2080, 0x3080, 0x2000, 0x3000 },
+};
+
+static char *scart_names[] = {
+ "mask", "in1", "in2", "in1 da", "in2 da", "in3", "in4", "mono", "mute"
+};
+
+static void
+msp3400c_set_scart(struct i2c_client *client, int in, int out)
+{
+ struct msp3400c *msp = client->data;
+
+ if (-1 == scarts[out][in])
+ return;
+
+ dprintk("msp34xx: scart switch: %s => %d\n",scart_names[in],out);
+ msp->acb &= ~scarts[out][SCART_MASK];
+ msp->acb |= scarts[out][in];
+ msp3400c_write(client,I2C_MSP3400C_DFP, 0x0013, msp->acb);
+}
+
/* ------------------------------------------------------------------------ */
static void msp3400c_setcarrier(struct i2c_client *client, int cdo1, int cdo2)
msp3400c_write(client,I2C_MSP3400C_DEM, 0x0056, 0); /*LOAD_REG_1/2*/
}
-static void msp3400c_setvolume(struct i2c_client *client, int left, int right)
+static void msp3400c_setvolume(struct i2c_client *client,
+ int muted, int left, int right)
{
- int vol,val,balance;
+ int vol = 0,val = 0,balance = 0;
- vol = (left > right) ? left : right;
- val = (vol * 0x73 / 65535) << 8;
- balance = 0;
- if (vol > 0)
+ if (!muted) {
+ vol = (left > right) ? left : right;
+ val = (vol * 0x73 / 65535) << 8;
+ }
+ if (vol > 0) {
balance = ((right-left) * 127) / vol;
+ }
- dprintk("msp34xx: setvolume: %d:%d 0x%02x 0x%02x\n",
- left,right,val>>8,balance);
+ dprintk("msp34xx: setvolume: mute=%s %d:%d v=0x%02x b=0x%02x\n",
+ muted ? "on" : "off", left, right, val>>8, balance);
msp3400c_write(client,I2C_MSP3400C_DFP, 0x0000, val); /* loudspeaker */
msp3400c_write(client,I2C_MSP3400C_DFP, 0x0006, val); /* headphones */
/* scart - on/off only */
if (msp->mode == MSP_MODE_AM_NICAM) {
dprintk("msp3400: switching to AM mono\n");
/* AM mono decoding is handled by tuner, not MSP chip */
- /* so let's redirect sound from tuner via SCART */
- /* volume prescale for SCART */
- msp3400c_write(client,I2C_MSP3400C_DFP, 0x000d, 0x1900);
- /* SCART switching control register*/
- msp3400c_write(client,I2C_MSP3400C_DFP, 0x0013, 0xe900);
+ /* SCART switching control register */
+ msp3400c_set_scart(client,SCART_MONO,0);
src = 0x0200;
break;
}
restart:
msp->restart = 0;
- msp3400c_setvolume(client, 0, 0);
+ msp3400c_setvolume(client, msp->muted, 0, 0);
msp3400c_setmode(client, MSP_MODE_AM_DETECT /* +1 */ );
val1 = val2 = 0;
max1 = max2 = -1;
msp->nicam_on = 0;
msp3400c_setstereo(client, VIDEO_SOUND_MONO);
msp3400c_setcarrier(client, msp->second, msp->main);
+ /* volume prescale for SCART (AM mono input) */
+ msp3400c_write(client,I2C_MSP3400C_DFP, 0x000d, 0x1900);
msp->watch_stereo = 1;
} else if (max2 == 0 && msp->nicam) {
/* D/K NICAM */
}
/* unmute */
- msp3400c_setvolume(client, msp->left, msp->right);
+ msp3400c_setvolume(client, msp->muted, msp->left, msp->right);
if (msp->watch_stereo)
mod_timer(&msp->wake_stereo, jiffies+5*HZ);
if (msp->rmmod || signal_pending(current))
goto done;
- if (VIDEO_MODE_RADIO == msp->norm)
- continue; /* nothing to do */
-
msp->active = 1;
if (msp->watch_stereo) {
/* put into sane state (and mute) */
msp3400c_reset(client);
+ /* set various prescales */
+ msp3400c_write(client, I2C_MSP3400C_DFP, 0x0d, 0x1900); /* scart */
+ msp3400c_write(client, I2C_MSP3400C_DFP, 0x0e, 0x2403); /* FM */
+ msp3400c_write(client, I2C_MSP3400C_DFP, 0x10, 0x5a00); /* nicam */
+
/* start autodetect */
switch (msp->norm) {
case VIDEO_MODE_PAL:
break;
case VIDEO_MODE_SECAM:
mode = 0x0003;
- std = 1;
+ std = 1;
+ break;
+ case VIDEO_MODE_RADIO:
+ mode = 0x0003;
+ std = 0x0040;
break;
default:
mode = 0x0003;
}
msp3400c_write(client, I2C_MSP3400C_DEM, 0x30, mode);
msp3400c_write(client, I2C_MSP3400C_DEM, 0x20, std);
- msp3400c_write(client, I2C_MSP3400C_DFP, 0x0e, 0x2403);
- msp3400c_write(client, I2C_MSP3400C_DFP, 0x10, 0x5a00);
+
if (debug) {
int i;
for (i = 0; modelist[i].name != NULL; i++)
}
}
for (i = 0; modelist[i].name != NULL; i++)
- if (modelist[i].retval == val)
+ if (modelist[i].retval == val)
break;
dprintk("msp3410: current mode: %s (0x%04x)\n",
modelist[i].name ? modelist[i].name : "unknown",
msp3400c_write(client, I2C_MSP3400C_DEM, 0x20, val);
}
- /* set prescale / stereo */
+ /* set stereo */
switch (val) {
+ case 0x0008: /* B/G NICAM */
+ case 0x000a: /* I NICAM */
+ if (val == 0x0008)
+ msp->mode = MSP_MODE_FM_NICAM1;
+ else
+ msp->mode = MSP_MODE_FM_NICAM2;
+ /* just turn on stereo */
+ msp->stereo = VIDEO_SOUND_STEREO;
+ msp->nicam_on = 1;
+ msp->watch_stereo = 1;
+ msp3400c_setstereo(client,VIDEO_SOUND_STEREO);
+ break;
case 0x0009:
msp->mode = MSP_MODE_AM_NICAM;
msp->stereo = VIDEO_SOUND_MONO;
+ msp->nicam_on = 1;
msp3400c_setstereo(client,VIDEO_SOUND_MONO);
msp->watch_stereo = 1;
break;
/* just turn on stereo */
msp->mode = MSP_MODE_BTSC;
msp->stereo = VIDEO_SOUND_STEREO;
+ msp->nicam_on = 0;
msp->watch_stereo = 1;
msp3400c_setstereo(client,VIDEO_SOUND_STEREO);
- /* set prescale */
- msp3400c_write(client, I2C_MSP3400C_DFP, 0x0e, 0x2403); /* FM */
+ break;
+ case 0x0040: /* FM radio */
+ msp->mode = MSP_MODE_FM_RADIO;
+ msp->stereo = VIDEO_SOUND_STEREO;
+ msp->nicam_on = 0;
+ msp->watch_stereo = 0;
+ /* scart routing */
+ msp3400c_set_scart(client,SCART_IN2,0);
+ msp3400c_write(client,I2C_MSP3400C_DFP, 0x08, 0x0220);
break;
case 0x0003:
msp->mode = MSP_MODE_FM_TERRA;
msp->stereo = VIDEO_SOUND_MONO;
+ msp->nicam_on = 0;
msp->watch_stereo = 1;
- /* fall */
- default:
- msp3400c_write(client, I2C_MSP3400C_DFP, 0x0e, 0x2403); /* FM */
- msp3400c_write(client, I2C_MSP3400C_DFP, 0x10, 0x5a00); /* NICAM */
break;
}
-
+
/* unmute */
msp3400c_setbass(client, msp->bass);
msp3400c_settreble(client, msp->treble);
- msp3400c_setvolume(client, msp->left, msp->right);
+ msp3400c_setvolume(client, msp->muted, msp->left, msp->right);
if (msp->watch_stereo)
mod_timer(&msp->wake_stereo, jiffies+HZ);
static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg);
static struct i2c_driver driver = {
- "i2c msp3400 driver",
- I2C_DRIVERID_MSP3400,
- I2C_DF_NOTIFY,
- msp_probe,
- msp_detach,
- msp_command,
+ name: "i2c msp3400 driver",
+ id: I2C_DRIVERID_MSP3400,
+ flags: I2C_DF_NOTIFY,
+ attach_adapter: msp_probe,
+ detach_client: msp_detach,
+ command: msp_command,
};
static struct i2c_client client_template =
{
- "unset",
- -1,
- 0,
- 0,
- NULL,
- &driver
+ name: "(unset)",
+ driver: &driver,
};
static int msp_attach(struct i2c_adapter *adap, int addr,
}
rev1 = msp3400c_read(c, I2C_MSP3400C_DFP, 0x1e);
- rev2 = msp3400c_read(c, I2C_MSP3400C_DFP, 0x1f);
- if (0 == rev1 && 0 == rev2) {
+ if (-1 != rev1)
+ rev2 = msp3400c_read(c, I2C_MSP3400C_DFP, 0x1f);
+ if ((-1 == rev1) || (0 == rev1 && 0 == rev2)) {
kfree(msp);
printk("msp3400: error while reading chip version\n");
return -1;
msp->wake_stereo.data = (unsigned long)msp;
/* hello world :-) */
- printk(KERN_INFO "msp3400: init: chip=%s",c->name);
+ printk(KERN_INFO "msp34xx: init: chip=%s",c->name);
if (msp->nicam)
printk(", has NICAM support");
printk("\n");
msp->notify = NULL;
wake_up_interruptible(&msp->wq);
-#ifdef REGISTER_MIXER
- if ((msp->mixer_num = register_sound_mixer(&msp3400c_mixer_fops,mixer)) < 0)
- printk(KERN_ERR "msp3400c: cannot allocate mixer device\n");
-#endif
-
/* update our own array */
for (i = 0; i < MSP3400_MAX; i++) {
if (NULL == msps[i]) {
struct msp3400c *msp = (struct msp3400c*)client->data;
int i;
-#ifdef REGISTER_MIXER
- if (msp->mixer_num >= 0)
- unregister_sound_mixer(msp->mixer_num);
-#endif
-
/* shutdown control thread */
del_timer(&msp->wake_stereo);
if (msp->thread)
static int msp_command(struct i2c_client *client,unsigned int cmd, void *arg)
{
struct msp3400c *msp = (struct msp3400c*)client->data;
+ __u16 *sarg = arg;
#if 0
int *iarg = (int*)arg;
- __u16 *sarg = arg;
#endif
switch (cmd) {
+ case AUDC_SET_INPUT:
+#if 1
+ /* hauppauge 44xxx scart switching */
+ switch (*sarg) {
+ case AUDIO_RADIO:
+ msp3400c_set_scart(client,SCART_IN2,0);
+ break;
+ case AUDIO_EXTERN:
+ msp3400c_set_scart(client,SCART_IN1,0);
+ break;
+ default:
+ if (*sarg & AUDIO_MUTE)
+ msp3400c_set_scart(client,SCART_MUTE,0);
+ break;
+ }
+#endif
+ break;
+
case AUDC_SET_RADIO:
msp->norm = VIDEO_MODE_RADIO;
msp->watch_stereo=0;
del_timer(&msp->wake_stereo);
+ dprintk("msp34xx: switching to radio mode\n");
if (msp->simple) {
- msp3400c_reset(client);
- msp3400c_write(client, I2C_MSP3400C_DEM, 0x30, 0x0003); /* automatic */
- msp3400c_write(client, I2C_MSP3400C_DEM, 0x20, 0x0040); /* FM Radio */
- msp3400c_write(client, I2C_MSP3400C_DFP, 0x0e, 0x2403); /* FM prescale */
- msp3400c_setbass(client, msp->bass);
- msp3400c_settreble(client, msp->treble);
- msp3400c_setvolume(client, msp->left, msp->right);
+ /* the thread will do for us */
+ msp3400c_setvolume(client,msp->muted,0,0);
+ if (msp->active)
+ msp->restart = 1;
+ wake_up_interruptible(&msp->wq);
} else {
+ /* set msp3400 to FM radio mode */
msp3400c_setmode(client,MSP_MODE_FM_RADIO);
msp3400c_setcarrier(client, MSP_CARRIER(10.7),MSP_CARRIER(10.7));
- msp3400c_setvolume(client,msp->left, msp->right);
+ msp3400c_setvolume(client,msp->muted,msp->left,msp->right);
}
break;
va->flags |= VIDEO_AUDIO_VOLUME |
VIDEO_AUDIO_BASS |
- VIDEO_AUDIO_TREBLE;
+ VIDEO_AUDIO_TREBLE |
+ VIDEO_AUDIO_MUTABLE;
+ if (msp->muted)
+ va->flags |= VIDEO_AUDIO_MUTE;
va->volume=MAX(msp->left,msp->right);
va->balance=(32768*MIN(msp->left,msp->right))/
(va->volume ? va->volume : 1);
va->bass = msp->bass;
va->treble = msp->treble;
- autodetect_stereo(client);
- va->mode = msp->stereo;
+ if (msp->norm != VIDEO_MODE_RADIO) {
+ autodetect_stereo(client);
+ va->mode = msp->stereo;
+ }
break;
}
case VIDIOCSAUDIO:
{
struct video_audio *va = arg;
+ msp->muted = (va->flags & VIDEO_AUDIO_MUTE);
msp->left = (MIN(65536 - va->balance,32768) *
va->volume) / 32768;
msp->right = (MIN(va->balance,32768) *
va->volume) / 32768;
msp->bass = va->bass;
msp->treble = va->treble;
- msp3400c_setvolume(client,msp->left, msp->right);
+ msp3400c_setvolume(client,msp->muted,msp->left,msp->right);
msp3400c_setbass(client,msp->bass);
msp3400c_settreble(client,msp->treble);
{
struct video_channel *vc = arg;
+ dprintk("msp34xx: switching to TV mode\n");
msp->norm = vc->norm;
break;
}
case VIDIOCSFREQ:
{
/* new channel -- kick audio carrier scan */
- msp3400c_setvolume(client,0,0);
+ msp3400c_setvolume(client,msp->muted,0,0);
msp->watch_stereo=0;
del_timer(&msp->wake_stereo);
if (msp->active)
* Added I2C_DRIVERID_TDA7432
* added loudness insmod control
* Revision: 0.1 - initial version
- *
- * Changes:
- * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 08/14/2000
- * - resource allocation fixes in tda7432_attach
*/
#include <linux/module.h>
#include "bttv.h"
#include "audiochip.h"
-
-/* This driver ID is brand new, so define it if it's not in i2c-id.h yet */
-#ifndef I2C_DRIVERID_TDA7432
- #define I2C_DRIVERID_TDA7432 27
-#endif
-
+#include "id.h"
MODULE_AUTHOR("Eric Sandeen <eric_sandeen@bigfoot.com>");
MODULE_DESCRIPTION("bttv driver for the tda7432 audio processor chip");
int tone;
int lf, lr, rf, rr;
int loud;
+ struct i2c_client c;
};
static struct i2c_driver driver;
struct tda7432 *t;
struct i2c_client *client;
d2printk("tda7432: In tda7432_attach\n");
- client = kmalloc(sizeof *client,GFP_KERNEL);
- if (!client)
- return -ENOMEM;
+
+ t = kmalloc(sizeof *t,GFP_KERNEL);
+ if (!t)
+ return -ENOMEM;
+ memset(t,0,sizeof *t);
+
+ client = &t->c;
memcpy(client,&client_template,sizeof(struct i2c_client));
client->adapter = adap;
client->addr = addr;
+ client->data = t;
- client->data = t = kmalloc(sizeof *t,GFP_KERNEL);
- if (!t) {
- kfree(client);
- return -ENOMEM;
- }
- memset(t,0,sizeof *t);
do_tda7432_init(client);
MOD_INC_USE_COUNT;
strcpy(client->name,"TDA7432");
i2c_detach_client(client);
kfree(t);
- kfree(client);
MOD_DEC_USE_COUNT;
return 0;
}
+++ /dev/null
-/*
- * for the TDA8425 chip (I don't know which cards have this)
- * WARNING: THIS DRIVER WILL LOAD WITHOUT COMPLAINTS EVEN IF A DIFFERENT
- * CHIP IS AT ADDRESS 0x82 (it relies on i2c to make sure that there is a
- * device acknowledging that address)
- *
- * Copyright (c) 1998 Greg Alexander <galexand@acm.org>
- * This code is placed under the terms of the GNU General Public License
- * Code liberally copied from msp3400.c, which is by Gerd Knorr
- *
- * All of this should work, though it would be nice to eventually support
- * balance (different left,right values). Also, the chip seems (?) to have
- * two stereo inputs, so if someone has this card, could they tell me if the
- * second one can be used for anything (i.e., does it have an external input
- * that you can't hear even if you set input to composite?)
- *
- * Changes:
- * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 08/14/2000
- * - resource allocation fixes in tda8425_attach
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/malloc.h>
-#include <linux/videodev.h>
-#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
-
-#include "bttv.h"
-#include "audiochip.h"
-
-/* Addresses to scan */
-static unsigned short normal_i2c[] = {
- I2C_TDA8425 >> 1,
- I2C_CLIENT_END};
-static unsigned short normal_i2c_range[] = {I2C_CLIENT_END};
-static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
-static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
-static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
-static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
-static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
-static struct i2c_client_address_data addr_data = {
- normal_i2c, normal_i2c_range,
- probe, probe_range,
- ignore, ignore_range,
- force
-};
-
-MODULE_PARM(debug,"i");
-static int debug = 0; /* insmod parameter */
-#define dprintk if (debug) printk
-
-
-struct tda8425 {
- int mode; /* set to AUDIO_{OFF,TUNER,RADIO,EXTERN} */
- int stereo;
- __u16 left,right;
- __u16 bass,treble;
-};
-
-static struct i2c_driver driver;
-static struct i2c_client client_template;
-
-
-#define TDA8425_VL 0x00 /* volume left */
-#define TDA8425_VR 0x01 /* volume right */
-#define TDA8425_BA 0x02 /* bass */
-#define TDA8425_TR 0x03 /* treble */
-#define TDA8425_S1 0x08 /* switch functions */
- /* values for those registers: */
-#define TDA8425_S1_OFF 0xEE /* audio off (mute on) */
-#define TDA8425_S1_ON 0xCE /* audio on (mute off) - "linear stereo" mode */
-
-
-/* ******************************** *
- * functions for talking to TDA8425 *
- * ******************************** */
-
-static int tda8425_write(struct i2c_client *client, int addr, int val)
-{
- unsigned char buffer[2];
-
- buffer[0] = addr;
- buffer[1] = val;
- if (2 != i2c_master_send(client,buffer,2)) {
- printk(KERN_WARNING "tda8425: I/O error, trying (write %d 0x%x)\n",
- addr, val);
- return -1;
- }
- return 0;
-}
-
-static void tda8425_set(struct i2c_client *client)
-{
- struct tda8425 *tda = client->data;
-
- /* mode is ignored today */
- dprintk(KERN_DEBUG "tda8425_set(%04x,%04x,%04x,%04x)\n",tda->left>>10,tda->right>>10,tda->bass>>12,tda->treble>>12);
- tda8425_write(client, TDA8425_VL, tda->left>>10 |0xC0);
- tda8425_write(client, TDA8425_VR, tda->right>>10 |0xC0);
- tda8425_write(client, TDA8425_BA, tda->bass>>12 |0xF0);
- tda8425_write(client, TDA8425_TR, tda->treble>>12|0xF0);
-}
-
-static void do_tda8425_init(struct i2c_client *client)
-{
- struct tda8425 *tda = client->data;
-
- tda->left=tda->right =61440; /* 0dB */
- tda->bass=tda->treble=24576; /* 0dB */
- tda->mode=AUDIO_OFF;
- tda->stereo=1;
- /* left=right=0x27<<10, bass=treble=0x07<<12 */
- tda8425_write(client, TDA8425_S1, TDA8425_S1_OFF); /* mute */
- tda8425_set(client);
-}
-
-static void tda8425_audio(struct i2c_client *client, int mode)
-{
- struct tda8425 *tda = client->data;
-
- /* valid for AUDIO_TUNER, RADIO, EXTERN, OFF */
- dprintk(KERN_DEBUG "tda8425_audio:%d (T,R,E,I,O)\n",mode);
- tda->mode=mode;
- tda8425_write(client, TDA8425_S1,
- (mode==AUDIO_OFF)?TDA8425_S1_OFF:TDA8425_S1_ON);
- /* this is the function we'll need to change if it turns out the
- * input-selecting capabilities should be used. */
-}
-
-
-/* *********************** *
- * i2c interface functions *
- * *********************** */
-
-static int tda8425_attach(struct i2c_adapter *adap, int addr,
- unsigned short flags, int kind)
-{
- struct tda8425 *tda;
- struct i2c_client *client;
-
- client = kmalloc(sizeof *client,GFP_KERNEL);
- if (!client)
- return -ENOMEM;
- memcpy(client,&client_template,sizeof(struct i2c_client));
- client->adapter = adap;
- client->addr = addr;
-
- client->data = tda = kmalloc(sizeof *tda,GFP_KERNEL);
- if (!tda) {
- kfree(client);
- return -ENOMEM;
- }
- memset(tda,0,sizeof *tda);
- do_tda8425_init(client);
- MOD_INC_USE_COUNT;
- strcpy(client->name,"TDA8425");
- printk(KERN_INFO "tda8425: init\n");
-
- i2c_attach_client(client);
- return 0;
-}
-
-static int tda8425_probe(struct i2c_adapter *adap)
-{
- if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848))
- return i2c_probe(adap, &addr_data, tda8425_attach);
- return 0;
-}
-
-
-static int tda8425_detach(struct i2c_client *client)
-{
- struct tda8425 *tda = client->data;
-
- do_tda8425_init(client);
- i2c_detach_client(client);
-
- kfree(tda);
- kfree(client);
- MOD_DEC_USE_COUNT;
- return 0;
-}
-
-static int tda8425_command(struct i2c_client *client,
- unsigned int cmd, void *arg)
-{
- struct tda8425 *tda = client->data;
- __u16 *sarg = arg;
-
- switch (cmd) {
- case AUDC_SET_RADIO:
- tda8425_audio(client,AUDIO_RADIO);
- break;
- case AUDC_SET_INPUT:
- tda8425_audio(client,*sarg);
- break;
-
- /* --- v4l ioctls --- */
- /* take care: bttv does userspace copying, we'll get a
- kernel pointer here... */
- case VIDIOCGAUDIO:
- {
- struct video_audio *va = arg;
-
- va->flags |= VIDEO_AUDIO_VOLUME |
- VIDEO_AUDIO_BASS |
- VIDEO_AUDIO_TREBLE;
- va->volume=MAX(tda->left,tda->right);
- va->balance=(32768*MIN(tda->left,tda->right))/
- (va->volume ? va->volume : 1);
- va->balance=(tda->left<tda->right)?
- (65535-va->balance) : va->balance;
- va->bass = tda->bass;
- va->treble = tda->treble;
- break;
- }
- case VIDIOCSAUDIO:
- {
- struct video_audio *va = arg;
-
- tda->left = (MIN(65536 - va->balance,32768) *
- va->volume) / 32768;
- tda->right = (MIN(va->balance,32768) *
- va->volume) / 32768;
- tda->bass = va->bass;
- tda->treble = va->treble;
- tda8425_set(client);
- break;
- }
-
-#if 0
- /* --- old, obsolete interface --- */
- case AUDC_GET_VOLUME_LEFT:
- *sarg = tda->left;
- break;
- case AUDC_GET_VOLUME_RIGHT:
- *sarg = tda->right;
- break;
- case AUDC_SET_VOLUME_LEFT:
- tda->left = *sarg;
- tda8425_set(client);
- break;
- case AUDC_SET_VOLUME_RIGHT:
- tda->right = *sarg;
- tda8425_set(client);
- break;
-
- case AUDC_GET_BASS:
- *sarg = tda->bass;
- break;
- case AUDC_SET_BASS:
- tda->bass = *sarg;
- tda8425_set(client);
- break;
-
- case AUDC_GET_TREBLE:
- *sarg = tda->treble;
- break;
- case AUDC_SET_TREBLE:
- tda->treble = *sarg;
- tda8425_set(client);
- break;
-
- case AUDC_GET_STEREO:
- *sarg = tda->stereo?VIDEO_SOUND_STEREO:VIDEO_SOUND_MONO;
- break;
- case AUDC_SET_STEREO:
- tda->stereo=(*sarg==VIDEO_SOUND_MONO)?0:1;
- /* TODO: make this write to the TDA9850? */
- break;
-
-/* case AUDC_SWITCH_MUTE: someday, maybe -- not a lot of point to
- case AUDC_NEWCHANNEL: it and it would require preserving state
- case AUDC_GET_DC: huh?? (not used by bttv.c)
-*/
-#endif
- default:
- /* nothing */
- }
- return 0;
-}
-
-
-static struct i2c_driver driver = {
- "i2c tda8424 driver",
- I2C_DRIVERID_TDA8425,
- I2C_DF_NOTIFY,
- tda8425_probe,
- tda8425_detach,
- tda8425_command,
-};
-
-static struct i2c_client client_template =
-{
- "(unset)", /* name */
- -1,
- 0,
- 0,
- NULL,
- &driver
-};
-
-#ifdef MODULE
-int init_module(void)
-#else
-int tda8425_init(void)
-#endif
-{
- i2c_add_driver(&driver);
- return 0;
-}
-
-#ifdef MODULE
-void cleanup_module(void)
-{
- i2c_del_driver(&driver);
-}
-#endif
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
+++ /dev/null
-/*
- * For the TDA9850 and TDA9855 chips
- * (The TDA9855 is used on the Diamond DTV2000 and the TDA9850 is used
- * on STB cards. Other cards probably use these chips as well.)
- * This driver will not complain if used with any
- * other i2c device with the same address.
- *
- * Copyright (c) 1999 Gerd Knorr
- * TDA9850 code and TDA9855.c merger by Eric Sandeen (eric_sandeen@bigfoot.com)
- * This code is placed under the terms of the GNU General Public License
- * Based on tda9855.c by Steve VanDeBogart (vandebo@uclink.berkeley.edu)
- * Which was based on tda8425.c by Greg Alexander (c) 1998
- *
- * Contributors:
- * Arnaldo Carvalho de Melo <acme@conectiva.com.br>
- *
- * OPTIONS:
- * debug - set to 1 if you'd like to see debug messages
- * - set to 2 if you'd like to be flooded with debug messages
- * chip - set to 9850 or 9855 to select your chip (default 9855)
- *
- * TODO:
- * Fix channel change bug - sound goes out when changeing channels, mute
- * and unmote to fix. - Is this still here?
- * Fine tune sound
- * Get rest of capabilities into video_audio struct...
- *
- * Revision 0.6 - resource allocation fixes in tda985x_attach (08/14/2000)
- * Revision 0.5 - cleaned up debugging messages, added debug level=2
- * Revision: 0.4 - check for correct chip= insmod value
- * also cleaned up comments a bit
- * Revision: 0.3 - took out extraneous tda985x_write in tda985x_command
- * Revision: 0.2 - added insmod option chip=
- * Revision: 0.1 - original version
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/malloc.h>
-#include <linux/videodev.h>
-#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
-
-#include "bttv.h"
-#include "audiochip.h"
-
-MODULE_PARM(debug,"i");
-MODULE_PARM(chip,"i");
-MODULE_PARM_DESC(chip, "Type of chip to handle: 9850 or 9855");
-
-static int debug = 0; /* insmod parameter */
-static int chip = 9855; /* insmod parameter */
-
-/* Addresses to scan */
-#define I2C_TDA985x_L 0xb4
-#define I2C_TDA985x_H 0xb6
-static unsigned short normal_i2c[] = {I2C_CLIENT_END};
-static unsigned short normal_i2c_range[] = {
- I2C_TDA985x_L >> 1,
- I2C_TDA985x_H >> 1,
- I2C_CLIENT_END
-};
-static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
-static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
-static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
-static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
-static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
-static struct i2c_client_address_data addr_data = {
- normal_i2c, normal_i2c_range,
- probe, probe_range,
- ignore, ignore_range,
- force
-};
-
-/* This is a superset of the TDA9850 and TDA9855 members */
-
-struct tda985x {
- int addr;
- int rvol, lvol;
- int bass, treble, sub;
- int c4, c5, c6, c7;
- int a1, a2, a3;
-};
-
-static struct i2c_driver driver;
-static struct i2c_client client_template;
-
-#define dprintk if (debug) printk
-#define d2printk if (debug == 2) printk
-
-/* The TDA9850 and TDA9855 are both made by Philips Semiconductor
- * http://www.semiconductors.philips.com
- * TDA9850: I2C-bus controlled BTSC stereo/SAP decoder
- * TDA9855: I2C-bus controlled BTSC stereo/SAP decoder and audio processor
- *
- * The TDA9850 has more or less a subset of the functions that the TDA9855
- * has. As a result, we can re-use many of these defines. Anything with
- * TDA9855 is specific to that chip, anything with TDA9850 is specific
- * to that chip, and anything with TDA985x is valid for either.
- *
- * To complicate things further, the TDA9850 uses labels C1 through C4
- * for subaddresses 0x04 through 0x07, while the TDA9855 uses
- * C1 through C3 for subadresses 0x05 through 0x07 - quite confusing.
- * To help keep things straight, I have renamed the various C[1,4] labels
- * to C[4,7] so that the numerical label matches the hex value of the
- * subaddress for both chips. At least the A[1,3] labels line up. :)
- */
-
- /* subaddresses for TDA9855 */
-#define TDA9855_VR 0x00 /* Volume, right */
-#define TDA9855_VL 0x01 /* Volume, left */
-#define TDA9855_BA 0x02 /* Bass */
-#define TDA9855_TR 0x03 /* Treble */
-#define TDA9855_SW 0x04 /* Subwoofer - not connected on DTV2000 */
-
- /* subaddresses for TDA9850 */
-#define TDA9850_C4 0x04 /* Control 1 for TDA9850 */
-
- /* subaddesses for both chips */
-#define TDA985x_C5 0x05 /* Control 2 for TDA9850, Control 1 for TDA9855 */
-#define TDA985x_C6 0x06 /* Control 3 for TDA9850, Control 2 for TDA9855 */
-#define TDA985x_C7 0x07 /* Control 4 for TDA9850, Control 3 for TDA9855 */
-#define TDA985x_A1 0x08 /* Alignment 1 for both chips */
-#define TDA985x_A2 0x09 /* Alignment 2 for both chips */
-#define TDA985x_A3 0x0a /* Alignment 3 for both chips */
-
- /* Masks for bits in TDA9855 subaddresses */
-/* 0x00 - VR in TDA9855 */
-/* 0x01 - VL in TDA9855 */
-/* lower 7 bits control gain from -71dB (0x28) to 16dB (0x7f)
- * in 1dB steps - mute is 0x27 */
-
-
-/* 0x02 - BA in TDA9855 */
-/* lower 5 bits control bass gain from -12dB (0x06) to 16.5dB (0x19)
- * in .5dB steps - 0 is 0x0E */
-
-
-/* 0x03 - TR in TDA9855 */
-/* 4 bits << 1 control treble gain from -12dB (0x3) to 12dB (0xb)
- * in 3dB steps - 0 is 0x7 */
-
- /* Masks for bits in both chips' subaddresses */
-/* 0x04 - SW in TDA9855, C4/Control 1 in TDA9850 */
-/* Unique to TDA9855: */
-/* 4 bits << 2 control subwoofer/surround gain from -14db (0x1) to 14db (0xf)
- * in 3dB steps - mute is 0x0 */
-
-/* Unique to TDA9850: */
-/* lower 4 bits control stereo noise threshold, over which stereo turns off
- * set to values of 0x00 through 0x0f for Ster1 through Ster16 */
-
-
-/* 0x05 - C5 - Control 1 in TDA9855 , Control 2 in TDA9850*/
-/* Unique to TDA9855: */
-#define TDA9855_MUTE 1<<7 /* GMU, Mute at outputs */
-#define TDA9855_AVL 1<<6 /* AVL, Automatic Volume Level */
-#define TDA9855_LOUD 1<<5 /* Loudness, 1==off */
-#define TDA9855_SUR 1<<3 /* Surround / Subwoofer 1==.5(L-R) 0==.5(L+R) */
- /* Bits 0 to 3 select various combinations
- * of line in and line out, only the
- * interesting ones are defined */
-#define TDA9855_EXT 1<<2 /* Selects inputs LIR and LIL. Pins 41 & 12 */
-#define TDA9855_INT 0 /* Selects inputs LOR and LOL. (internal) */
-
-/* Unique to TDA9850: */
-/* lower 4 bits contol SAP noise threshold, over which SAP turns off
- * set to values of 0x00 through 0x0f for SAP1 through SAP16 */
-
-
-/* 0x06 - C6 - Control 2 in TDA9855, Control 3 in TDA9850 */
-/* Common to TDA9855 and TDA9850: */
-#define TDA985x_SAP 3<<6 /* Selects SAP output, mute if not received */
-#define TDA985x_STEREO 1<<6 /* Selects Stereo ouput, mono if not received */
-#define TDA985x_MONO 0 /* Forces Mono output */
-#define TDA985x_LMU 1<<3 /* Mute (LOR/LOL for 9855, OUTL/OUTR for 9850) */
-
-/* Unique to TDA9855: */
-#define TDA9855_TZCM 1<<5 /* If set, don't mute till zero crossing */
-#define TDA9855_VZCM 1<<4 /* If set, don't change volume till zero crossing*/
-#define TDA9855_LINEAR 0 /* Linear Stereo */
-#define TDA9855_PSEUDO 1 /* Pseudo Stereo */
-#define TDA9855_SPAT_30 2 /* Spatial Stereo, 30% anti-phase crosstalk */
-#define TDA9855_SPAT_50 3 /* Spatial Stereo, 52% anti-phase crosstalk */
-#define TDA9855_E_MONO 7 /* Forced mono - mono select elseware, so useless*/
-
-
-/* 0x07 - C7 - Control 3 in TDA9855, Control 4 in TDA9850 */
-/* Common to both TDA9855 and TDA9850: */
-/* lower 4 bits control input gain from -3.5dB (0x0) to 4dB (0xF)
- * in .5dB steps - 0dB is 0x7 */
-
-
-/* 0x08, 0x09 - A1 and A2 (read/write) */
-/* Common to both TDA9855 and TDA9850: */
-/* lower 5 bites are wideband and spectral expander alignment
- * from 0x00 to 0x1f - nominal at 0x0f and 0x10 (read/write) */
-#define TDA985x_STP 1<<5 /* Stereo Pilot/detect (read-only) */
-#define TDA985x_SAPP 1<<6 /* SAP Pilot/detect (read-only) */
-#define TDA985x_STS 1<<7 /* Stereo trigger 1= <35mV 0= <30mV (write-only)*/
-
-
-/* 0x0a - A3 */
-/* Common to both TDA9855 and TDA9850: */
-/* lower 3 bits control timing current for alignment: -30% (0x0), -20% (0x1),
- * -10% (0x2), nominal (0x3), +10% (0x6), +20% (0x5), +30% (0x4) */
-#define TDA985x_ADJ 1<<7 /* Stereo adjust on/off (wideband and spectral */
-
-/* Unique to TDA9855: */
-/* 2 bits << 5 control AVL attack time: 420ohm (0x0), 730ohm (0x2),
- * 1200ohm (0x1), 2100ohm (0x3) */
-
-
-/* Begin code */
-
-static int tda985x_write(struct i2c_client *client, int subaddr, int val)
-{
- unsigned char buffer[2];
- d2printk("tda985x: In tda985x_write\n");
- dprintk("tda985x: Writing %d 0x%x\n", subaddr, val);
- buffer[0] = subaddr;
- buffer[1] = val;
- if (2 != i2c_master_send(client,buffer,2)) {
- printk(KERN_WARNING "tda985x: I/O error, trying (write %d 0x%x)\n",
- subaddr, val);
- return -1;
- }
- return 0;
-}
-
-static int tda985x_read(struct i2c_client *client)
-{
- unsigned char buffer;
- d2printk("tda985x: In tda985x_read\n");
- if (1 != i2c_master_recv(client,&buffer,1)) {
- printk(KERN_WARNING "tda985x: I/O error, trying (read)\n");
- return -1;
- }
- dprintk("tda985x: Read 0x%02x\n", buffer);
- return buffer;
-}
-
-static int tda985x_set(struct i2c_client *client)
-{
- struct tda985x *t = client->data;
- unsigned char buf[16];
- d2printk("tda985x: In tda985x_set\n");
-
- if (chip == 9855)
- {
- dprintk(KERN_INFO
- "tda985x: tda985x_set(0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x)\n",
- t->rvol,t->lvol,t->bass,t->treble,t->sub,
- t->c5,t->c6,t->c7,t->a1,t->a2,t->a3);
- buf[0] = TDA9855_VR;
- buf[1] = t->rvol;
- buf[2] = t->lvol;
- buf[3] = t->bass;
- buf[4] = t->treble;
- buf[5] = t->sub;
- buf[6] = t->c5;
- buf[7] = t->c6;
- buf[8] = t->c7;
- buf[9] = t->a1;
- buf[10] = t->a2;
- buf[11] = t->a3;
- if (12 != i2c_master_send(client,buf,12)) {
- printk(KERN_WARNING "tda985x: I/O error, trying tda985x_set\n");
- return -1;
- }
- }
-
- else if (chip == 9850)
- {
- dprintk(KERN_INFO
- "tda986x: tda985x_set(0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x)\n",
- t->c4,t->c5,t->c6,t->c7,t->a1,t->a2,t->a3);
- buf[0] = TDA9850_C4;
- buf[1] = t->c4;
- buf[2] = t->c5;
- buf[3] = t->c6;
- buf[4] = t->c7;
- buf[5] = t->a1;
- buf[6] = t->a2;
- buf[7] = t->a3;
- if (8 != i2c_master_send(client,buf,8)) {
- printk(KERN_WARNING "tda985x: I/O error, trying tda985x_set\n");
- return -1;
- }
- }
-
- return 0;
-}
-
-static void do_tda985x_init(struct i2c_client *client)
-{
- struct tda985x *t = client->data;
- d2printk("tda985x: In tda985x_init\n");
-
- if (chip == 9855)
- {
- printk("tda985x: Using tda9855 options\n");
- t->rvol = 0x6f; /* 0dB */
- t->lvol = 0x6f; /* 0dB */
- t->bass = 0x0e; /* 0dB */
- t->treble = (0x07 << 1); /* 0dB */
- t->sub = 0x8 << 2; /* 0dB */
- t->c5 = TDA9855_MUTE | TDA9855_AVL |
- TDA9855_LOUD | TDA9855_INT;
- /* Set Mute, AVL, Loudness off, Internal sound */
- t->c6 = TDA985x_STEREO | TDA9855_LINEAR |
- TDA9855_TZCM | TDA9855_VZCM;
- /* Stereo linear mode, also wait til zero crossings */
- t->c7 = 0x07; /* 0dB input gain */
- }
-
- else if (chip == 9850)
- {
- printk("tda985x: Using tda9850 options\n");
- t->c4 = 0x08; /* Set stereo noise thresh to nominal */
- t->c5 = 0x08; /* Set SAP noise threshold to nominal */
- t->c6 = TDA985x_STEREO; /* Select Stereo mode for decoder */
- t->c7 = 0x07; /* 0dB input gain */
- }
-
- /* The following is valid for both chip types */
- t->a1 = 0x10; /* Select nominal wideband expander */
- t->a2 = 0x10; /* Select nominal spectral expander and 30mV trigger */
- t->a3 = 0x3; /* Set: nominal timing current, 420ohm AVL attack */
-
- tda985x_set(client);
-}
-
-/* *********************** *
- * i2c interface functions *
- * *********************** */
-
-static int tda985x_attach(struct i2c_adapter *adap, int addr,
- unsigned short flags, int kind)
-{
- struct tda985x *t;
- struct i2c_client *client;
- d2printk("tda985x: In tda985x_attach\n");
- client = kmalloc(sizeof *client,GFP_KERNEL);
- if (!client)
- return -ENOMEM;
- memcpy(client,&client_template,sizeof(struct i2c_client));
- client->adapter = adap;
- client->addr = addr;
-
- client->data = t = kmalloc(sizeof *t,GFP_KERNEL);
- if (!t) {
- kfree(client);
- return -ENOMEM;
- }
- memset(t,0,sizeof *t);
- do_tda985x_init(client);
- MOD_INC_USE_COUNT;
- strcpy(client->name,"TDA985x");
- printk(KERN_INFO "tda985x: init\n");
-
- i2c_attach_client(client);
- return 0;
-}
-
-static int tda985x_probe(struct i2c_adapter *adap)
-{
- if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848))
- return i2c_probe(adap, &addr_data, tda985x_attach);
- return 0;
-}
-
-static int tda985x_detach(struct i2c_client *client)
-{
- struct tda985x *t = client->data;
-
- do_tda985x_init(client);
- i2c_detach_client(client);
-
- kfree(t);
- kfree(client);
- MOD_DEC_USE_COUNT;
- return 0;
-}
-
-static int tda985x_command(struct i2c_client *client,
- unsigned int cmd, void *arg)
-{
- struct tda985x *t = client->data;
- d2printk("tda985x: In tda985x_command\n");
-#if 0
- __u16 *sarg = arg;
-#endif
-
- switch (cmd) {
- /* --- v4l ioctls --- */
- /* take care: bttv does userspace copying, we'll get a
- kernel pointer here... */
- case VIDIOCGAUDIO:
- {
- struct video_audio *va = arg;
- dprintk("tda985x: VIDIOCGAUDIO\n");
- if (chip == 9855)
- {
- int left,right;
-
- va->flags |= VIDEO_AUDIO_VOLUME |
- VIDEO_AUDIO_BASS |
- VIDEO_AUDIO_TREBLE;
-
- /* min is 0x27 max is 0x7f, vstep is 2e8 */
- left = (t->lvol-0x27)*0x2e8;
- right = (t->rvol-0x27)*0x2e8;
- va->volume=MAX(left,right);
- va->balance=(32768*MIN(left,right))/
- (va->volume ? va->volume : 1);
- va->balance=(left<right)?
- (65535-va->balance) : va->balance;
- va->bass = (t->bass-0x6)*0xccc; /* min 0x6 max 0x19 */
- va->treble = ((t->treble>>1)-0x3)*0x1c71;
- }
-
- /* Valid for both chips: */
- {
- va->mode = ((TDA985x_STP | TDA985x_SAPP) &
- tda985x_read(client)) >> 4;
- /* Add mono mode regardless of SAP and stereo */
- /* Allows forced mono */
- va->mode |= VIDEO_SOUND_MONO;
- }
-
- break; /* VIDIOCGAUDIO case */
- }
-
- case VIDIOCSAUDIO:
- {
- struct video_audio *va = arg;
- dprintk("tda985x: VIDEOCSAUDIO\n");
- if (chip == 9855)
- {
- int left,right;
-
- left = (MIN(65536 - va->balance,32768) *
- va->volume) / 32768;
- right = (MIN(va->balance,32768) *
- va->volume) / 32768;
- t->lvol = left/0x2e8+0x27;
- t->rvol = right/0x2e8+0x27;
- t->bass = va->bass/0xccc+0x6;
- t->treble = (va->treble/0x1c71+0x3)<<1;
- tda985x_write(client,TDA9855_VL,t->lvol);
- tda985x_write(client,TDA9855_VR,t->rvol);
- tda985x_write(client,TDA9855_BA, t->bass);
- tda985x_write(client,TDA9855_TR,t->treble);
- }
-
- /* The following is valid for both chips */
-
- switch (va->mode) {
- case VIDEO_SOUND_MONO:
- dprintk("tda985x: VIDEO_SOUND_MONO\n");
- t->c6= TDA985x_MONO | (t->c6 & 0x3f);
- tda985x_write(client,TDA985x_C6,t->c6);
- break;
- case VIDEO_SOUND_STEREO:
- dprintk("tda985x: VIDEO_SOUND_STEREO\n");
- t->c6= TDA985x_STEREO | (t->c6 & 0x3f);
- tda985x_write(client,TDA985x_C6,t->c6);
- break;
- case VIDEO_SOUND_LANG1:
- dprintk("tda985x: VIDEO_SOUND_LANG1\n");
- t->c6= TDA985x_SAP | (t->c6 & 0x3f);
- tda985x_write(client,TDA985x_C6,t->c6);
- break;
- } /* End of (va->mode) switch */
-
- break;
-
- } /* end of VIDEOCSAUDIO case */
-
- default: /* Not VIDEOCGAUDIO or VIDEOCSAUDIO */
-
- /* nothing */
- d2printk("tda985x: Default\n");
-
- } /* end of (cmd) switch */
-
- return 0;
-}
-
-
-static struct i2c_driver driver = {
- "i2c tda985x driver",
- I2C_DRIVERID_TDA9855, /* Get new one for TDA985x? */
- I2C_DF_NOTIFY,
- tda985x_probe,
- tda985x_detach,
- tda985x_command,
-};
-
-static struct i2c_client client_template =
-{
- "(unset)", /* name */
- -1,
- 0,
- 0,
- NULL,
- &driver
-};
-
-#ifdef MODULE
-int init_module(void)
-#else
-int tda985x_init(void)
-#endif
-{
- if ( (chip != 9850) && (chip != 9855) )
- {
- printk(KERN_ERR "tda985x: chip parameter must be 9850 or 9855\n");
- return -EINVAL;
- }
- i2c_add_driver(&driver);
- return 0;
-}
-
-#ifdef MODULE
-void cleanup_module(void)
-{
- i2c_del_driver(&driver);
-}
-#endif
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
* Based on tda9855.c by Steve VanDeBogart (vandebo@uclink.berkeley.edu)
* Which was based on tda8425.c by Greg Alexander (c) 1998
*
- * Contributors:
- * Arnaldo Carvalho de Melo <acme@conectiva.com.br> (0.2)
- *
* OPTIONS:
* debug - set to 1 if you'd like to see debug messages
*
- * Revision 0.2 - resource allocation fixes in tda9875_attach (08/14/2000)
* Revision: 0.1 - original version
*/
#include "bttv.h"
#include "audiochip.h"
-
-/* This driver ID is brand new, so define it if it's not in i2c-id.h yet */
-#ifndef I2C_DRIVERID_TDA9875
- #define I2C_DRIVERID_TDA9875 28
-#endif
-
+#include "id.h"
MODULE_PARM(debug,"i");
int mode;
int rvol, lvol;
int bass, treble;
+ struct i2c_client c;
};
struct tda9875 *t;
struct i2c_client *client;
dprintk("In tda9875_attach\n");
- client = kmalloc(sizeof *client,GFP_KERNEL);
- if (!client)
- return -ENOMEM;
+
+ t = kmalloc(sizeof *t,GFP_KERNEL);
+ if (!t)
+ return -ENOMEM;
+ memset(t,0,sizeof *t);
+
+ client = &t->c;
memcpy(client,&client_template,sizeof(struct i2c_client));
client->adapter = adap;
client->addr = addr;
+ client->data = t;
- client->data = t = kmalloc(sizeof *t,GFP_KERNEL);
- if (!t) {
- kfree(client);
- return -ENOMEM;
- }
- memset(t,0,sizeof *t);
do_tda9875_init(client);
MOD_INC_USE_COUNT;
strcpy(client->name,"TDA9875");
i2c_detach_client(client);
kfree(t);
- kfree(client);
MOD_DEC_USE_COUNT;
return 0;
}
+++ /dev/null
-/*
- * for the TEA6300 chip (only found on Gateway STB TV/FM cards tho the best
- * of my knowledge)
- * WARNING: THIS DRIVER WILL LOAD WITHOUT COMPLAINTS EVEN IF THE WRONG
- * CHIP (i.e., an MSP3400) IS ON I2C ADDRESS 0x80 (it relies on i2c to
- * make sure that there is a device acknowledging that address). This
- * is a potential problem because the MSP3400 is very popular and does
- * use this address! You have been warned!
- *
- * Copyright (c) 1998 Greg Alexander <galexand@acm.org>
- * This code is placed under the terms of the GNU General Public License
- * Code liberally copied from msp3400.c, which is by Gerd Knorr
- *
- * All of this should work, though it would be nice to eventually support
- * balance (different left,right values) and, if someone ever finds a card
- * with the support (or if you're careful with a soldering iron), fade
- * (front/back).
- *
- * Changes:
- * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 08/14/2000
- * - resource allocation fixes in tea6300_attach
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/malloc.h>
-#include <linux/videodev.h>
-#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
-
-#include "bttv.h"
-#include "audiochip.h"
-
-
-/* Addresses to scan */
-#define I2C_TEA6300 0x80
-static unsigned short normal_i2c[] = {
- I2C_TEA6300 >> 1,
- I2C_CLIENT_END};
-static unsigned short normal_i2c_range[] = {I2C_CLIENT_END};
-static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
-static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
-static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
-static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
-static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
-static struct i2c_client_address_data addr_data = {
- normal_i2c, normal_i2c_range,
- probe, probe_range,
- ignore, ignore_range,
- force
-};
-
-
-MODULE_PARM(debug,"i");
-static int debug = 0; /* insmod parameter */
-
-#define dprintk if (debug) printk
-
-
-struct tea6300 {
- int mode; /* set to AUDIO_{OFF,TUNER,RADIO,EXTERN} */
- int stereo;
- __u16 left,right;
- __u16 bass,treble;
-};
-
-static struct i2c_driver driver;
-static struct i2c_client client_template;
-
-#define TEA6300_VL 0x00 /* volume left */
-#define TEA6300_VR 0x01 /* volume right */
-#define TEA6300_BA 0x02 /* bass */
-#define TEA6300_TR 0x03 /* treble */
-#define TEA6300_FA 0x04 /* fader control */
-#define TEA6300_S 0x05 /* switch register */
- /* values for those registers: */
-#define TEA6300_S_SA 0x01 /* stereo A input */
-#define TEA6300_S_SB 0x02 /* stereo B */
-#define TEA6300_S_SC 0x04 /* stereo C */
-#define TEA6300_S_GMU 0x80 /* general mute */
-
-
-/* ******************************** *
- * functions for talking to TEA6300 *
- * ******************************** */
-
-static int tea6300_write(struct i2c_client *client, int addr, int val)
-{
- unsigned char buffer[2];
-
- buffer[0] = addr;
- buffer[1] = val;
- if (2 != i2c_master_send(client,buffer,2)) {
- printk(KERN_WARNING "tea6300: I/O error, trying (write %d 0x%x)\n",
- addr, val);
- return -1;
- }
- return 0;
-}
-
-static void tea6300_set(struct i2c_client *client)
-{
- struct tea6300 *tea = client->data;
-
- /* mode is ignored today */
- dprintk(KERN_DEBUG "tea6300_set(%04x,%04x,%04x,%04x)\n",tea->left>>10,tea->right>>10,tea->bass>>12,tea->treble>>12);
- tea6300_write(client, TEA6300_VL, tea->left>>10 );
- tea6300_write(client, TEA6300_VR, tea->right>>10 );
- tea6300_write(client, TEA6300_BA, tea->bass>>12 );
- tea6300_write(client, TEA6300_TR, tea->treble>>12);
-}
-
-static void do_tea6300_init(struct i2c_client *client)
-{
- struct tea6300 *tea = client->data;
-
- tea->left=tea->right =49152; /* -10dB (loud enough, but not beyond
- normal line levels - so as to avoid
- clipping */
- tea->bass=tea->treble=28672; /* 0dB */
- tea->mode=AUDIO_OFF;
- tea->stereo=1;
- /* left=right=0x27<<10, bass=treble=0x07<<12 */
- tea6300_write(client, TEA6300_FA, 0x3f ); /* fader off */
- tea6300_write(client, TEA6300_S , TEA6300_S_GMU); /* mute */
- tea6300_set(client);
-}
-
-static void tea6300_audio(struct i2c_client *client, int mode)
-{
- struct tea6300 *tea = client->data;
-
- /* valid for AUDIO_TUNER, RADIO, EXTERN, OFF */
- dprintk(KERN_DEBUG "tea6300_audio:%d (T,R,E,I,O)\n",mode);
- tea->mode=mode;
- if (mode==AUDIO_OFF) { /* just mute it */
- tea6300_write(client, TEA6300_S, TEA6300_S_GMU);
- return;
- }
- switch(mode) {
- case AUDIO_TUNER:
- tea6300_write(client, TEA6300_S, TEA6300_S_SA);
- break;
- case AUDIO_RADIO:
- tea6300_write(client, TEA6300_S, TEA6300_S_SB);
- break;
- case AUDIO_EXTERN:
- tea6300_write(client, TEA6300_S, TEA6300_S_SC);
- break;
- }
-}
-
-
-/* *********************** *
- * i2c interface functions *
- * *********************** */
-
-static int tea6300_attach(struct i2c_adapter *adap, int addr,
- unsigned short flags, int kind)
-{
- struct tea6300 *tea;
- struct i2c_client *client;
-
- client = kmalloc(sizeof *client,GFP_KERNEL);
- if (!client)
- return -ENOMEM;
- memcpy(client,&client_template,sizeof(struct i2c_client));
- client->adapter = adap;
- client->addr = addr;
-
- client->data = tea = kmalloc(sizeof *tea,GFP_KERNEL);
- if (!tea) {
- kfree(client);
- return -ENOMEM;
- }
- memset(tea,0,sizeof *tea);
- do_tea6300_init(client);
-
- MOD_INC_USE_COUNT;
- strcpy(client->name,"TEA6300T");
- printk(KERN_INFO "tea6300: initialized\n");
-
- i2c_attach_client(client);
- return 0;
-}
-
-static int tea6300_probe(struct i2c_adapter *adap)
-{
- if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848))
- return i2c_probe(adap, &addr_data, tea6300_attach);
- return 0;
-}
-
-static int tea6300_detach(struct i2c_client *client)
-{
- struct tea6300 *tea = client->data;
-
- do_tea6300_init(client);
- i2c_detach_client(client);
-
- kfree(tea);
- kfree(client);
- MOD_DEC_USE_COUNT;
- return 0;
-}
-
-static int
-tea6300_command(struct i2c_client *client, unsigned int cmd, void *arg)
-{
- struct tea6300 *tea = client->data;
- __u16 *sarg = arg;
-
- switch (cmd) {
- case AUDC_SET_RADIO:
- tea6300_audio(client,AUDIO_RADIO);
- break;
- case AUDC_SET_INPUT:
- tea6300_audio(client,*sarg);
- break;
-
- /* --- v4l ioctls --- */
- /* take care: bttv does userspace copying, we'll get a
- kernel pointer here... */
- case VIDIOCGAUDIO:
- {
- struct video_audio *va = arg;
-
- va->flags |= VIDEO_AUDIO_VOLUME |
- VIDEO_AUDIO_BASS |
- VIDEO_AUDIO_TREBLE;
- va->volume=MAX(tea->left,tea->right);
- va->balance=(32768*MIN(tea->left,tea->right))/
- (va->volume ? va->volume : 1);
- va->balance=(tea->left<tea->right)?
- (65535-va->balance) : va->balance;
- va->bass = tea->bass;
- va->treble = tea->treble;
- break;
- }
- case VIDIOCSAUDIO:
- {
- struct video_audio *va = arg;
-
- tea->left = (MIN(65536 - va->balance,32768) *
- va->volume) / 32768;
- tea->right = (MIN(va->balance,32768) *
- va->volume) / 32768;
- tea->bass = va->bass;
- tea->treble = va->treble;
- tea6300_set(client);
- break;
- }
-#if 0
- /* --- old, obsolete interface --- */
- case AUDC_GET_VOLUME_LEFT:
- *sarg = tea->left;
- break;
- case AUDC_GET_VOLUME_RIGHT:
- *sarg = tea->right;
- break;
- case AUDC_SET_VOLUME_LEFT:
- tea->left = *sarg;
- tea6300_set(client);
- break;
- case AUDC_SET_VOLUME_RIGHT:
- tea->right = *sarg;
- tea6300_set(client);
- break;
-
- case AUDC_GET_BASS:
- *sarg = tea->bass;
- break;
- case AUDC_SET_BASS:
- tea->bass = *sarg;
- tea6300_set(client);
- break;
-
- case AUDC_GET_TREBLE:
- *sarg = tea->treble;
- break;
- case AUDC_SET_TREBLE:
- tea->treble = *sarg;
- tea6300_set(client);
- break;
-
- case AUDC_GET_STEREO:
- *sarg = tea->stereo?VIDEO_SOUND_STEREO:VIDEO_SOUND_MONO;
- break;
- case AUDC_SET_STEREO:
- tea->stereo=(*sarg==VIDEO_SOUND_MONO)?0:1;
- /* TODO: make this write to the TDA9850? */
- break;
-
-/* case AUDC_SWITCH_MUTE: someday, maybe -- not a lot of point to
- case AUDC_NEWCHANNEL: it and it would require preserving state
- case AUDC_GET_DC: huh?? (not used by bttv.c)
-*/
-#endif
- default:
- /* nothing */
- }
- return 0;
-}
-
-static struct i2c_driver driver = {
- "i2c tea6300 driver",
- I2C_DRIVERID_TEA6300,
- I2C_DF_NOTIFY,
- tea6300_probe,
- tea6300_detach,
- tea6300_command,
-};
-
-static struct i2c_client client_template =
-{
- "(unset)", /* name */
- -1,
- 0,
- 0,
- NULL,
- &driver
-};
-
-#ifdef MODULE
-int init_module(void)
-#else
-int tea6300_init(void)
-#endif
-{
- i2c_add_driver(&driver);
- return 0;
-}
-
-#ifdef MODULE
-void cleanup_module(void)
-{
- i2c_del_driver(&driver);
-}
-#endif
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
+++ /dev/null
-/*
- * for the TEA6420 chip (only found on 3DFX (STB) TV/FM cards to the best
- * of my knowledge)
- * Copyright (C) 2000 Dave Stuart <justdave@ynn.com>
- * This code is placed under the terms of the GNU General Public License
- * Code liberally copied from tea6300 by . . .
- *
- * Copyright (c) 1998 Greg Alexander <galexand@acm.org>
- * This code is placed under the terms of the GNU General Public License
- * Code liberally copied from msp3400.c, which is by Gerd Knorr
- *
- * Changes:
- * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 08/14/2000
- * - resource allocation fixes in tea6300_attach
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/malloc.h>
-#include <linux/videodev.h>
-#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
-
-#include "bttv.h"
-#include "audiochip.h"
-
-
-/* Addresses to scan */
-#define I2C_TEA6420 0x98
-static unsigned short normal_i2c[] = {
- I2C_TEA6420 >> 1,
- I2C_CLIENT_END};
-static unsigned short normal_i2c_range[] = {I2C_CLIENT_END};
-static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
-static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
-static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
-static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
-static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
-static struct i2c_client_address_data addr_data = {
- normal_i2c, normal_i2c_range,
- probe, probe_range,
- ignore, ignore_range,
- force
-};
-
-
-MODULE_PARM(debug,"i");
-static int debug = 0; /* insmod parameter */
-
-#define dprintk if (debug) printk
-
-
-struct tea6420 {
- int mode; /* set to AUDIO_{OFF,TUNER,RADIO,EXTERN} */
- int stereo;
-};
-
-static struct i2c_driver driver;
-static struct i2c_client client_template;
-
-#define TEA6420_S_SA 0x00 /* stereo A input */
-#define TEA6420_S_SB 0x01 /* stereo B */
-#define TEA6420_S_SC 0x02 /* stereo C */
-#define TEA6420_S_SD 0x03 /* stereo D */
-#define TEA6420_S_SE 0x04 /* stereo E */
-#define TEA6420_S_GMU 0x05 /* general mute */
-
-
-/* ******************************** *
- * functions for talking to TEA6420 *
- * ******************************** */
-
-static int tea6420_write(struct i2c_client *client, int val)
-{
- unsigned char buffer[2];
- int result;
-
-/* buffer[0] = addr; */
- buffer[0] = val;
- result = i2c_master_send(client,buffer,1);
- if (1 != result) {
- printk(KERN_WARNING "tea6420: I/O error, trying (write
-0x%x) result = %d\n", val, result);
- return -1;
- }
- return 0;
-}
-
-
-static void do_tea6420_init(struct i2c_client *client)
-{
- struct tea6420 *tea = client->data;
-
- tea->mode=AUDIO_OFF;
- tea->stereo=1;
- tea6420_write(client, TEA6420_S_GMU); /* mute */
-}
-
-static void tea6420_audio(struct i2c_client *client, int mode)
-{
- struct tea6420 *tea = client->data;
-
- /* valid for AUDIO_TUNER, RADIO, EXTERN, OFF */
- dprintk(KERN_DEBUG "tea6420_audio:%d (T,R,E,I,O)\n",mode);
- tea->mode=mode;
- if (mode==AUDIO_OFF) { /* just mute it */
- tea6420_write(client, TEA6420_S_GMU);
- return;
- }
- switch(mode) {
- case AUDIO_TUNER:
- tea6420_write(client, TEA6420_S_SA);
- break;
- case AUDIO_RADIO:
- tea6420_write(client, TEA6420_S_SB);
- break;
- case AUDIO_EXTERN:
- tea6420_write(client, TEA6420_S_SC);
- break;
- }
-}
-
-
-/* *********************** *
- * i2c interface functions *
- * *********************** */
-
-static int tea6420_attach(struct i2c_adapter *adap, int addr,
- unsigned short flags, int kind)
-{
- struct tea6420 *tea;
- struct i2c_client *client;
-
- client = kmalloc(sizeof *client,GFP_KERNEL);
- if (!client)
- return -ENOMEM;
- memcpy(client,&client_template,sizeof(struct i2c_client));
- client->adapter = adap;
- client->addr = addr;
-
- client->data = tea = kmalloc(sizeof *tea,GFP_KERNEL);
- if (!tea) {
- kfree(client);
- return -ENOMEM;
- }
- memset(tea,0,sizeof *tea);
- do_tea6420_init(client);
-
- MOD_INC_USE_COUNT;
- strcpy(client->name,"TEA6420");
- printk(KERN_INFO "tea6420: initialized\n");
-
- i2c_attach_client(client);
- return 0;
-}
-
-static int tea6420_probe(struct i2c_adapter *adap)
-{
- if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848))
- return i2c_probe(adap, &addr_data, tea6420_attach);
- return 0;
-}
-
-static int tea6420_detach(struct i2c_client *client)
-{
- struct tea6420 *tea = client->data;
-
- do_tea6420_init(client);
- i2c_detach_client(client);
-
- kfree(tea);
- kfree(client);
- MOD_DEC_USE_COUNT;
- return 0;
-}
-
-static int
-tea6420_command(struct i2c_client *client, unsigned int cmd, void *arg)
-{
- __u16 *sarg = arg;
-
- switch (cmd) {
- case AUDC_SET_RADIO:
- tea6420_audio(client,AUDIO_RADIO);
- break;
- case AUDC_SET_INPUT:
- tea6420_audio(client,*sarg);
- break;
-
- /* --- v4l ioctls --- */
- /* take care: bttv does userspace copying, we'll get a
- kernel pointer here... */
- case VIDIOCGAUDIO:
- {
- struct video_audio *va = arg;
-
- va->flags |= VIDEO_AUDIO_VOLUME |
- VIDEO_AUDIO_BASS |
- VIDEO_AUDIO_TREBLE;
-/* va->volume=MAX(tea->left,tea->right);
- va->balance=(32768*MIN(tea->left,tea->right))/
- (va->volume ? va->volume : 1);
- va->balance=(tea->left<tea->right)?
- (65535-va->balance) : va->balance;
- va->bass = tea->bass;
- va->treble = tea->treble;
-*/ break;
- }
- case VIDIOCSAUDIO:
- {
-
-/* tea->left = (MIN(65536 - va->balance,32768) *
- va->volume) / 32768;
- tea->right = (MIN(va->balance,32768) *
- va->volume) / 32768;
- tea->bass = va->bass;
- tea->treble = va->treble;
- tea6420_set(client);
-*/ break;
- }
-
-default:
- /* nothing */
- }
- return 0;
-}
-
-static struct i2c_driver driver = {
- "i2c tea6420 driver",
- I2C_DRIVERID_TEA6420,
- I2C_DF_NOTIFY,
- tea6420_probe,
- tea6420_detach,
- tea6420_command,
-};
-
-static struct i2c_client client_template =
-{
- "(unset)", /* name */
- -1,
- 0,
- 0,
- NULL,
- &driver
-};
-
-#ifdef MODULE
-int init_module(void)
-#else
-int tea6420_init(void)
-#endif
-{
- i2c_add_driver(&driver);
- return 0;
-}
-
-#ifdef MODULE
-void cleanup_module(void)
-{
- i2c_del_driver(&driver);
-}
-#endif
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
16*133.25,16*351.25,0x01,0x02,0x08,0x8e,608},
{ "Temic 4006FH5", TEMIC, PAL_I,
16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,623},
+ { "Alps TSCH6",Alps,NTSC,
+ 16*137.25,16*385.25,0x14,0x12,0x11,0x8e,732},
};
#define TUNERS (sizeof(tuners)/sizeof(struct tunertype))
#define TUNER_MODE 0x38
#define TUNER_AFC 0x07
+#define TUNER_STEREO 0x10 /* radio mode */
+#define TUNER_SIGNAL 0x07 /* radio mode */
+
+static int tuner_signal(struct i2c_client *c)
+{
+ return (tuner_getstatus(c) & TUNER_SIGNAL)<<13;
+}
+
+static int tuner_stereo(struct i2c_client *c)
+{
+ return (tuner_getstatus (c) & TUNER_STEREO);
+}
+
+
static int tuner_islocked (struct i2c_client *c)
{
return (tuner_getstatus (c) & TUNER_FL);
}
tun=&tuners[t->type];
- config = 0xa5;
+ config = 0xa4 /* 0xa5 */; /* bit 0 is AFC (set) vs. RF-Signal (clear) */
div=freq + (int)(16*10.7);
div&=0x7fff;
printk ("tuner: PLL locked\n");
else
printk ("tuner: PLL not locked\n");
-
- printk ("tuner: AFC: %d\n", tuner_afcstatus (c));
+
+ if (config & 1) {
+ printk ("tuner: AFC: %d\n", tuner_afcstatus(c));
+ } else {
+ printk ("tuner: Signal: %d\n", tuner_signal(c));
+ }
}
}
/* ---------------------------------------------------------------------- */
}
return 0;
}
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner *vt = arg;
+
+ if (t->radio)
+ vt->signal = tuner_signal(client);
+ return 0;
+ }
+ case VIDIOCGAUDIO:
+ {
+ struct video_audio *va = arg;
+ if (t->radio)
+ va->mode = (tuner_stereo(client) ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO);
+ return 0;
+ }
+
#if 0
/* --- old, obsolete interface --- */
case TUNER_SET_TVFREQ:
#define TUNER_ALPS_TSBB5_PAL_I 11
#define TUNER_ALPS_TSBE5_PAL 12
#define TUNER_ALPS_TSBC5_PAL 13
+#define TUNER_TEMIC_4006FH5_PAL 14
+#define TUNER_ALPS_TSHC6_NTSC 15
#define NOTUNER 0
#define PAL 1
--- /dev/null
+/*
+ * experimental driver for simple i2c audio chips.
+ *
+ * Copyright (c) 2000 Gerd Knorr
+ * based on code by:
+ * Eric Sandeen (eric_sandeen@bigfoot.com)
+ * Steve VanDeBogart (vandebo@uclink.berkeley.edu)
+ * Greg Alexander (galexand@acm.org)
+ *
+ * This code is placed under the terms of the GNU General Public License
+ *
+ * OPTIONS:
+ * debug - set to 1 if you'd like to see debug messages
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/videodev.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+
+#include "audiochip.h"
+#include "tvaudio.h"
+#include "id.h"
+
+
+/* ---------------------------------------------------------------------- */
+/* insmod args */
+
+MODULE_PARM(debug,"i");
+static int debug = 0; /* insmod parameter */
+
+#define dprintk if (debug) printk
+
+
+/* ---------------------------------------------------------------------- */
+/* our structs */
+
+#define MAXREGS 64
+
+struct CHIPSTATE;
+typedef int (*getvalue)(int);
+typedef int (*checkit)(struct CHIPSTATE*);
+typedef int (*getmode)(struct CHIPSTATE*);
+typedef void (*setmode)(struct CHIPSTATE*, int mode);
+typedef void (*checkmode)(struct CHIPSTATE*);
+
+/* i2c command */
+typedef struct AUDIOCMD {
+ int count; /* # of bytes to send */
+ unsigned char bytes[MAXREGS+1]; /* addr, data, data, ... */
+} audiocmd;
+
+/* chip description */
+struct CHIPDESC {
+ char *name; /* chip name */
+ int id; /* ID */
+ int addr_lo, addr_hi; /* i2c address range */
+ int registers; /* # of registers */
+
+ int *insmodopt;
+ checkit checkit;
+ int flags;
+#define CHIP_HAS_VOLUME 1
+#define CHIP_HAS_BASSTREBLE 2
+#define CHIP_HAS_INPUTSEL 4
+
+ /* various i2c command sequences */
+ audiocmd init;
+
+ /* which register has which value */
+ int leftreg,rightreg,treblereg,bassreg;
+
+ /* initialize with (defaults to 65535/65535/32768/32768 */
+ int leftinit,rightinit,trebleinit,bassinit;
+
+ /* functions to convert the values (v4l -> chip) */
+ getvalue volfunc,treblefunc,bassfunc;
+
+ /* get/set mode */
+ getmode getmode;
+ setmode setmode;
+
+ /* check / autoswitch audio after channel switches */
+ checkmode checkmode;
+
+ /* input switch register + values for v4l inputs */
+ int inputreg;
+ int inputmap[8];
+ int inputmute;
+};
+static struct CHIPDESC chiplist[];
+
+/* current state of the chip */
+struct CHIPSTATE {
+ struct i2c_client c;
+
+ /* index into CHIPDESC array */
+ int type;
+
+ /* shadow register set */
+ audiocmd shadow;
+
+ /* current settings */
+ __u16 left,right,treble,bass;
+
+ /* thread */
+ struct task_struct *thread;
+ struct semaphore *notify;
+ wait_queue_head_t wq;
+ int wake,done;
+};
+
+
+/* ---------------------------------------------------------------------- */
+/* i2c adresses */
+
+static unsigned short normal_i2c[] = {
+ I2C_TDA8425 >> 1,
+ I2C_TEA6300 >> 1,
+ I2C_TEA6420 >> 1,
+ I2C_TDA9840 >> 1,
+ I2C_TDA985x_L >> 1,
+ I2C_TDA985x_H >> 1,
+ I2C_PIC16C54 >> 1,
+ I2C_CLIENT_END };
+static unsigned short normal_i2c_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
+static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
+static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
+static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
+static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
+static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
+static struct i2c_client_address_data addr_data = {
+ normal_i2c, normal_i2c_range,
+ probe, probe_range,
+ ignore, ignore_range,
+ force
+};
+
+static struct i2c_driver driver;
+static struct i2c_client client_template;
+
+
+/* ---------------------------------------------------------------------- */
+/* i2c I/O functions */
+
+static int chip_write(struct CHIPSTATE *chip, int subaddr, int val)
+{
+ unsigned char buffer[2];
+
+ if (-1 == subaddr) {
+ dprintk("%s: chip_write: 0x%x\n", chip->c.name, val);
+ chip->shadow.bytes[1] = val;
+ buffer[0] = val;
+ if (1 != i2c_master_send(&chip->c,buffer,1)) {
+ printk(KERN_WARNING "%s: I/O error (write 0x%x)\n",
+ chip->c.name, val);
+ return -1;
+ }
+ } else {
+ dprintk("%s: chip_write: reg%d=0x%x\n", chip->c.name, subaddr, val);
+ chip->shadow.bytes[subaddr+1] = val;
+ buffer[0] = subaddr;
+ buffer[1] = val;
+ if (2 != i2c_master_send(&chip->c,buffer,2)) {
+ printk(KERN_WARNING "%s: I/O error (write reg%d=0x%x)\n",
+ chip->c.name, subaddr, val);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int chip_read(struct CHIPSTATE *chip)
+{
+ unsigned char buffer;
+
+ if (1 != i2c_master_recv(&chip->c,&buffer,1)) {
+ printk(KERN_WARNING "%s: I/O error (read)\n",
+ chip->c.name);
+ return -1;
+ }
+ dprintk("%s: chip_read: 0x%x\n",chip->c.name,buffer);
+ return buffer;
+}
+
+static int chip_read2(struct CHIPSTATE *chip, int subaddr)
+{
+ unsigned char write[1];
+ unsigned char read[1];
+ struct i2c_msg msgs[2] = {
+ { chip->c.addr, 0, 1, write },
+ { chip->c.addr, I2C_M_RD, 1, read }
+ };
+ write[1] = subaddr;
+
+ if (2 != i2c_transfer(chip->c.adapter,msgs,2)) {
+ printk(KERN_WARNING "%s: I/O error (read2)\n",
+ chip->c.name);
+ return -1;
+ }
+ dprintk("%s: chip_read2: reg%d=0x%x\n",
+ chip->c.name,subaddr,read[0]);
+ return read[0];
+}
+
+static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd)
+{
+ int i;
+
+ if (0 == cmd->count)
+ return 0;
+
+ /* update our shadow register set; print bytes if (debug > 0) */
+ dprintk("%s: chip_cmd(%s): reg=%d, data:",
+ chip->c.name,name,cmd->bytes[0]);
+ for (i = 1; i < cmd->count; i++) {
+ dprintk(" 0x%x",cmd->bytes[i]);
+ chip->shadow.bytes[i+cmd->bytes[0]] = cmd->bytes[i];
+ }
+ dprintk("\n");
+
+ /* send data to the chip */
+ if (cmd->count != i2c_master_send(&chip->c,cmd->bytes,cmd->count)) {
+ printk(KERN_WARNING "%s: I/O error (%s)\n", chip->c.name, name);
+ return -1;
+ }
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+/* kernel thread for doing i2c stuff asyncronly
+ * right now it is used only to check the audio mode (mono/stereo/whatever)
+ * some time after switching to another TV channel, then turn on stereo
+ * if available, ...
+ */
+
+static int chip_thread(void *data)
+{
+ struct CHIPSTATE *chip = data;
+ struct CHIPDESC *desc = chiplist + chip->type;
+
+#ifdef CONFIG_SMP
+ lock_kernel();
+#endif
+ daemonize();
+ sigfillset(¤t->blocked);
+ strcpy(current->comm,chip->c.name);
+ chip->thread = current;
+#ifdef CONFIG_SMP
+ unlock_kernel();
+#endif
+
+ dprintk("%s: thread started\n", chip->c.name);
+ if(chip->notify != NULL)
+ up(chip->notify);
+
+ for (;;) {
+ if (chip->done)
+ break;
+ if (!chip->wake) {
+ interruptible_sleep_on(&chip->wq);
+ dprintk("%s: thread wakeup\n", chip->c.name);
+ if (chip->done || signal_pending(current))
+ break;
+ }
+ chip->wake = 0;
+
+ /* wait some time -- let the audio hardware
+ figure the current mode */
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(HZ);
+ if (signal_pending(current))
+ break;
+ if (chip->wake)
+ continue;
+
+ dprintk("%s: thread checkmode\n", chip->c.name);
+ desc->checkmode(chip);
+ }
+
+ chip->thread = NULL;
+ dprintk("%s: thread exiting\n", chip->c.name);
+ if(chip->notify != NULL)
+ up(chip->notify);
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+/* audio chip descriptions - defines+functions for tda9840 */
+
+#define TDA9840_SW 0x00
+#define TDA9840_LVADJ 0x02
+#define TDA9840_STADJ 0x03
+#define TDA9840_TEST 0x04
+
+#define TDA9840_MONO 0x10
+#define TDA9840_STEREO 0x2a
+#define TDA9840_DUALA 0x12
+#define TDA9840_DUALB 0x1e
+#define TDA9840_DUALAB 0x1a
+#define TDA9840_DUALBA 0x16
+#define TDA9840_EXTERNAL 0x7a
+
+int tda9840_getmode(struct CHIPSTATE *chip)
+{
+ return VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
+ VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
+}
+
+void tda9840_setmode(struct CHIPSTATE *chip, int mode)
+{
+ int update = 1;
+ int t = chip->shadow.bytes[TDA9840_SW + 1] & ~0x7e;
+
+ switch (mode) {
+ case VIDEO_SOUND_MONO:
+ t |= TDA9840_MONO;
+ break;
+ case VIDEO_SOUND_STEREO:
+ t |= TDA9840_STEREO;
+ break;
+ case VIDEO_SOUND_LANG1:
+ t |= TDA9840_DUALA;
+ break;
+ case VIDEO_SOUND_LANG2:
+ t |= TDA9840_DUALB;
+ break;
+ default:
+ update = 0;
+ }
+
+ if (update)
+ chip_write(chip, TDA9840_SW, t);
+}
+
+/* ---------------------------------------------------------------------- */
+/* audio chip descriptions - defines+functions for tda985x */
+
+/* subaddresses for TDA9855 */
+#define TDA9855_VR 0x00 /* Volume, right */
+#define TDA9855_VL 0x01 /* Volume, left */
+#define TDA9855_BA 0x02 /* Bass */
+#define TDA9855_TR 0x03 /* Treble */
+#define TDA9855_SW 0x04 /* Subwoofer - not connected on DTV2000 */
+
+/* subaddresses for TDA9850 */
+#define TDA9850_C4 0x04 /* Control 1 for TDA9850 */
+
+/* subaddesses for both chips */
+#define TDA985x_C5 0x05 /* Control 2 for TDA9850, Control 1 for TDA9855 */
+#define TDA985x_C6 0x06 /* Control 3 for TDA9850, Control 2 for TDA9855 */
+#define TDA985x_C7 0x07 /* Control 4 for TDA9850, Control 3 for TDA9855 */
+#define TDA985x_A1 0x08 /* Alignment 1 for both chips */
+#define TDA985x_A2 0x09 /* Alignment 2 for both chips */
+#define TDA985x_A3 0x0a /* Alignment 3 for both chips */
+
+/* Masks for bits in TDA9855 subaddresses */
+/* 0x00 - VR in TDA9855 */
+/* 0x01 - VL in TDA9855 */
+/* lower 7 bits control gain from -71dB (0x28) to 16dB (0x7f)
+ * in 1dB steps - mute is 0x27 */
+
+
+/* 0x02 - BA in TDA9855 */
+/* lower 5 bits control bass gain from -12dB (0x06) to 16.5dB (0x19)
+ * in .5dB steps - 0 is 0x0E */
+
+
+/* 0x03 - TR in TDA9855 */
+/* 4 bits << 1 control treble gain from -12dB (0x3) to 12dB (0xb)
+ * in 3dB steps - 0 is 0x7 */
+
+/* Masks for bits in both chips' subaddresses */
+/* 0x04 - SW in TDA9855, C4/Control 1 in TDA9850 */
+/* Unique to TDA9855: */
+/* 4 bits << 2 control subwoofer/surround gain from -14db (0x1) to 14db (0xf)
+ * in 3dB steps - mute is 0x0 */
+
+/* Unique to TDA9850: */
+/* lower 4 bits control stereo noise threshold, over which stereo turns off
+ * set to values of 0x00 through 0x0f for Ster1 through Ster16 */
+
+
+/* 0x05 - C5 - Control 1 in TDA9855 , Control 2 in TDA9850*/
+/* Unique to TDA9855: */
+#define TDA9855_MUTE 1<<7 /* GMU, Mute at outputs */
+#define TDA9855_AVL 1<<6 /* AVL, Automatic Volume Level */
+#define TDA9855_LOUD 1<<5 /* Loudness, 1==off */
+#define TDA9855_SUR 1<<3 /* Surround / Subwoofer 1==.5(L-R) 0==.5(L+R) */
+ /* Bits 0 to 3 select various combinations
+ * of line in and line out, only the
+ * interesting ones are defined */
+#define TDA9855_EXT 1<<2 /* Selects inputs LIR and LIL. Pins 41 & 12 */
+#define TDA9855_INT 0 /* Selects inputs LOR and LOL. (internal) */
+
+/* Unique to TDA9850: */
+/* lower 4 bits contol SAP noise threshold, over which SAP turns off
+ * set to values of 0x00 through 0x0f for SAP1 through SAP16 */
+
+
+/* 0x06 - C6 - Control 2 in TDA9855, Control 3 in TDA9850 */
+/* Common to TDA9855 and TDA9850: */
+#define TDA985x_SAP 3<<6 /* Selects SAP output, mute if not received */
+#define TDA985x_STEREO 1<<6 /* Selects Stereo ouput, mono if not received */
+#define TDA985x_MONO 0 /* Forces Mono output */
+#define TDA985x_LMU 1<<3 /* Mute (LOR/LOL for 9855, OUTL/OUTR for 9850) */
+
+/* Unique to TDA9855: */
+#define TDA9855_TZCM 1<<5 /* If set, don't mute till zero crossing */
+#define TDA9855_VZCM 1<<4 /* If set, don't change volume till zero crossing*/
+#define TDA9855_LINEAR 0 /* Linear Stereo */
+#define TDA9855_PSEUDO 1 /* Pseudo Stereo */
+#define TDA9855_SPAT_30 2 /* Spatial Stereo, 30% anti-phase crosstalk */
+#define TDA9855_SPAT_50 3 /* Spatial Stereo, 52% anti-phase crosstalk */
+#define TDA9855_E_MONO 7 /* Forced mono - mono select elseware, so useless*/
+
+/* 0x07 - C7 - Control 3 in TDA9855, Control 4 in TDA9850 */
+/* Common to both TDA9855 and TDA9850: */
+/* lower 4 bits control input gain from -3.5dB (0x0) to 4dB (0xF)
+ * in .5dB steps - 0dB is 0x7 */
+
+/* 0x08, 0x09 - A1 and A2 (read/write) */
+/* Common to both TDA9855 and TDA9850: */
+/* lower 5 bites are wideband and spectral expander alignment
+ * from 0x00 to 0x1f - nominal at 0x0f and 0x10 (read/write) */
+#define TDA985x_STP 1<<5 /* Stereo Pilot/detect (read-only) */
+#define TDA985x_SAPP 1<<6 /* SAP Pilot/detect (read-only) */
+#define TDA985x_STS 1<<7 /* Stereo trigger 1= <35mV 0= <30mV (write-only)*/
+
+/* 0x0a - A3 */
+/* Common to both TDA9855 and TDA9850: */
+/* lower 3 bits control timing current for alignment: -30% (0x0), -20% (0x1),
+ * -10% (0x2), nominal (0x3), +10% (0x6), +20% (0x5), +30% (0x4) */
+#define TDA985x_ADJ 1<<7 /* Stereo adjust on/off (wideband and spectral */
+
+int tda9855_volume(int val) { return val/0x2e8+0x27; }
+int tda9855_bass(int val) { return val/0xccc+0x06; }
+int tda9855_treble(int val) { return (val/0x1c71+0x3)<<1; }
+
+int tda985x_getmode(struct CHIPSTATE *chip)
+{
+ int mode;
+
+ mode = ((TDA985x_STP | TDA985x_SAPP) &
+ chip_read(chip)) >> 4;
+ /* Add mono mode regardless of SAP and stereo */
+ /* Allows forced mono */
+ return mode | VIDEO_SOUND_MONO;
+}
+
+void tda985x_setmode(struct CHIPSTATE *chip, int mode)
+{
+ int update = 1;
+ int c6 = chip->shadow.bytes[TDA985x_C6+1] & 0x3f;
+
+ switch (mode) {
+ case VIDEO_SOUND_MONO:
+ c6 |= TDA985x_MONO;
+ break;
+ case VIDEO_SOUND_STEREO:
+ c6 |= TDA985x_STEREO;
+ break;
+ case VIDEO_SOUND_LANG1:
+ c6 |= TDA985x_SAP;
+ break;
+ default:
+ update = 0;
+ }
+ if (update)
+ chip_write(chip,TDA985x_C6,c6);
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* audio chip descriptions - defines+functions for tda9873h */
+
+/* Subaddresses for TDA9873H */
+
+#define TDA9873_SW 0x00 /* Switching */
+#define TDA9873_AD 0x01 /* Adjust */
+#define TDA9873_PT 0x02 /* Port */
+
+/* Subaddress 0x00: Switching Data
+ * B7..B0:
+ *
+ * B1, B0: Input source selection
+ * 0, 0 internal
+ * 1, 0 external stereo
+ * 0, 1 external mono
+ */
+#define TDA9873_INTERNAL 0
+#define TDA9873_EXT_STEREO 2
+#define TDA9873_EXT_MONO 1
+
+/* B3, B2: output signal select
+ * B4 : transmission mode
+ * 0, 0, 1 Mono
+ * 1, 0, 0 Stereo
+ * 1, 1, 1 Stereo (reversed channel)
+ * 0, 0, 0 Dual AB
+ * 0, 0, 1 Dual AA
+ * 0, 1, 0 Dual BB
+ * 0, 1, 1 Dual BA
+ */
+
+#define TDA9873_TR_MONO 4
+#define TDA9873_TR_STEREO 1 << 4
+#define TDA9873_TR_REVERSE (1 << 3) & (1 << 2)
+#define TDA9873_TR_DUALA 1 << 2
+#define TDA9873_TR_DUALB 1 << 3
+
+/* output level controls
+ * B5: output level switch (0 = reduced gain, 1 = normal gain)
+ * B6: mute (1 = muted)
+ * B7: auto-mute (1 = auto-mute enabled)
+ */
+
+#define TDA9873_GAIN_NORMAL 1 << 5
+#define TDA9873_MUTE 1 << 6
+#define TDA9873_AUTOMUTE 1 << 7
+
+/* Subaddress 0x01: Adjust/standard */
+
+/* Lower 4 bits (C3..C0) control stereo adjustment on R channel (-0.6 - +0.7 dB)
+ * Recommended value is +0 dB
+ */
+
+#define TDA9873_STEREO_ADJ 0x06 /* 0dB gain */
+
+/* Bits C6..C4 control FM stantard
+ * C6, C5, C4
+ * 0, 0, 0 B/G (PAL FM)
+ * 0, 0, 1 M
+ * 0, 1, 0 D/K(1)
+ * 0, 1, 1 D/K(2)
+ * 1, 0, 0 D/K(3)
+ * 1, 0, 1 I
+ */
+#define TDA9873_BG 0
+#define TDA9873_M 1
+#define TDA9873_DK1 2
+#define TDA9873_DK2 3
+#define TDA9873_DK3 4
+#define TDA9873_I 5
+
+/* C7 controls identification response time (1=fast/0=normal)
+ */
+#define TDA9873_IDR_NORM 0
+#define TDA9873_IDR_FAST 1 << 7
+
+
+/* Subaddress 0x02: Port data */
+
+/* E1, E0 free programmable ports P1/P2
+ 0, 0 both ports low
+ 0, 1 P1 high
+ 1, 0 P2 high
+ 1, 1 both ports high
+*/
+
+#define TDA9873_PORTS 3
+
+/* E2: test port */
+#define TDA9873_TST_PORT 1 << 2
+
+/* E5..E3 control mono output channel (together with transmission mode bit B4)
+ *
+ * E5 E4 E3 B4 OUTM
+ * 0 0 0 0 mono
+ * 0 0 1 0 DUAL B
+ * 0 1 0 1 mono (from stereo decoder)
+ */
+#define TDA9873_MOUT_MONO 0
+#define TDA9873_MOUT_FMONO 0
+#define TDA9873_MOUT_DUALA 0
+#define TDA9873_MOUT_DUALB 1 << 3
+#define TDA9873_MOUT_ST 1 << 4
+#define TDA9873_MOUT_EXTM (1 << 4 ) & (1 << 3)
+#define TDA9873_MOUT_EXTL 1 << 5
+#define TDA9873_MOUT_EXTR (1 << 5 ) & (1 << 3)
+#define TDA9873_MOUT_EXTLR (1 << 5 ) & (1 << 4)
+#define TDA9873_MOUT_MUTE (1 << 5 ) & (1 << 4) & (1 << 3)
+
+/* Status bits: (chip read) */
+#define TDA9873_PONR 0 /* Power-on reset detected if = 1 */
+#define TDA9873_STEREO 2 /* Stereo sound is identified */
+#define TDA9873_DUAL 4 /* Dual sound is identified */
+
+int tda9873_getmode(struct CHIPSTATE *chip)
+{
+ int val,mode;
+
+ val = chip_read(chip);
+ mode = VIDEO_SOUND_MONO;
+ if (val & TDA9873_STEREO)
+ mode |= VIDEO_SOUND_STEREO;
+ if (val & TDA9873_DUAL)
+ mode |= VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
+ dprintk ("tda9873_getmode(): raw chip read: %d, return: %d\n",
+ val, mode);
+ return mode;
+}
+
+void tda9873_setmode(struct CHIPSTATE *chip, int mode)
+{
+ int sw_data = chip->shadow.bytes[TDA9873_SW+1] & 0xe3;
+ /* int adj_data = chip->shadow.bytes[TDA9873_AD+1] ; */
+ dprintk("tda9873_setmode(): chip->shadow.bytes[%d] = %d\n", TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]);
+ dprintk("tda9873_setmode(): sw_data = %d\n", sw_data);
+
+ switch (mode) {
+ case VIDEO_SOUND_MONO:
+ sw_data |= TDA9873_TR_MONO;
+ break;
+ case VIDEO_SOUND_STEREO:
+ sw_data |= TDA9873_TR_STEREO;
+ break;
+ case VIDEO_SOUND_LANG1:
+ sw_data |= TDA9873_TR_DUALA;
+ break;
+ case VIDEO_SOUND_LANG2:
+ sw_data |= TDA9873_TR_DUALB;
+ break;
+ }
+ dprintk("tda9873_setmode(): req. mode %d; chip_write: %d\n", mode, sw_data);
+ chip_write(chip,TDA9873_SW,sw_data);
+}
+
+void tda9873_checkmode(struct CHIPSTATE *chip)
+{
+ int mode = tda9873_getmode(chip);
+
+ if (mode & VIDEO_SOUND_STEREO)
+ tda9873_setmode(chip,VIDEO_SOUND_STEREO);
+ if (mode & VIDEO_SOUND_LANG1)
+ tda9873_setmode(chip,VIDEO_SOUND_LANG1);
+}
+
+int tda9873_checkit(struct CHIPSTATE *chip)
+{
+ int rc;
+
+ if (-1 == (rc = chip_read2(chip,254)))
+ return 0;
+ return (rc & ~0x1f) == 0x80;
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* audio chip descriptions - defines+functions for tea6420 */
+
+#define TEA6300_VL 0x00 /* volume left */
+#define TEA6300_VR 0x01 /* volume right */
+#define TEA6300_BA 0x02 /* bass */
+#define TEA6300_TR 0x03 /* treble */
+#define TEA6300_FA 0x04 /* fader control */
+#define TEA6300_S 0x05 /* switch register */
+ /* values for those registers: */
+#define TEA6300_S_SA 0x01 /* stereo A input */
+#define TEA6300_S_SB 0x02 /* stereo B */
+#define TEA6300_S_SC 0x04 /* stereo C */
+#define TEA6300_S_GMU 0x80 /* general mute */
+
+#define TEA6420_S_SA 0x00 /* stereo A input */
+#define TEA6420_S_SB 0x01 /* stereo B */
+#define TEA6420_S_SC 0x02 /* stereo C */
+#define TEA6420_S_SD 0x03 /* stereo D */
+#define TEA6420_S_SE 0x04 /* stereo E */
+#define TEA6420_S_GMU 0x05 /* general mute */
+
+int tea6300_shift10(int val) { return val >> 10; }
+int tea6300_shift12(int val) { return val >> 12; }
+
+
+/* ---------------------------------------------------------------------- */
+/* audio chip descriptions - defines+functions for tda8425 */
+
+#define TDA8425_VL 0x00 /* volume left */
+#define TDA8425_VR 0x01 /* volume right */
+#define TDA8425_BA 0x02 /* bass */
+#define TDA8425_TR 0x03 /* treble */
+#define TDA8425_S1 0x08 /* switch functions */
+ /* values for those registers: */
+#define TDA8425_S1_OFF 0xEE /* audio off (mute on) */
+#define TDA8425_S1_ON 0xCE /* audio on (mute off) - "linear stereo" mode */
+
+int tda8425_shift10(int val) { return val >> 10 | 0xc0; }
+int tda8425_shift12(int val) { return val >> 12 | 0xf0; }
+
+
+/* ---------------------------------------------------------------------- */
+/* audio chip descriptions - defines+functions for pic16c54 (PV951) */
+
+/* the registers of 16C54, I2C sub address. */
+#define PIC16C54_REG_KEY_CODE 0x01 /* Not use. */
+#define PIC16C54_REG_MISC 0x02
+
+/* bit definition of the RESET register, I2C data. */
+#define PIC16C54_MISC_RESET_REMOTE_CTL 0x01 /* bit 0, Reset to receive the key */
+ /* code of remote controller */
+#define PIC16C54_MISC_MTS_MAIN 0x02 /* bit 1 */
+#define PIC16C54_MISC_MTS_SAP 0x04 /* bit 2 */
+#define PIC16C54_MISC_MTS_BOTH 0x08 /* bit 3 */
+#define PIC16C54_MISC_SND_MUTE 0x10 /* bit 4, Mute Audio(Line-in and Tuner) */
+#define PIC16C54_MISC_SND_NOTMUTE 0x20 /* bit 5 */
+#define PIC16C54_MISC_SWITCH_TUNER 0x40 /* bit 6 , Switch to Line-in */
+#define PIC16C54_MISC_SWITCH_LINE 0x80 /* bit 7 , Switch to Tuner */
+
+
+/* ---------------------------------------------------------------------- */
+/* audio chip descriptions - struct CHIPDESC */
+
+/* insmod options to enable/disable individual audio chips */
+int tda8425 = 1;
+int tda9840 = 1;
+int tda9850 = 1;
+int tda9855 = 1;
+int tda9873 = 1;
+int tea6300 = 0;
+int tea6420 = 1;
+int pic16c54 = 1;
+MODULE_PARM(tda8425,"i");
+MODULE_PARM(tda9840,"i");
+MODULE_PARM(tda9850,"i");
+MODULE_PARM(tda9855,"i");
+MODULE_PARM(tda9873,"i");
+MODULE_PARM(tea6300,"i");
+MODULE_PARM(tea6420,"i");
+MODULE_PARM(pic16c54,"i");
+
+static struct CHIPDESC chiplist[] = {
+ {
+ name: "tda9840",
+ id: I2C_DRIVERID_TDA9840,
+ insmodopt: &tda9840,
+ addr_lo: I2C_TDA9840 >> 1,
+ addr_hi: I2C_TDA9840 >> 1,
+ registers: 5,
+
+ getmode: tda9840_getmode,
+ setmode: tda9840_setmode,
+
+ init: { 2, { TDA9840_SW, 0x2a } }
+ },
+ {
+ name: "tda9873h",
+ id: I2C_DRIVERID_TDA9873,
+ checkit: tda9873_checkit,
+ insmodopt: &tda9873,
+ addr_lo: I2C_TDA985x_L >> 1,
+ addr_hi: I2C_TDA985x_H >> 1,
+ registers: 3,
+
+ getmode: tda9873_getmode,
+ setmode: tda9873_setmode,
+ checkmode: tda9873_checkmode,
+
+ init: { 4, { TDA9873_SW, 0xa0, 0x06, 0x03 } }
+ },
+ {
+ name: "tda9850",
+ id: I2C_DRIVERID_TDA9850,
+ insmodopt: &tda9850,
+ addr_lo: I2C_TDA985x_L >> 1,
+ addr_hi: I2C_TDA985x_H >> 1,
+ registers: 11,
+
+ getmode: tda985x_getmode,
+ setmode: tda985x_setmode,
+
+ init: { 8, { TDA9850_C4, 0x08, 0x08, TDA985x_STEREO, 0x07, 0x10, 0x10, 0x03 } }
+ },
+ {
+ name: "tda9855",
+ id: I2C_DRIVERID_TDA9855,
+ insmodopt: &tda9855,
+ addr_lo: I2C_TDA985x_L >> 1,
+ addr_hi: I2C_TDA985x_H >> 1,
+ registers: 11,
+ flags: CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE,
+
+ leftreg: TDA9855_VR,
+ rightreg: TDA9855_VL,
+ bassreg: TDA9855_BA,
+ treblereg: TDA9855_TR,
+ volfunc: tda9855_volume,
+ bassfunc: tda9855_bass,
+ treblefunc: tda9855_treble,
+
+ getmode: tda985x_getmode,
+ setmode: tda985x_setmode,
+
+ init: { 12, { 0, 0x6f, 0x6f, 0x0e, 0x07<<1, 0x8<<2,
+ TDA9855_MUTE | TDA9855_AVL | TDA9855_LOUD | TDA9855_INT,
+ TDA985x_STEREO | TDA9855_LINEAR | TDA9855_TZCM | TDA9855_VZCM,
+ 0x07, 0x10, 0x10, 0x03 }}
+ },
+ {
+ name: "tea6300",
+ id: I2C_DRIVERID_TEA6300,
+ insmodopt: &tea6300,
+ addr_lo: I2C_TEA6300 >> 1,
+ addr_hi: I2C_TEA6300 >> 1,
+ registers: 6,
+ flags: CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL,
+
+ leftreg: TEA6300_VR,
+ rightreg: TEA6300_VL,
+ bassreg: TEA6300_BA,
+ treblereg: TEA6300_TR,
+ volfunc: tea6300_shift10,
+ bassfunc: tea6300_shift12,
+ treblefunc: tea6300_shift12,
+
+ inputreg: TEA6300_S,
+ inputmap: { TEA6300_S_SA, TEA6300_S_SB, TEA6300_S_SC },
+ inputmute: TEA6300_S_GMU,
+ },
+ {
+ name: "tea6420",
+ id: I2C_DRIVERID_TEA6420,
+ insmodopt: &tea6420,
+ addr_lo: I2C_TEA6420 >> 1,
+ addr_hi: I2C_TEA6420 >> 1,
+ registers: 1,
+ flags: CHIP_HAS_INPUTSEL,
+
+ inputreg: -1,
+ inputmap: { TEA6420_S_SA, TEA6420_S_SB, TEA6420_S_SC },
+ inputmute: TEA6300_S_GMU,
+ },
+ {
+ name: "tda8425",
+ id: I2C_DRIVERID_TDA8425,
+ insmodopt: &tda8425,
+ addr_lo: I2C_TDA8425 >> 1,
+ addr_hi: I2C_TDA8425 >> 1,
+ registers: 9,
+ flags: CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL,
+
+ leftreg: TDA8425_VR,
+ rightreg: TDA8425_VL,
+ bassreg: TDA8425_BA,
+ treblereg: TDA8425_TR,
+ volfunc: tda8425_shift10,
+ bassfunc: tda8425_shift12,
+ treblefunc: tda8425_shift12,
+
+ inputreg: TDA8425_S1,
+ inputmap: { TDA8425_S1_ON, TDA8425_S1_ON, TDA8425_S1_ON },
+ inputmute: TDA8425_S1_OFF,
+ },
+ {
+ name: "pic16c54 (PV951)",
+ id: I2C_DRIVERID_PIC16C54_PV951,
+ insmodopt: &pic16c54,
+ addr_lo: I2C_PIC16C54 >> 1,
+ addr_hi: I2C_PIC16C54>> 1,
+ registers: 2,
+ flags: CHIP_HAS_INPUTSEL,
+
+ inputreg: PIC16C54_REG_MISC,
+ inputmap: {PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_TUNER,
+ PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_LINE},
+ inputmute: PIC16C54_MISC_SND_MUTE,
+ },
+ { name: NULL } /* EOF */
+};
+
+
+/* ---------------------------------------------------------------------- */
+/* i2c registration */
+
+static int chip_attach(struct i2c_adapter *adap, int addr,
+ unsigned short flags, int kind)
+{
+ struct CHIPSTATE *chip;
+ struct CHIPDESC *desc;
+
+ chip = kmalloc(sizeof(*chip),GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+ memset(chip,0,sizeof(*chip));
+ memcpy(&chip->c,&client_template,sizeof(struct i2c_client));
+ chip->c.adapter = adap;
+ chip->c.addr = addr;
+ chip->c.data = chip;
+
+ /* find description for the chip */
+ dprintk("tvaudio: chip @ addr=0x%x\n",addr<<1);
+ for (desc = chiplist; desc->name != NULL; desc++) {
+ if (0 == *(desc->insmodopt))
+ continue;
+ if (addr < desc->addr_lo ||
+ addr > desc->addr_hi)
+ continue;
+ if (desc->checkit && !desc->checkit(chip))
+ continue;
+ break;
+ }
+ if (desc->name == NULL) {
+ dprintk("tvaudio: no matching chip description found\n");
+ return -EIO;
+ }
+ dprintk("tvaudio: %s matches:%s%s%s\n",desc->name,
+ (desc->flags & CHIP_HAS_VOLUME) ? " volume" : "",
+ (desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "",
+ (desc->flags & CHIP_HAS_INPUTSEL) ? " audiomux" : "");
+
+ /* fill required data structures */
+ strcpy(chip->c.name,desc->name);
+ chip->type = desc-chiplist;
+ chip->shadow.count = desc->registers+1;
+
+ /* register */
+ MOD_INC_USE_COUNT;
+ i2c_attach_client(&chip->c);
+
+ /* initialization */
+ chip_cmd(chip,"init",&desc->init);
+ if (desc->flags & CHIP_HAS_VOLUME) {
+ chip->left = desc->leftinit ? desc->leftinit : 65536;
+ chip->right = desc->rightinit ? desc->rightinit : 65536;
+ chip_write(chip,desc->leftreg,desc->volfunc(chip->left));
+ chip_write(chip,desc->rightreg,desc->volfunc(chip->right));
+ }
+ if (desc->flags & CHIP_HAS_BASSTREBLE) {
+ chip->treble = desc->trebleinit ? desc->trebleinit : 32768;
+ chip->bass = desc->bassinit ? desc->bassinit : 32768;
+ chip_write(chip,desc->bassreg,desc->bassfunc(chip->bass));
+ chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble));
+ }
+
+ if (desc->checkmode) {
+ /* start async thread */
+ DECLARE_MUTEX_LOCKED(sem);
+ chip->notify = &sem;
+ init_waitqueue_head(&chip->wq);
+ kernel_thread(chip_thread,(void *)chip,0);
+ down(&sem);
+ chip->notify = NULL;
+ chip->wake++;
+ wake_up_interruptible(&chip->wq);
+ }
+ return 0;
+}
+
+static int chip_probe(struct i2c_adapter *adap)
+{
+ if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848))
+ return i2c_probe(adap, &addr_data, chip_attach);
+ return 0;
+}
+
+static int chip_detach(struct i2c_client *client)
+{
+ struct CHIPSTATE *chip = client->data;
+
+ if (NULL != chip->thread) {
+ /* shutdown async thread */
+ DECLARE_MUTEX_LOCKED(sem);
+ chip->notify = &sem;
+ chip->done = 1;
+ wake_up_interruptible(&chip->wq);
+ down(&sem);
+ chip->notify = NULL;
+ }
+
+ i2c_detach_client(&chip->c);
+ kfree(chip);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+/* video4linux interface */
+
+static int chip_command(struct i2c_client *client,
+ unsigned int cmd, void *arg)
+{
+ __u16 *sarg = arg;
+ struct CHIPSTATE *chip = client->data;
+ struct CHIPDESC *desc = chiplist + chip->type;
+
+ dprintk("%s: chip_command 0x%x\n",chip->c.name,cmd);
+
+ switch (cmd) {
+ case AUDC_SET_INPUT:
+ if (desc->flags & CHIP_HAS_INPUTSEL) {
+ if (*sarg & 0x80)
+ chip_write(chip,desc->inputreg,desc->inputmute);
+ else
+ chip_write(chip,desc->inputreg,desc->inputmap[*sarg]);
+ }
+ break;
+ /* --- v4l ioctls --- */
+ /* take care: bttv does userspace copying, we'll get a
+ kernel pointer here... */
+ case VIDIOCGAUDIO:
+ {
+ struct video_audio *va = arg;
+
+ if (desc->flags & CHIP_HAS_VOLUME) {
+ va->flags |= VIDEO_AUDIO_VOLUME;
+ va->volume = MAX(chip->left,chip->right);
+ va->balance = (32768*MIN(chip->left,chip->right))/
+ (va->volume ? va->volume : 1);
+ }
+ if (desc->flags & CHIP_HAS_BASSTREBLE) {
+ va->flags |= VIDEO_AUDIO_BASS | VIDEO_AUDIO_TREBLE;
+ va->bass = chip->bass;
+ va->treble = chip->treble;
+ }
+ if (desc->getmode)
+ va->mode = desc->getmode(chip);
+ else
+ va->mode = VIDEO_SOUND_MONO;
+ break;
+ }
+
+ case VIDIOCSAUDIO:
+ {
+ struct video_audio *va = arg;
+
+ if (desc->flags & CHIP_HAS_VOLUME) {
+ chip->left = (MIN(65536 - va->balance,32768) *
+ va->volume) / 32768;
+ chip->right = (MIN(va->balance,32768) *
+ va->volume) / 32768;
+ chip_write(chip,desc->leftreg,desc->volfunc(chip->left));
+ chip_write(chip,desc->rightreg,desc->volfunc(chip->right));
+ }
+ if (desc->flags & CHIP_HAS_BASSTREBLE) {
+ chip->bass = va->bass;
+ chip->treble = va->treble;
+ chip_write(chip,desc->bassreg,desc->bassfunc(chip->bass));
+ chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble));
+ }
+ if (desc->setmode && va->mode)
+ desc->setmode(chip,va->mode);
+ break;
+ }
+ case VIDIOCSFREQ:
+ {
+ if (desc->checkmode) {
+ desc->setmode(chip,VIDEO_SOUND_MONO);
+ chip->wake++;
+ wake_up_interruptible(&chip->wq);
+ /* the thread will call checkmode() a second later */
+ }
+ }
+ }
+ return 0;
+}
+
+
+static struct i2c_driver driver = {
+ name: "generic i2c audio driver",
+ id: I2C_DRIVERID_TVAUDIO, /* FIXME */
+ flags: I2C_DF_NOTIFY,
+ attach_adapter: chip_probe,
+ detach_client: chip_detach,
+ command: chip_command,
+};
+
+static struct i2c_client client_template =
+{
+ name: "(unset)",
+ driver: &driver,
+};
+
+int audiochip_init_module(void)
+{
+ struct CHIPDESC *desc;
+ printk(KERN_INFO "tvaudio: TV audio decoder + audio/video mux driver\n");
+ printk(KERN_INFO "tvaudio: known chips: ");
+ for (desc = chiplist; desc->name != NULL; desc++)
+ printk("%s%s", (desc == chiplist) ? "" : ",",desc->name);
+ printk("\n");
+ i2c_add_driver(&driver);
+ return 0;
+}
+
+void audiochip_cleanup_module(void)
+{
+ i2c_del_driver(&driver);
+}
+
+module_init(audiochip_init_module);
+module_exit(audiochip_cleanup_module);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
--- /dev/null
+/*
+ * i2c bus addresses for the chips supported by tvaudio.c
+ */
+
+#define I2C_TDA8425 0x82
+#define I2C_TDA9840 0x84
+#define I2C_TDA985x_L 0xb4 /* also used by 9873 */
+#define I2C_TDA985x_H 0xb6
+
+#define I2C_TEA6300 0x80
+#define I2C_TEA6420 0x98
+
+#define I2C_PIC16C54 0x96 /* PV951 */
#include <asm/uaccess.h>
#include "audiochip.h"
+#include "id.h"
#define DEV_MAX 4
static struct i2c_driver driver = {
- "tv card mixer driver",
- 42 /* I2C_DRIVERID_FIXME */,
- I2C_DF_DUMMY,
- tvmixer_adapters,
- tvmixer_clients,
+ name: "tv card mixer driver",
+ id: I2C_DRIVERID_TVMIXER,
+ flags: I2C_DF_DUMMY,
+ attach_adapter: tvmixer_adapters,
+ detach_client: tvmixer_clients,
};
static struct file_operations tvmixer_fops = {
static int tvmixer_adapters(struct i2c_adapter *adap)
{
+ int i;
+
+ if (debug)
+ printk("tvmixer: adapter %s\n",adap->name);
+ for (i=0; i<I2C_CLIENT_MAX; i++) {
+ if (!adap->clients[i])
+ continue;
+ tvmixer_clients(adap->clients[i]);
+ }
return 0;
}
#define DOC_PASSIVE_PROBE
*/
-
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <asm/errno.h>
int __init el1_probe(struct net_device *dev)
{
int i;
- int base_addr = dev ? dev->base_addr : 0;
+ int base_addr = dev->base_addr;
+
+ SET_MODULE_OWNER(dev);
if (base_addr > 0x1ff) /* Check a single specified location. */
return el1_probe1(dev, base_addr);
struct net_local *lp = (struct net_local *)dev->priv;
unsigned long flags;
- MOD_INC_USE_COUNT;
-
if (el_debug > 2)
printk("%s: Doing el_open()...", dev->name);
- if ((retval = request_irq(dev->irq, &el_interrupt, 0, dev->name, dev))) {
- MOD_DEC_USE_COUNT;
+ if ((retval = request_irq(dev->irq, &el_interrupt, 0, dev->name, dev)))
return retval;
- }
spin_lock_irqsave(&lp->lock, flags);
el_reset(dev);
free_irq(dev->irq, dev);
outb(AX_RESET, AX_CMD); /* Reset the chip */
- MOD_DEC_USE_COUNT;
return 0;
}
#ifdef MODULE
-static struct net_device dev_3c501 =
-{
- "", /* device name is inserted by linux/drivers/net/net_init.c */
- 0, 0, 0, 0,
- 0x280, 5,
- 0, 0, 0, NULL, el1_probe
+static struct net_device dev_3c501 = {
+ init: el1_probe,
+ base_addr: 0x280,
+ irq: 5,
};
static int io=0x280;
int *addr, addrs[] = { 0xddffe, 0xd9ffe, 0xcdffe, 0xc9ffe, 0};
int base_addr = dev->base_addr;
+ SET_MODULE_OWNER(dev);
+
if (base_addr > 0x1ff) /* Check a single specified location. */
return el2_probe1(dev, base_addr);
else if (base_addr != 0) /* Don't probe at all. */
el2_init_card(dev);
ei_open(dev);
- MOD_INC_USE_COUNT;
return 0;
}
outb(EGACFR_IRQOFF, E33G_GACFR); /* disable interrupts. */
ei_close(dev);
- MOD_DEC_USE_COUNT;
return 0;
}
#ifdef MODULE
#define MAX_EL2_CARDS 4 /* Max number of EL2 cards per module */
-static struct net_device dev_el2[MAX_EL2_CARDS] = {
- {
- "",
- 0, 0, 0, 0,
- 0, 0,
- 0, 0, 0, NULL, NULL
- },
-};
-
-static int io[MAX_EL2_CARDS] = { 0, };
-static int irq[MAX_EL2_CARDS] = { 0, };
-static int xcvr[MAX_EL2_CARDS] = { 0, }; /* choose int. or ext. xcvr */
+static struct net_device dev_el2[MAX_EL2_CARDS];
+static int io[MAX_EL2_CARDS];
+static int irq[MAX_EL2_CARDS];
+static int xcvr[MAX_EL2_CARDS]; /* choose int. or ext. xcvr */
MODULE_PARM(io, "1-" __MODULE_STRING(MAX_EL2_CARDS) "i");
MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_EL2_CARDS) "i");
MODULE_PARM(xcvr, "1-" __MODULE_STRING(MAX_EL2_CARDS) "i");
* device is now officially open!
*/
- netif_wake_queue(dev);
- MOD_INC_USE_COUNT;
-
- return 0; /* Always succeed */
+ netif_start_queue(dev);
+ return 0;
}
free_dma(dev->dma);
free_pages((unsigned long) adapter->dma_buffer, get_order(DMA_BUFFER_SIZE));
- MOD_DEC_USE_COUNT;
-
return 0;
}
int i, tries, tries1, timeout, okay;
unsigned long cookie = 0;
+ SET_MODULE_OWNER(dev);
+
/*
* setup adapter structure
*/
int __init el16_probe(struct net_device *dev)
{
- int base_addr = dev ? dev->base_addr : 0;
+ int base_addr = dev->base_addr;
int i;
+ SET_MODULE_OWNER(dev);
+
if (base_addr > 0x1ff) /* Check a single specified location. */
return el16_probe1(dev, base_addr);
else if (base_addr != 0)
init_ID_done = 1;
}
- if (!request_region(ioaddr, EL16_IO_EXTENT, "3c507"))
+ if (!request_region(ioaddr, EL16_IO_EXTENT, dev->name))
return -ENODEV;
if ((inb(ioaddr) != '*') || (inb(ioaddr + 1) != '3') ||
irq = inb(ioaddr + IRQ_CONFIG) & 0x0f;
- irqval = request_irq(irq, &el16_interrupt, 0, "3c507", dev);
+ irqval = request_irq(irq, &el16_interrupt, 0, dev->name, dev);
if (irqval) {
printk ("unable to get IRQ %d (irqval=%d).\n", irq, irqval);
retval = -EAGAIN;
init_82586_mem(dev);
netif_start_queue(dev);
-
- MOD_INC_USE_COUNT;
-
return 0;
}
/* Update the statistics here. */
- MOD_DEC_USE_COUNT;
-
return 0;
}
lp->rx_tail = rx_tail;
}
#ifdef MODULE
-static struct net_device dev_3c507 = { init: el16_probe };
-
+static struct net_device dev_3c507;
static int io = 0x300;
static int irq = 0;
MODULE_PARM(io, "i");
printk("3c507: You should not use auto-probing with insmod!\n");
dev_3c507.base_addr = io;
dev_3c507.irq = irq;
+ dev_3c507.init = el16_probe;
if (register_netdev(&dev_3c507) != 0) {
printk("3c507: register_netdev() returned non-zero.\n");
return -EIO;
static int pnp_cards = 0;
#endif /* __ISAPNP__ */
+ if (dev) SET_MODULE_OWNER(dev);
+
/* First check all slots of the EISA bus. The next slot address to
probe is kept in 'eisa_addr' to support multiple probe() calls. */
if (EISA_bus) {
release_region(ioaddr, EL3_IO_EXTENT);
return -ENOMEM;
}
+ SET_MODULE_OWNER(dev);
}
memcpy(dev->dev_addr, phys_addr, sizeof(phys_addr));
dev->base_addr = ioaddr;
outw(RxReset, ioaddr + EL3_CMD);
outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
- if (request_irq(dev->irq, &el3_interrupt, 0, dev->name, dev)) {
- return -EAGAIN;
- }
+ i = request_irq(dev->irq, &el3_interrupt, 0, dev->name, dev);
+ if (i) return i;
EL3WINDOW(0);
if (el3_debug > 3)
printk("%s: Opened 3c509 IRQ %d status %4.4x.\n",
dev->name, dev->irq, inw(ioaddr + EL3_STATUS));
- MOD_INC_USE_COUNT;
- return 0; /* Always succeed */
+ return 0;
}
static void
outw(0x0f00, ioaddr + WN0_IRQ);
update_stats(dev);
- MOD_DEC_USE_COUNT;
return 0;
}
{
int cards_found = 0;
+ SET_MODULE_OWNER(dev);
+
cards_found = corkscrew_scan(dev);
if (corkscrew_debug > 0 && cards_found)
sizeof(struct corkscrew_private) + 15; /* Pad for alignment */
dev = (struct net_device *) kmalloc(dev_size, GFP_KERNEL);
+ if (!dev)
+ return NULL;
memset(dev, 0, dev_size);
/* Align the Rx and Tx ring entries. */
dev->priv =
ether_setup(dev);
vp->next_module = root_corkscrew_dev;
root_corkscrew_dev = dev;
- if (register_netdev(dev) != 0)
- return 0;
-#else /* not a MODULE */
- if (dev) {
- /* Caution: quad-word alignment required for rings! */
- dev->priv =
- kmalloc(sizeof(struct corkscrew_private), GFP_KERNEL);
- memset(dev->priv, 0, sizeof(struct corkscrew_private));
+ if (register_netdev(dev) != 0) {
+ kfree(dev);
+ return NULL;
}
+ SET_MODULE_OWNER(dev);
+#else /* not a MODULE */
+ /* Caution: quad-word alignment required for rings! */
+ dev->priv =
+ kmalloc(sizeof(struct corkscrew_private), GFP_KERNEL);
+ memset(dev->priv, 0, sizeof(struct corkscrew_private));
dev = init_etherdev(dev, sizeof(struct corkscrew_private));
dev->base_addr = ioaddr;
dev->irq = irq;
| (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete,
ioaddr + EL3_CMD);
- MOD_INC_USE_COUNT;
-
return 0;
}
}
}
- MOD_DEC_USE_COUNT;
-
return 0;
}
netif_stop_queue(dev);
elmc_id_reset586(); /* the hard way to stop the receiver */
free_irq(dev->irq, dev);
- MOD_DEC_USE_COUNT;
return 0;
}
static int elmc_open(struct net_device *dev)
{
+ int ret;
elmc_id_attn586(); /* disable interrupts */
- if (request_irq(dev->irq, &elmc_interrupt, SA_SHIRQ | SA_SAMPLE_RANDOM,
- "3c523", dev)
- ) {
+ ret = request_irq(dev->irq, &elmc_interrupt, SA_SHIRQ | SA_SAMPLE_RANDOM,
+ dev->name, dev);
+ if (ret) {
printk(KERN_ERR "%s: couldn't get irq %d\n", dev->name, dev->irq);
elmc_id_reset586();
- return -EAGAIN;
+ return ret;
}
alloc586(dev);
init586(dev);
startrecv586(dev);
netif_start_queue(dev);
- MOD_INC_USE_COUNT;
return 0; /* most done by init */
}
int __init elmc_probe(struct net_device *dev)
{
static int slot = 0;
- int base_addr = dev ? dev->base_addr : 0;
- int irq = dev ? dev->irq : 0;
+ int base_addr = dev->base_addr;
+ int irq = dev->irq;
u_char status = 0;
u_char revision = 0;
int i = 0;
unsigned int size = 0;
+ SET_MODULE_OWNER(dev);
if (MCA_bus == 0) {
return -ENODEV;
}
/* Increase if needed ;) */
#define MAX_3C523_CARDS 4
-static struct net_device dev_elmc[MAX_3C523_CARDS] =
-{
- {
- "", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL
- },
-};
-
-static int irq[MAX_3C523_CARDS] = {0,};
-static int io[MAX_3C523_CARDS] = {0,};
+static struct net_device dev_elmc[MAX_3C523_CARDS];
+static int irq[MAX_3C523_CARDS];
+static int io[MAX_3C523_CARDS];
MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_3C523_CARDS) "i");
MODULE_PARM(io, "1-" __MODULE_STRING(MAX_3C523_CARDS) "i");
int i;
int adapter_found = 0;
+ SET_MODULE_OWNER(dev);
+
/* Do not check any supplied i/o locations.
POS registers usually don't fail :) */
* Grab the IRQ
*/
- if(request_irq(dev->irq, &mc32_interrupt, 0, cardname, dev))
- {
- printk("%s: unable to get IRQ %d.\n",
- dev->name, dev->irq);
- return -EAGAIN;
+ i = request_irq(dev->irq, &mc32_interrupt, 0, dev->name, dev);
+ if (i) {
+ printk("%s: unable to get IRQ %d.\n", dev->name, dev->irq);
+ return i;
}
/* Initialize the device structure. */
- if (dev->priv == NULL) {
- dev->priv = kmalloc(sizeof(struct mc32_local), GFP_KERNEL);
- if (dev->priv == NULL)
- {
- free_irq(dev->irq, dev);
- return -ENOMEM;
- }
+ dev->priv = kmalloc(sizeof(struct mc32_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ {
+ free_irq(dev->irq, dev);
+ return -ENOMEM;
}
memset(dev->priv, 0, sizeof(struct mc32_local));
- lp = (struct mc32_local *)dev->priv;
+ lp = dev->priv;
lp->slot = slot;
i=0;
mc32_tx_begin(dev);
netif_start_queue(dev);
- MOD_INC_USE_COUNT;
-
return 0;
}
/* Update the statistics here. */
- MOD_DEC_USE_COUNT;
-
return 0;
}
#ifdef MODULE
-static struct net_device this_device = { init: mc32_probe };
+static struct net_device this_device;
/**
{
int result;
+ this_device.init = mc32_probe;
if ((result = register_netdev(&this_device)) != 0)
return result;
- Added experimental support for the 3c556B Laptop Hurricane (Louis Gerbarg)
- Add HAS_NWAY to "3c900 Cyclone 10Mbps TPO"
+ LK1.1.11 13 Nov 2000 andrewm
+ - Dump MOD_INC/DEC_USE_COUNT, use SET_MODULE_OWNER
+
- See http://www.uow.edu.au/~andrewm/linux/#3c59x-2.3 for more details.
- Also see Documentation/networking/vortex.txt
*/
#include <linux/delay.h>
static char version[] __devinitdata =
-"3c59x.c:LK1.1.10 17 Sep 2000 Donald Becker and others. http://www.scyld.com/network/vortex.html " "$Revision: 1.102.2.40 $\n";
+"3c59x.c:LK1.1.11 13 Nov 2000 Donald Becker and others. http://www.scyld.com/network/vortex.html " "$Revision: 1.102.2.46 $\n";
MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
MODULE_DESCRIPTION("3Com 3c59x/3c90x/3c575 series Vortex/Boomerang/Cyclone driver");
retval = -ENOMEM;
goto out;
}
-
+ SET_MODULE_OWNER(dev);
+
printk(KERN_INFO "%s: 3Com %s %s at 0x%lx, ",
dev->name,
pdev ? "PCI" : "EISA",
int i;
int retval;
- MOD_INC_USE_COUNT;
-
/* Use the now-standard shared IRQ implementation. */
if ((retval = request_irq(dev->irq, vp->full_bus_master_rx ?
&boomerang_interrupt : &vortex_interrupt, SA_SHIRQ, dev->name, dev))) {
out:
if (vortex_debug > 1)
printk(KERN_ERR "%s: vortex_open() fails: returning %d\n", dev->name, retval);
- MOD_DEC_USE_COUNT;
return retval;
}
}
}
- MOD_DEC_USE_COUNT;
vp->open = 0;
return 0;
}
vp = (void *)(dev->priv);
- /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
/* AKPM: FIXME: we should have
* if (vp->cb_fn_base) iounmap(vp->cb_fn_base);
* here
{
int i;
struct i596_private *lp;
- char eth_addr[6];
+ char eth_addr[8];
static int probed = 0;
if (probed)
tristate ' 3c590/3c900 series (592/595/597) "Vortex/Boomerang" support' CONFIG_VORTEX
fi
fi
- tristate ' AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE
+ dep_tristate ' AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE $CONFIG_ISA
bool ' Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC
if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then
tristate ' WD80*3 support' CONFIG_WD80x3
fi
bool ' EISA, VLB, PCI and on board controllers' CONFIG_NET_PCI
if [ "$CONFIG_NET_PCI" = "y" ]; then
- tristate ' AMD PCnet32 (VLB and PCI) support' CONFIG_PCNET32
+ dep_tristate ' AMD PCnet32 PCI support' CONFIG_PCNET32 $CONFIG_PCI
dep_tristate ' Adaptec Starfire support (EXPERIMENTAL)' CONFIG_ADAPTEC_STARFIRE $CONFIG_PCI $CONFIG_EXPERIMENTAL
- if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- tristate ' Ansel Communications EISA 3200 support (EXPERIMENTAL)' CONFIG_AC3200
+ if [ "$CONFIG_ISA" = "y" -o "$CONFIG_EISA" = "y" ]; then
+ dep_tristate ' Ansel Communications EISA 3200 support (EXPERIMENTAL)' CONFIG_AC3200 $CONFIG_EXPERIMENTAL
fi
tristate ' Apricot Xen-II on board Ethernet' CONFIG_APRICOT
- tristate ' CS89x0 support' CONFIG_CS89x0
- tristate ' Generic DECchip & DIGITAL EtherWORKS PCI/EISA' CONFIG_DE4X5
+ dep_tristate ' CS89x0 support' CONFIG_CS89x0 $CONFIG_ISA
dep_tristate ' DECchip Tulip (dc21x4x) PCI support' CONFIG_TULIP $CONFIG_PCI
- tristate ' Digi Intl. RightSwitch SE-X support' CONFIG_DGRS
- if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- tristate ' DM9102 PCI Fast Ethernet Adapter support (EXPERIMENTAL)' CONFIG_DM9102
+ if [ "$CONFIG_PCI" = "y" -o "$CONFIG_EISA" = "y" ]; then
+ tristate ' Generic DECchip & DIGITAL EtherWORKS PCI/EISA' CONFIG_DE4X5
+ tristate ' Digi Intl. RightSwitch SE-X support' CONFIG_DGRS
fi
+ dep_tristate ' DM9102 PCI Fast Ethernet Adapter support (EXPERIMENTAL)' CONFIG_DM9102 $CONFIG_PCI $CONFIG_EXPERIMENTAL
dep_tristate ' EtherExpressPro/100 support' CONFIG_EEPRO100 $CONFIG_PCI
- if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- if [ "$CONFIG_EEPRO100" = "y" -o "$CONFIG_EEPRO100" = "m" ]; then
- bool ' Enable Power Management (EXPERIMENTAL)' CONFIG_EEPRO100_PM
- fi
- tristate ' Mylex EISA LNE390A/B support (EXPERIMENTAL)' CONFIG_LNE390
- fi
+ dep_mbool ' Enable Power Management (EXPERIMENTAL)' CONFIG_EEPRO100_PM $CONFIG_EEPRO100 $CONFIG_EXPERIMENTAL
+ dep_tristate ' Mylex EISA LNE390A/B support (EXPERIMENTAL)' CONFIG_LNE390 $CONFIG_EISA $CONFIG_EXPERIMENTAL
dep_tristate ' National Semiconductor DP83810 series PCI Ethernet support' CONFIG_NATSEMI $CONFIG_PCI
dep_tristate ' PCI NE2000 and clones support (see help)' CONFIG_NE2K_PCI $CONFIG_PCI
- if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- tristate ' Novell/Eagle/Microdyne NE3210 EISA support (EXPERIMENTAL)' CONFIG_NE3210
- fi
- if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- tristate ' Racal-Interlan EISA ES3210 support (EXPERIMENTAL)' CONFIG_ES3210
- dep_tristate ' RealTek 8129 (not 8019/8029/8139!) support (EXPERIMENTAL)' CONFIG_RTL8129 $CONFIG_PCI
- fi
+ dep_tristate ' Novell/Eagle/Microdyne NE3210 EISA support (EXPERIMENTAL)' CONFIG_NE3210 $CONFIG_EISA $CONFIG_EXPERIMENTAL
+ dep_tristate ' Racal-Interlan EISA ES3210 support (EXPERIMENTAL)' CONFIG_ES3210 $CONFIG_EISA $CONFIG_EXPERIMENTAL
dep_tristate ' RealTek RTL-8139 PCI Fast Ethernet Adapter support' CONFIG_8139TOO $CONFIG_PCI
+ dep_tristate ' RealTek 8129 (not 8019/8029/8139!) support (EXPERIMENTAL)' CONFIG_RTL8129 $CONFIG_PCI $CONFIG_EXPERIMENTAL
dep_tristate ' SiS 900/7016 PCI Fast Ethernet Adapter support' CONFIG_SIS900 $CONFIG_PCI
dep_tristate ' SMC EtherPower II' CONFIG_EPIC100 $CONFIG_PCI
dep_tristate ' Sundance Alta support' CONFIG_SUNDANCE $CONFIG_PCI
- tristate ' TI ThunderLAN support' CONFIG_TLAN
+ if [ "$CONFIG_PCI" = "y" -o "$CONFIG_EISA" = "y" ]; then
+ tristate ' TI ThunderLAN support' CONFIG_TLAN
+ fi
dep_tristate ' VIA Rhine support' CONFIG_VIA_RHINE $CONFIG_PCI
dep_tristate ' Winbond W89c840 Ethernet support' CONFIG_WINBOND_840 $CONFIG_PCI
+ dep_tristate ' Sun Happy Meal 10/100baseT PCI support' CONFIG_HAPPYMEAL $CONFIG_PCI
if [ "$CONFIG_OBSOLETE" = "y" ]; then
bool ' Zenith Z-Note support (EXPERIMENTAL)' CONFIG_ZNET
fi
fi
bool ' Pocket and portable adapters' CONFIG_NET_POCKET
if [ "$CONFIG_NET_POCKET" = "y" ]; then
- if [ "$CONFIG_X86" = "y" ]; then
- tristate ' AT-LAN-TEC/RealTek pocket adapter support' CONFIG_ATP
- fi
+ dep_tristate ' AT-LAN-TEC/RealTek pocket adapter support' CONFIG_ATP $CONFIG_ISA
tristate ' D-Link DE600 pocket adapter support' CONFIG_DE600
tristate ' D-Link DE620 pocket adapter support' CONFIG_DE620
fi
barrier();
if ((i == 100) || (ll->rdp & LE_C0_ERR)) {
printk ("LANCE unopened after %d ticks, csr0=%4.4x.\n", i, ll->rdp);
- return -1;
+ return -EIO;
}
/* Clear IDON by writing a "1", enable interrupts and start lance */
{
struct lance_private *lp = (struct lance_private *)dev->priv;
volatile struct lance_regs *ll = lp->ll;
- int status = 0;
+ int ret;
+
+ MOD_INC_USE_COUNT;
last_dev = dev;
/* Install the Interrupt handler */
- if (request_irq(IRQ_AMIGA_PORTS, lance_interrupt, SA_SHIRQ,
- "a2065 Ethernet", dev))
- return -EAGAIN;
+ ret = request_irq(IRQ_AMIGA_PORTS, lance_interrupt, SA_SHIRQ,
+ dev->name, dev);
+ if (ret) {
+ MOD_DEC_USE_COUNT;
+ return ret;
+ }
/* Stop the Lance */
ll->rap = LE_CSR0;
netif_start_queue(dev);
- status = init_restart_lance (lp);
-
- MOD_INC_USE_COUNT;
-
- return status;
+ return init_restart_lance (lp);
}
static int lance_close (struct net_device *dev)
while ((z = zorro_find_device(ZORRO_WILDCARD, z))) {
unsigned long board, base_addr, mem_start;
+ struct resource *r1, *r2;
int is_cbm;
if (z->id == ZORRO_PROD_CBM_A2065_1 ||
base_addr = board+A2065_LANCE;
mem_start = board+A2065_RAM;
- if (!request_mem_region(base_addr, sizeof(struct lance_regs),
- "Am7990"))
- continue;
- if (!request_mem_region(mem_start, A2065_RAM_SIZE, "RAM")) {
- release_mem_region(base_addr,
- sizeof(struct lance_regs));
+ r1 = request_mem_region(base_addr, sizeof(struct lance_regs),
+ "Am7990");
+ if (!r1) continue;
+ r2 = request_mem_region(mem_start, A2065_RAM_SIZE, "RAM");
+ if (!r2) {
+ release_resource(r1);
continue;
}
dev = init_etherdev(NULL, sizeof(struct lance_private));
if (dev == NULL) {
- release_mem_region(base_addr,
- sizeof(struct lance_regs));
- release_mem_region(mem_start, A2065_RAM_SIZE);
+ release_resource(r1);
+ release_resource(r2);
return -ENOMEM;
}
priv = (struct lance_private *)dev->priv;
- memset(priv, 0, sizeof(struct lance_private));
+
+ r1->name = dev->name;
+ r2->name = dev->name;
priv->dev = dev;
dev->dev_addr[0] = 0x00;
{
unsigned short ioaddr = dev->base_addr;
+ SET_MODULE_OWNER(dev);
+
if (ioaddr > 0x1ff) /* Check a single specified location. */
return ac_probe1(ioaddr, dev);
else if (ioaddr > 0) /* Don't probe at all. */
#endif
ei_open(dev);
-
- MOD_INC_USE_COUNT;
-
return 0;
}
#endif
ei_close(dev);
-
- MOD_DEC_USE_COUNT;
-
return 0;
}
: abstracted IRQ mapping to support CONFIG_ARCH_CLPS7500 arch
: (Jason Gunthorpe <jgg@ualberta.ca>)
+ Andrew Morton : Kernel 2.4.0-test11-pre4
+ : Use dev->name in request_*() (Andrey Panin)
+ : Fix an error-path memleak in init_module()
+ : Preserve return value from request_irq()
+ : Fix type of `media' module parm (Keith Owens)
+ : Use SET_MODULE_OWNER()
+ : Tidied up strange request_irq() abuse in net_open().
+
*/
-static char *version =
-"cs89x0.c: v2.3.99-pre1-2 Russell Nelson <nelson@crynwr.com>, Andrew Morton <andrewm@uow.edu.au>\n";
+static char version[] =
+"cs89x0.c: v2.4.0-test11-pre4 Russell Nelson <nelson@crynwr.com>, Andrew Morton <andrewm@uow.edu.au>\n";
/* ======================= end of configuration ======================= */
/* Always include 'config.h' first in case the user wants to turn on
or override something. */
#include <linux/config.h>
-#ifdef MODULE
#include <linux/module.h>
#include <linux/version.h>
-#else
-#define MOD_INC_USE_COUNT
-#define MOD_DEC_USE_COUNT
-#endif
/*
* Set this to zero to disable DMA code
* Set this to zero to remove all the debug statements via
* dead code elimination
*/
-#define DEBUGGING 0
+#define DEBUGGING 1
/*
Sources:
int i;
int base_addr = dev ? dev->base_addr : 0;
+ SET_MODULE_OWNER(dev);
+
if (net_debug)
printk("cs89x0:cs89x0_probe()\n");
lp = (struct net_local *)dev->priv;
/* Grab the region so we can find another board if autoIRQ fails. */
- if (!request_region(ioaddr, NETCARD_IO_EXTENT, "cs89x0")) {
- retval = -ENODEV;
+ if (!request_region(ioaddr, NETCARD_IO_EXTENT, dev->name)) {
+ retval = -EBUSY;
goto out1;
}
int i;
if (chip_type == CS8900) {
- /* Search the mapping table for the corrisponding IRQ pin. */
+ /* Search the mapping table for the corresponding IRQ pin. */
for (i = 0; i != sizeof(cs8900_irq_map)/sizeof(cs8900_irq_map[0]); i++)
if (cs8900_irq_map[i] == irq)
break;
int i;
int ret;
- MOD_INC_USE_COUNT;
-
if (dev->irq < 2) {
/* Allow interrupts to be generated by the chip */
/* Cirrus' release had this: */
/* And 2.3.47 had this: */
writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON);
- for (i = 2; i < CS8920_NO_INTS; i++) if ((1 << dev->irq) & lp->irq_map) {
- if (request_irq (i, NULL, 0, "cs89x0", dev) != -EBUSY) {
- write_irq(dev, lp->chip_type, i);
- writereg(dev, PP_BufCFG, GENERATE_SW_INTERRUPT);
- if (request_irq (dev->irq = i, &net_interrupt, 0, "cs89x0", dev) == 0)
+ for (i = 2; i < CS8920_NO_INTS; i++) {
+ if ((1 << dev->irq) & lp->irq_map) {
+ if (request_irq(i, net_interrupt, 0, dev->name, dev) == 0) {
+ dev->irq = i;
+ write_irq(dev, lp->chip_type, i);
+ /* writereg(dev, PP_BufCFG, GENERATE_SW_INTERRUPT); */
break;
+ }
}
}
-
if (i >= CS8920_NO_INTS) {
writereg(dev, PP_BusCTL, 0); /* disable interrupts. */
- if (net_debug)
- printk("cs89x0: can't get an interrupt\n");
+ printk(KERN_ERR "cs89x0: can't get an interrupt\n");
ret = -EAGAIN;
goto bad_out;
}
ret = request_irq(dev->irq, &net_interrupt, 0, dev->name, dev);
if (ret) {
if (net_debug)
- printk("cs89x0: request_irq(%d) failed\n", dev->irq);
+ printk(KERN_DEBUG "cs89x0: request_irq(%d) failed\n", dev->irq);
goto bad_out;
}
}
goto release_irq;
}
memset(lp->dma_buff, 0, lp->dmasize * 1024); /* Why? */
- if (request_dma(dev->dma, "cs89x0")) {
+ if (request_dma(dev->dma, dev->name)) {
printk(KERN_ERR "%s: cannot get dma channel %d\n", dev->name, dev->dma);
goto release_irq;
}
#endif
);
netif_start_queue(dev);
- if (net_debug)
+ if (net_debug > 1)
printk("cs89x0: net_open() succeeded\n");
return 0;
bad_out:
- MOD_DEC_USE_COUNT;
return ret;
}
#endif
/* Update the statistics here. */
- MOD_DEC_USE_COUNT;
return 0;
}
MODULE_PARM(io, "i");
MODULE_PARM(irq, "i");
MODULE_PARM(debug, "i");
-MODULE_PARM(media, "s");
+MODULE_PARM(media, "c8");
MODULE_PARM(duplex, "i");
MODULE_PARM(dma , "i");
MODULE_PARM(dmasize , "i");
init_module(void)
{
struct net_local *lp;
+ int ret = 0;
#if DEBUGGING
net_debug = debug;
if (io == 0) {
printk(KERN_ERR "cs89x0.c: Module autoprobing not allowed.\n");
printk(KERN_ERR "cs89x0.c: Append io=0xNNN\n");
- return -EPERM;
+ ret = -EPERM;
+ goto out;
}
#if ALLOW_DMA
if (use_dma && dmasize != 16 && dmasize != 64) {
printk(KERN_ERR "cs89x0.c: dma size must be either 16K or 64K, not %dK\n", dmasize);
- return -EPERM;
+ ret = -EPERM;
+ goto out;
}
#endif
if (register_netdev(&dev_cs89x0) != 0) {
printk(KERN_ERR "cs89x0.c: No card found at 0x%x\n", io);
- return -ENXIO;
+ ret = -ENXIO;
+ goto out;
}
- return 0;
+out:
+ if (ret)
+ kfree(dev_cs89x0.priv);
+ return ret;
}
void
\f
/*
* Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -DMODULE -DCONFIG_MODVERSIONS -c cs89x0.c"
* version-control: t
* kept-new-versions: 5
* c-indent-level: 8
dev->init = irport_net_init;
dev->hard_start_xmit = irport_hard_xmit;
dev->tx_timeout = irport_timeout;
- dev->watchdog_timeo = HZ/20;
+ dev->watchdog_timeo = HZ; /* Allow time enough for speed change */
dev->open = irport_net_open;
dev->stop = irport_net_close;
dev->get_stats = irport_net_get_stats;
self->tx_buff.data += actual;
self->tx_buff.len -= actual;
} else {
-
/*
* Now serial buffer is almost free & we can start
* transmission of another packet. But first we must check
* Author: Thomas Davis (tadavis@jps.net)
* Created at:
* Modified at: Tue Feb 22 10:05:06 2000
- * Modified by: Dag Brattli <dagb@cs.uit.no>
+ * Modified by: Dag Brattli <dag@brattli.net>
*
* Copyright (c) 1999-2000 Dag Brattli
* Copyright (c) 1998-1999 Thomas Davis,
IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8);
irport->qos.min_turn_time.bits = 0x07;
+ irport->qos.window_size.bits = 0x01;
irda_qos_bits_to_value(&irport->qos);
irport->flags = IFF_FIR|IFF_MIR|IFF_SIR|IFF_DMA|IFF_PIO;
outb(0x00, iobase+IRCC_MASTER);
switch (speed) {
+ default:
+ IRDA_DEBUG(0, __FUNCTION__ "(), unknown baud rate of %d\n",
+ speed);
+ /* FALLTHROUGH */
case 9600:
case 19200:
case 38400:
fast = IRCC_LCR_A_FAST;
IRDA_DEBUG(0, __FUNCTION__ "(), handling baud of 4000000\n");
break;
- default:
- IRDA_DEBUG(0, __FUNCTION__ "(), unknown baud rate of %d\n",
- speed);
- return;
}
register_bank(iobase, 0);
if ((speed = irda_get_speed(skb)) != self->io.speed) {
/* Check for empty frame */
if (!skb->len) {
- smc_ircc_change_speed(self, speed);
+ ircc_change_speed(self, speed);
return 0;
} else
self->new_speed = speed;
int bofs;
/*
- * Compute who many BOFS (STA or PA's) we need to waste the
+ * Compute how many BOFs (STA or PA's) we need to waste the
* min turn time given the speed of the link.
*/
bofs = mtt * (self->io.speed / 1000) / 8000;
/* Transmit frame */
ircc_dma_xmit(self, iobase, 0);
}
-
spin_unlock_irqrestore(&self->lock, flags);
dev_kfree_skb(skb);
setup_dma(self->io.dma, self->rx_buff.data, self->rx_buff.truesize,
DMA_RX_MODE);
+
/* Set max Rx frame size */
register_bank(iobase, 4);
outb((2050 >> 8) & 0x0f, iobase+IRCC_RX_SIZE_HI);
*
* Finished with receiving frames
*
- *
*/
static void ircc_dma_receive_complete(struct ircc_cb *self, int iobase)
{
if ((speed = irda_get_speed(skb)) != self->io.speed) {
/* Check for empty frame */
if (!skb->len) {
- toshoboe_change_speed(self, speed);
+ toshoboe_setbaud(self, speed);
return 0;
} else
self->new_speed = speed;
-/* $Id: sunhme.c,v 1.99 2000/11/10 05:44:33 davem Exp $
+/* $Id: sunhme.c,v 1.100 2000/11/12 10:23:30 davem Exp $
* sunhme.c: Sparc HME/BigMac 10/100baseT half/full duplex auto switching,
* auto carrier detecting ethernet driver. Also known as the
* "Happy Meal Ethernet" found on SunSwift SBUS cards.
*
* Copyright (C) 1996, 1998, 1999 David S. Miller (davem@redhat.com)
+ *
+ * Changes :
+ * 2000/11/11 Willy Tarreau <willy AT meta-x.org>
+ * - port to non-sparc architectures. Tested only on x86 and
+ * only currently works with QFE PCI cards.
+ * - ability to specify the MAC address at module load time by passing this
+ * argument : macaddr=0x00,0x10,0x20,0x30,0x40,0x50
*/
static char *version =
#include <linux/errno.h>
#include <asm/byteorder.h>
+#ifdef __sparc__
#include <asm/idprom.h>
#include <asm/sbus.h>
#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/auxio.h>
-#include <asm/pgtable.h>
-#include <asm/irq.h>
#ifndef __sparc_v9__
#include <asm/io-unit.h>
#endif
#include <asm/uaccess.h>
+#endif
+
+#include <asm/pgtable.h>
+#include <asm/irq.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#ifdef CONFIG_PCI
#include <linux/pci.h>
+#ifdef __sparc__
#include <asm/pbm.h>
#endif
+#endif
#include "sunhme.h"
+
+static int macaddr[6];
+
+/* accept MAC address of the form macaddr=0x08,0x00,0x20,0x30,0x40,0x50 */
+MODULE_PARM(macaddr, "6i");
+
static struct happy_meal *root_happy_dev = NULL;
+#ifdef CONFIG_SBUS
static struct quattro *qfe_sbus_list = NULL;
+#endif
+
#ifdef CONFIG_PCI
static struct quattro *qfe_pci_list = NULL;
#endif
#endif
#endif
-#define DMA_BIDIRECTIONAL SBUS_DMA_BIDIRECTIONAL
-#define DMA_FROMDEVICE SBUS_DMA_FROMDEVICE
-#define DMA_TODEVICE SBUS_DMA_TODEVICE
+
+#ifdef SBUS_DMA_BIDIRECTIONAL
+# define DMA_BIDIRECTIONAL SBUS_DMA_BIDIRECTIONAL
+#else
+# define DMA_BIDIRECTIONAL 0
+#endif
+
+#ifdef SBUS_DMA_FROMDEVICE
+# define DMA_FROMDEVICE SBUS_DMA_FROMDEVICE
+#else
+# define DMA_TODEVICE 1
+#endif
+
+#ifdef SBUS_DMA_TODEVICE
+# define DMA_TODEVICE SBUS_DMA_TODEVICE
+#else
+# define DMA_FROMDEVICE 2
+#endif
+
/* Oh yes, the MIF BitBang is mighty fun to program. BitBucket is more like it. */
static void BB_PUT_BIT(struct happy_meal *hp, unsigned long tregs, int bit)
HMD(("done\n"));
}
+#ifdef CONFIG_SBUS
static void quattro_sbus_interrupt(int irq, void *cookie, struct pt_regs *ptregs)
{
struct quattro *qp = (struct quattro *) cookie;
}
HMD(("done\n"));
}
+#endif
static int happy_meal_open(struct net_device *dev)
{
if (request_irq(dev->irq, &happy_meal_interrupt,
SA_SHIRQ, "HAPPY MEAL", (void *)dev)) {
HMD(("EAGAIN\n"));
+#ifdef __sparc__
printk(KERN_ERR "happy_meal(SBUS): Can't order irq %s to go.\n",
__irq_itoa(dev->irq));
+#else
+ printk(KERN_ERR "happy_meal(SBUS): Can't order irq %d to go.\n",
+ dev->irq);
+#endif
+
return -EAGAIN;
}
}
#define SXD(x)
#endif
+#ifdef CONFIG_SBUS
static void happy_meal_tx_timeout(struct net_device *dev)
{
struct happy_meal *hp = (struct happy_meal *) dev->priv;
happy_meal_init(hp, 0);
netif_wake_queue(dev);
}
+#endif
static int happy_meal_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
printk(KERN_INFO "%s: HAPPY MEAL (SBUS) 10/100baseT Ethernet ",
dev->name);
- /* Quattro local-mac-address... */
- if (qfe_slot != -1 && prom_getproplen(sdev->prom_node,"local-mac-address")==6)
- prom_getproperty(sdev->prom_node,"local-mac-address",dev->dev_addr,6);
- else
- memcpy(dev->dev_addr,idprom->id_ethaddr,6);
+ /* If user did not specify a MAC address specifically, use
+ * the Quattro local-mac-address property...
+ */
+ for (i = 0; i < 6; i++) {
+ if (macaddr[i] != 0)
+ break;
+ }
+ if (i < 6) { /* a mac address was given */
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = macaddr[i];
+ } else if (qfe_slot != -1 &&
+ prom_getproplen(sdev->prom_node,
+ "local-mac-address") == 6) {
+ prom_getproperty(sdev->prom_node, "local-mac-address",
+ dev->dev_addr, 6);
+ } else {
+ memcpy(dev->dev_addr, idprom->id_ethaddr, 6);
+ }
+
for (i = 0; i < 6; i++)
printk("%2.2x%c",
dev->dev_addr[i], i == 5 ? ' ' : ':');
static int __init happy_meal_pci_init(struct net_device *dev, struct pci_dev *pdev)
{
struct quattro *qp = NULL;
+#ifdef __sparc__
struct pcidev_cookie *pcp;
+ int node;
+#endif
struct happy_meal *hp;
unsigned long hpreg_base;
- int i, node, qfe_slot = -1;
+ int i, qfe_slot = -1;
char prom_name[64];
/* Now make sure pci_dev cookie is there. */
+#ifdef __sparc__
pcp = pdev->sysdata;
if (pcp == NULL || pcp->prom_node == -1) {
printk(KERN_ERR "happymeal(PCI): Some PCI device info missing\n");
node = pcp->prom_node;
prom_getstring(node, "name", prom_name, sizeof(prom_name));
+#else
+#warning This needs to be corrected... -DaveM
+ strcpy(prom_name, "qfe");
+#endif
+
if (!strcmp(prom_name, "SUNW,qfe") || !strcmp(prom_name, "qfe")) {
qp = quattro_pci_find(pdev);
if (qp == NULL)
}
hpreg_base = (unsigned long) ioremap(hpreg_base, 0x8000);
- if (qfe_slot != -1 && prom_getproplen(node, "local-mac-address") == 6)
- prom_getproperty(node, "local-mac-address", dev->dev_addr, 6);
- else
- memcpy(dev->dev_addr, idprom->id_ethaddr, 6);
+ for (i = 0; i < 6; i++) {
+ if (macaddr[i] != 0)
+ break;
+ }
+ if (i < 6) { /* a mac address was given */
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = macaddr[i];
+ } else {
+#ifdef __sparc__
+ if (qfe_slot != -1 &&
+ prom_getproplen(node, "local-mac-address") == 6) {
+ prom_getproperty(node, "local-mac-address",
+ dev->dev_addr, 6);
+ } else {
+ memcpy(dev->dev_addr, idprom->id_ethaddr, 6);
+ }
+#else
+ memset(dev->dev_addr, 0, 6);
+#endif
+ }
+
for (i = 0; i < 6; i++)
printk("%2.2x%c", dev->dev_addr[i], i == 5 ? ' ' : ':');
hp->bigmacregs = (hpreg_base + 0x6000UL);
hp->tcvregs = (hpreg_base + 0x7000UL);
+#ifdef __sparc__
hp->hm_revision = prom_getintdefault(node, "hm-rev", 0xff);
if (hp->hm_revision == 0xff)
hp->hm_revision = 0xa0;
+#else
+ /* works with this on non-sparc hosts */
+ hp->hm_revision = 0x20;
+#endif
/* Now enable the feature flags we can. */
if (hp->hm_revision == 0x20 || hp->hm_revision == 0x21)
-/* $Id: sunhme.h,v 1.30 2000/02/18 13:49:26 davem Exp $
+/* $Id: sunhme.h,v 1.31 2000/11/12 10:23:30 davem Exp $
* sunhme.h: Definitions for Sparc HME/BigMac 10/100baseT ethernet driver.
* Also known as the "Happy Meal".
*
#define _SUNHME_H
#include <linux/config.h>
+#include <linux/pci.h>
/* Happy Meal global registers. */
#define GREG_SWRESET 0x000UL /* Software Reset */
struct quattro *next;
/* PROM ranges, if any. */
+#ifdef CONFIG_SBUS
struct linux_prom_ranges ranges[8];
+#endif
int nranges;
};
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/signal.h>
Copyright 1992 - 2000 Kai Makisara
email Kai.Makisara@metla.fi
- Last modified: Tue Aug 15 16:56:35 2000 by makisara@kai.makisara.local
+ Last modified: Mon Nov 13 21:01:09 2000 by makisara@kai.makisara.local
Some small formal changes - aeb, 950809
Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support
((SRpnt->sr_sense_buffer[0] & 0x80) != 0 &&
(SRpnt->sr_sense_buffer[3] | SRpnt->sr_sense_buffer[4] |
SRpnt->sr_sense_buffer[5] |
- SRpnt->sr_sense_buffer[6]) == 0))) {
+ SRpnt->sr_sense_buffer[6]) != 0))) {
/* Filter out successful write at EOM */
scsi_release_request(SRpnt);
SRpnt = NULL;
STps->drv_block = 0;
STps->eof = ST_NOEOF;
} else if ((cmd_in == MTBSF) || (cmd_in == MTBSFM)) {
- if (fileno >= 0)
+ if (STps->drv_file >= 0)
STps->drv_file = fileno + undone;
- else
- STps->drv_file = fileno;
STps->drv_block = 0;
STps->eof = ST_NOEOF;
} else if (cmd_in == MTFSR) {
STps->drv_file--;
STps->drv_block = (-1);
} else {
- if (blkno >= 0)
+ if (STps->drv_block >= 0)
STps->drv_block = blkno + undone;
- else
- STps->drv_block = (-1);
}
STps->eof = ST_NOEOF;
} else if (cmd_in == MTEOM) {
#define CS4281_MAGIC ((PCI_DEVICE_ID_CRYSTAL_CS4281<<16) | PCI_VENDOR_ID_CIRRUS)
-#include <linux/version.h>
-
// Turn on/off debugging compilation by using 1/0 respectively for CSDEBUG
//
#define CSDEBUG_INTERFACE 1
* 11-10-2000 Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
* Added __init to gus_midi_init()
*/
+
+#include "linux/init.h"
#include "sound_config.h"
#include "gus.h"
* Bartlomiej Zolnierkiewicz : added some __init/__exit
*/
-
+#include <linux/init.h>
#include <linux/config.h>
#define GUSPNP_AUTODETECT
* Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
* Bartlomiej Zolnierkiewicz : added __init to ics2101_mixer_init()
*/
+#include <linux/init.h>
#include "sound_config.h"
#include <linux/ultrasound.h>
* Bartlomiej Zolnierkiewicz : Added __init to pas_init_mixer()
*/
+#include <linux/init.h>
#include "sound_config.h"
#include "pas2.h"
* Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
* Bartlomiej Zolnierkiewicz : added __init to pas_init_mixer()
*/
+#include <linux/init.h>
#include "sound_config.h"
#include "pas2.h"
* Bartlomiej Zolnierkiewicz : Added __init to pas_pcm_init()
*/
+#include <linux/init.h>
#include "sound_config.h"
#include "pas2.h"
+#include <linux/init.h>
+
unsigned char page_zero[] __initdata = {
0x01, 0x7c, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0x00,
0x11, 0x00, 0x20, 0x00, 0x32, 0x00, 0x40, 0x00, 0x13, 0x00, 0x00,
* patches, and shared their sucesses!
*/
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/signal.h>
return -EINVAL;
len1 = bulk.len;
if (len1 > PAGE_SIZE)
- len1 = PAGE_SIZE;
+ return -EINVAL;
if (!(tbuf = (unsigned char *)__get_free_page(GFP_KERNEL)))
return -ENOMEM;
tmo = (bulk.timeout * HZ + 999) / 1000;
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-static const char version[] = "1.25";
+static const char version[] = "1.28";
#define __NO_VERSION__
#include "ov511.h"
-#undef OV511_GBR422 /* Experimental -- sets the 7610 to GBR422 */
-
-static struct usb_device_id ov511_table [] = {
-
- { idVendor: 0x05a9, idProduct: 0x0511, bInterfaceClass: 0xFF },
- { idVendor: 0x05a9, idProduct: 0xA511, bInterfaceClass: 0xFF },
- { idVendor: 0x05a9, idProduct: 0x0002, bInterfaceClass: 0xFF },
-
- { idVendor: 0x0813, idProduct: 0x0511, bInterfaceClass: 0xFF },
- { idVendor: 0x0813, idProduct: 0xA511, bInterfaceClass: 0xFF },
-
- { } /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE (usb, ov511_table);
-
-
#define OV511_I2C_RETRIES 3
/* Video Size 640 x 480 x 3 bytes for RGB */
#define MAX_DATA_SIZE (MAX_FRAME_SIZE + sizeof(struct timeval))
#define GET_SEGSIZE(p) ((p) == VIDEO_PALETTE_GREY ? 256 : 384)
-#define GET_DEPTH(p) ((p) == VIDEO_PALETTE_GREY ? 8 : \
- ((p) == VIDEO_PALETTE_YUV422 ? 16 : 24))
/* PARAMETER VARIABLES: */
static int autoadjust = 1; /* CCD dynamically changes exposure, etc... */
* 5=highly repetitive mesgs
* NOTE: This should be changed to 0, 1, or 2 for production kernels
*/
-static int debug = 3;
+static int debug = 0;
/* Fix vertical misalignment of red and blue at 640x480 */
static int fix_rgb_offset = 0;
/* Display test pattern - doesn't work yet either */
static int testpat = 0;
+/* Setting this to 1 will make the sensor output GBR422 instead on YUV420. Only
+ * affects RGB24 mode. */
+static int sensor_gbr = 0;
+
+/* Dump raw pixel data, in one of 3 formats. See ov511_dumppix() for details. */
+static int dumppix = 0;
+
MODULE_PARM(autoadjust, "i");
MODULE_PARM_DESC(autoadjust, "CCD dynamically changes exposure");
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(compress, "Turn on compression (not functional yet)");
MODULE_PARM(testpat, "i");
MODULE_PARM_DESC(testpat, "Replace image with vertical bar testpattern (only partially working)");
+MODULE_PARM(sensor_gbr, "i");
+MODULE_PARM_DESC(sensor_gbr, "Make sensor output GBR422 rather than YUV420");
+MODULE_PARM(dumppix, "i");
+MODULE_PARM_DESC(dumppix, "Dump raw pixel data, in one of 3 formats. See ov511_dumppix() for details");
MODULE_AUTHOR("Mark McClelland <mwm@i.am> & Bret Wallach & Orion Sky Lawlor <olawlor@acm.org> & Kevin Moore & Charl P. Botha <cpbotha@ieee.org> & Claudio Matsuoka <claudio@conectiva.com>");
MODULE_DESCRIPTION("OV511 USB Camera Driver");
{ -1, NULL }
};
+static __devinitdata struct usb_device_id device_table [] = {
+ { idVendor: 0x05a9, idProduct: 0x0511 }, /* OV511 */
+ { idVendor: 0x05a9, idProduct: 0xA511 }, /* OV511+ */
+ { idVendor: 0x0813, idProduct: 0x0002 }, /* Intel Play Me2Cam OV511+ */
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, device_table);
+
#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
static struct palette_list plist[] = {
{ VIDEO_PALETTE_GREY, "GREY" },
ov511->bridge == BRG_OV511PLUS ? "OV511+" :
"unknown");
out += sprintf (out, "sensor : %s\n",
+ ov511->sensor == SEN_OV6620 ? "OV6620" :
ov511->sensor == SEN_OV7610 ? "OV7610" :
ov511->sensor == SEN_OV7620 ? "OV7620" :
ov511->sensor == SEN_OV7620AE ? "OV7620AE" :
}
#endif /* CONFIG_PROC_FS && CONFIG_VIDEO_PROC_FS */
-
/**********************************************************************
*
* Camera interface
{
int rc;
- PDEBUG(3, "Reset: type=0x%X", reset_type);
+ PDEBUG(4, "Reset: type=0x%X", reset_type);
rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, reset_type);
rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0);
} else if ((ov511->sensor == SEN_OV7620)
|| (ov511->sensor == SEN_OV7620AE)) {
#if 0
- cur_con = ov511_i2c_read(dev, OV7610_REG_CNT);
- cur_brt = ov511_i2c_read(dev, OV7610_REG_BRT);
- // DEBUG_CODE
- PDEBUG(1, "con=%d brt=%d", ov511_i2c_read(dev, OV7610_REG_CNT),
- ov511_i2c_read(dev, OV7610_REG_BRT));
+ int cur_sat, new_sat, tmp;
- if (ov511_i2c_write(dev, OV7610_REG_CNT, p->contrast >> 8) < 0)
+ cur_sat = ov511_i2c_read(dev, OV7610_REG_BLUE);
+
+ tmp = (p->hue >> 8) - cur_sat;
+ new_sat = (tmp < 0) ? (-tmp) | 0x80 : tmp;
+
+ PDEBUG(1, "cur=%d target=%d diff=%d", cur_sat, p->hue >> 8, tmp);
+
+ if (ov511_i2c_write(dev, OV7610_REG_BLUE, new_sat) < 0)
return -EIO;
+
+ // DEBUG_CODE
+ PDEBUG(1, "hue=%d", ov511_i2c_read(dev, OV7610_REG_BLUE));
+
#endif
}
return 0;
}
+/* Returns number of bits per pixel (regardless of where they are located; planar or
+ * not), or zero for unsupported format.
+ */
+static int ov511_get_depth(int palette)
+{
+ switch (palette) {
+ case VIDEO_PALETTE_GREY: return 8;
+ case VIDEO_PALETTE_RGB565: return 16;
+ case VIDEO_PALETTE_RGB24: return 24;
+ case VIDEO_PALETTE_YUV422: return 16;
+ case VIDEO_PALETTE_YUYV: return 16;
+ case VIDEO_PALETTE_YUV420: return 24;
+ case VIDEO_PALETTE_YUV422P: return 24; /* Planar */
+ default: return 0; /* Invalid format */
+ }
+}
+
/* LNCNT values fixed by Lawrence Glaister <lg@jfm.bc.ca> */
static struct mode_list mlist[] = {
/* W H C PXCNT LNCNT PXDIV LNDIV M420 COMA COML */
if (ov511_stop(ov511->dev) < 0)
return -EIO;
+ /* Dumppix only works with RGB24 */
+ if (dumppix && (mode != VIDEO_PALETTE_RGB24)) {
+ err("dumppix only supported with RGB 24");
+ return -EINVAL;
+ }
+
if (mode == VIDEO_PALETTE_GREY) {
ov511_reg_write(dev, 0x16, 0x00);
if (ov511->sensor == SEN_OV7610
break;
case SEN_OV6620:
hwsbase = 0x38;
- hwebase = 0x39;
- vwsbase = 0x03;
- vwebase = 0x04;
+ hwebase = 0x3a;
+ vwsbase = 0x05;
+ vwebase = 0x06;
break;
case SEN_OV7620:
hwsbase = 0x2c;
/* Calculate and set the clock divisor */
clock = ((sub_flag ? ov511->subw * ov511->subh : width * height)
* (mlist[i].color ? 3 : 2) / 2) / 66000;
+#if 0
+ clock *= cams;
+#endif
ov511_i2c_write(dev, 0x11, clock);
-#ifdef OV511_GBR422
- ov511_i2c_write(dev, 0x12, mlist[i].common_A | 0x08);
-#else
- ov511_i2c_write(dev, 0x12, mlist[i].common_A | (testpat?0x02:0x00));
-#endif
+ /* We only have code to convert GBR -> RGB24 */
+ if ((mode == VIDEO_PALETTE_RGB24) && sensor_gbr)
+ ov511_i2c_write(dev, 0x12, mlist[i].common_A | (testpat?0x0a:0x08));
+ else
+ ov511_i2c_write(dev, 0x12, mlist[i].common_A | (testpat?0x02:0x00));
/* 7620/6620 don't have register 0x35, so play it safe */
if (ov511->sensor == SEN_OV7610 ||
static inline void
ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v,
- int rowPixels, unsigned char * rgb)
+ int rowPixels, unsigned char * rgb, int bits)
{
const int rvScale = 91881;
const int guScale = -22553;
yTL *= yScale; yTR *= yScale;
yBL *= yScale; yBR *= yScale;
- /* Write out top two pixels */
- rgb[0] = LIMIT(b+yTL); rgb[1] = LIMIT(g+yTL); rgb[2] = LIMIT(r+yTL);
- rgb[3] = LIMIT(b+yTR); rgb[4] = LIMIT(g+yTR); rgb[5] = LIMIT(r+yTR);
+ if (bits == 24) {
+ /* Write out top two pixels */
+ rgb[0] = LIMIT(b+yTL); rgb[1] = LIMIT(g+yTL); rgb[2] = LIMIT(r+yTL);
+ rgb[3] = LIMIT(b+yTR); rgb[4] = LIMIT(g+yTR); rgb[5] = LIMIT(r+yTR);
- /* Skip down to next line to write out bottom two pixels */
- rgb += 3 * rowPixels;
- rgb[0] = LIMIT(b+yBL); rgb[1] = LIMIT(g+yBL); rgb[2] = LIMIT(r+yBL);
- rgb[3] = LIMIT(b+yBR); rgb[4] = LIMIT(g+yBR); rgb[5] = LIMIT(r+yBR);
+ /* Skip down to next line to write out bottom two pixels */
+ rgb += 3 * rowPixels;
+ rgb[0] = LIMIT(b+yBL); rgb[1] = LIMIT(g+yBL); rgb[2] = LIMIT(r+yBL);
+ rgb[3] = LIMIT(b+yBR); rgb[4] = LIMIT(g+yBR); rgb[5] = LIMIT(r+yBR);
+ } else if (bits == 16) {
+ /* Write out top two pixels */
+ rgb[0] = ((LIMIT(b+yTL) >> 3) & 0x1F) | ((LIMIT(g+yTL) << 3) & 0xE0);
+ rgb[1] = ((LIMIT(g+yTL) >> 5) & 0x07) | (LIMIT(r+yTL) & 0xF8);
+
+ rgb[2] = ((LIMIT(b+yTR) >> 3) & 0x1F) | ((LIMIT(g+yTR) << 3) & 0xE0);
+ rgb[3] = ((LIMIT(g+yTR) >> 5) & 0x07) | (LIMIT(r+yTR) & 0xF8);
+
+ /* Skip down to next line to write out bottom two pixels */
+ rgb += 2 * rowPixels;
+
+ rgb[0] = ((LIMIT(b+yBL) >> 3) & 0x1F) | ((LIMIT(g+yBL) << 3) & 0xE0);
+ rgb[1] = ((LIMIT(g+yBL) >> 5) & 0x07) | (LIMIT(r+yBL) & 0xF8);
+
+ rgb[2] = ((LIMIT(b+yBR) >> 3) & 0x1F) | ((LIMIT(g+yBR) << 3) & 0xE0);
+ rgb[3] = ((LIMIT(g+yBR) >> 5) & 0x07) | (LIMIT(r+yBR) & 0xF8);
+ }
}
/*
* Note that the U and V data in one segment represents a 16 x 16 pixel
* area, but the Y data represents a 32 x 8 pixel area.
*
- * If OV511_DUMPPIX is defined, _parse_data just dumps the incoming segments,
+ * If dumppix module param is set, _parse_data just dumps the incoming segments,
* verbatim, in order, into the frame. When used with vidcat -f ppm -s 640x480
* this puts the data on the standard output and can be analyzed with the
* parseppm.c utility I wrote. That's a much faster way for figuring out how
#define HDIV 8
#define WDIV (256/HDIV)
-#undef OV511_DUMPPIX
-
-/* #define this and OV511_DUMPPIX to disable parsing of UV data */
-#undef OV511_FORCE_MONO
-
-#ifdef OV511_GBR422
static void
-ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0,
+ov511_parse_gbr422_to_rgb24(unsigned char *pIn0, unsigned char *pOut0,
int iOutY, int iOutUV, int iHalf, int iWidth)
{
int k, l, m;
}
}
-#else
-
static void
-ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0,
- int iOutY, int iOutUV, int iHalf, int iWidth)
+ov511_parse_yuv420_to_rgb(unsigned char *pIn0, unsigned char *pOut0,
+ int iOutY, int iOutUV, int iHalf, int iWidth, int bits)
{
-#ifndef OV511_DUMPPIX
int k, l, m;
+ int bytes = bits >> 3;
unsigned char *pIn;
unsigned char *pOut, *pOut1;
for (l = 0; l < 8; l++) {
for (m = 0; m < 8; m++) {
*pOut1 = *pIn++;
- pOut1 += 3;
+ pOut1 += bytes;
}
- pOut1 += (iWidth - 8) * 3;
+ pOut1 += (iWidth - 8) * bytes;
}
- pOut += 8 * 3;
+ pOut += 8 * bytes;
}
}
for (l = 0; l < 4; l++) {
for (m=0; m<8; m++) {
int y00 = *(pOut);
- int y01 = *(pOut+3);
- int y10 = *(pOut+iWidth*3);
- int y11 = *(pOut+iWidth*3+3);
+ int y01 = *(pOut+bytes);
+ int y10 = *(pOut+iWidth*bytes);
+ int y11 = *(pOut+iWidth*bytes+bytes);
int v = *(pIn+64) - 128;
int u = *pIn++ - 128;
ov511_move_420_block(y00, y01, y10, y11, u, v, iWidth,
- pOut);
- pOut += 6;
+ pOut, bits);
+ pOut += 2 * bytes;
}
- pOut += (iWidth*2 - 16) * 3;
+ pOut += (iWidth*2 - 16) * bytes;
}
/* Just copy the other UV rows */
for (m = 0; m < 8; m++) {
*pOut++ = *(pIn + 64);
*pOut = *pIn++;
- pOut += 5;
+ pOut += 2 * bytes - 1;
}
- pOut += (iWidth*2 - 16) * 3;
+ pOut += (iWidth*2 - 16) * bytes;
}
/* Calculate values if it's the second half */
int v = *pOut1 - 128;
int u = *(pOut1+1) - 128;
ov511_move_420_block(y00, y01, y10,
- y11, u, v, iWidth, pOut1);
- pOut1 += 6;
+ y11, u, v, iWidth, pOut1, bits);
+ pOut1 += 2 * bytes;
}
- pOut1 += (iWidth*2 - 8) * 3;
+ pOut1 += (iWidth*2 - 8) * bytes;
pIn += 8;
}
- pOut += 8 * 3;
- }
- }
-#else
-
-#ifndef OV511_FORCE_MONO
- /* Just dump pix data straight out for debug */
- int i, j;
-
- pOut0 += iOutY;
- for (i = 0; i < HDIV; i++) {
- for (j = 0; j < WDIV; j++) {
- *pOut0++ = *pIn0++;
- *pOut0++ = *pIn0++;
- *pOut0++ = *pIn0++;
+ pOut += 8 * bytes;
}
- pOut0 += (iWidth - WDIV) * 3;
}
-#else
+}
-#if 1
- /* This converts the Y data to "black-and-white" RGB data */
- /* Useful for experimenting with compression */
- int k, l, m;
+static void
+ov511_dumppix(unsigned char *pIn0, unsigned char *pOut0,
+ int iOutY, int iOutUV, int iHalf, int iWidth)
+{
+ int i, j, k;
unsigned char *pIn, *pOut, *pOut1;
- pIn = pIn0 + 128;
- pOut = pOut0 + iOutY;
- for (k = 0; k < 4; k++) {
- pOut1 = pOut;
- for (l = 0; l < 8; l++) {
- for (m = 0; m < 8; m++) {
- *pOut1++ = *pIn;
- *pOut1++ = *pIn;
- *pOut1++ = *pIn++;
+ switch (dumppix) {
+ case 1: /* Just dump YUV data straight out for debug */
+ pOut0 += iOutY;
+ for (i = 0; i < HDIV; i++) {
+ for (j = 0; j < WDIV; j++) {
+ *pOut0++ = *pIn0++;
+ *pOut0++ = *pIn0++;
+ *pOut0++ = *pIn0++;
}
- pOut1 += (iWidth - 8) * 3;
+ pOut0 += (iWidth - WDIV) * 3;
}
- pOut += 8 * 3;
- }
-#else
- /* This will dump the Y channel data stream as-is */
- int count;
- unsigned char *pIn, *pOut;
-
- pIn = pIn0 + 128;
- pOut = pOut0 + output_offset;
- for (count = 0; count < 256; count++) {
- *pOut++ = *pIn;
- *pOut++ = *pIn;
- *pOut++ = *pIn++;
- output_offset += 3;
- }
-#endif
-
-#endif
-
-#endif
+ break;
+ case 2: /* This converts the Y data to "black-and-white" RGB data */
+ /* Useful for experimenting with compression */
+ pIn = pIn0 + 128;
+ pOut = pOut0 + iOutY;
+ for (i = 0; i < 4; i++) {
+ pOut1 = pOut;
+ for (j = 0; j < 8; j++) {
+ for (k = 0; k < 8; k++) {
+ *pOut1++ = *pIn;
+ *pOut1++ = *pIn;
+ *pOut1++ = *pIn++;
+ }
+ pOut1 += (iWidth - 8) * 3;
+ }
+ pOut += 8 * 3;
+ }
+ break;
+ case 3: /* This will dump only the Y channel data stream as-is */
+ pIn = pIn0 + 128;
+ pOut = pOut0 + output_offset;
+ for (i = 0; i < 256; i++) {
+ *pOut++ = *pIn;
+ *pOut++ = *pIn;
+ *pOut++ = *pIn++;
+ output_offset += 3;
+ }
+ break;
+ } /* End switch (dumppix) */
}
-#endif
/* This converts YUV420 segments to YUYV */
static void
/* Frame start */
PDEBUG(4, "Frame start, framenum = %d", ov511->curframe);
+#if 0
+ /* Make sure no previous data carries over; necessary
+ * for compression experimentation */
+ memset(frame->data, 0, MAX_DATA_SIZE);
+#endif
output_offset = 0;
/* Check to see if it's a snapshot frame */
ov511_parse_data_grey (pData, pOut, iOutY, frame->width);
break;
case VIDEO_PALETTE_RGB24:
- ov511_parse_data_rgb24 (pData, pOut, iOutY, iOutUV,
- iY & 1, frame->width);
+ if (dumppix)
+ ov511_dumppix(pData, pOut, iOutY, iOutUV,
+ iY & 1, frame->width);
+ else if (sensor_gbr)
+ ov511_parse_gbr422_to_rgb24(pData, pOut, iOutY, iOutUV,
+ iY & 1, frame->width);
+ else
+ ov511_parse_yuv420_to_rgb(pData, pOut, iOutY, iOutUV,
+ iY & 1, frame->width, 24);
+ break;
+ case VIDEO_PALETTE_RGB565:
+ ov511_parse_yuv420_to_rgb(pData, pOut, iOutY, iOutUV,
+ iY & 1, frame->width, 16);
break;
case VIDEO_PALETTE_YUV422:
case VIDEO_PALETTE_YUYV:
static void ov511_isoc_irq(struct urb *urb)
{
int len;
- struct usb_ov511 *ov511 = urb->context;
+ struct usb_ov511 *ov511;
struct ov511_sbuf *sbuf;
- if (!ov511->dev)
+ if (!urb->context) {
+ PDEBUG(4, "no context");
return;
+ }
+
+ ov511 = (struct usb_ov511 *) urb->context;
+
+ if (!ov511->dev || !ov511->user) {
+ PDEBUG(4, "no device, or not open");
+ return;
+ }
if (!ov511->streaming) {
PDEBUG(4, "hmmm... not streaming, but got interrupt");
/* Move to the next sbuf */
ov511->cursbuf = (ov511->cursbuf + 1) % OV511_NUMSBUF;
+ urb->dev = ov511->dev;
+
return;
}
ov511->sbuf[n].urb->next = ov511->sbuf[n+1].urb;
for (n = 0; n < OV511_NUMSBUF; n++) {
+ ov511->sbuf[n].urb->dev = ov511->dev;
err = usb_submit_urb(ov511->sbuf[n].urb);
if (err)
err("init isoc: usb_submit_urb(%d) ret %d", n, err);
static int ov511_open(struct video_device *dev, int flags)
{
struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
- int err = 0;
+ int err;
MOD_INC_USE_COUNT;
PDEBUG(4, "opening");
b.audios = 0;
b.maxwidth = ov511->maxwidth;
b.maxheight = ov511->maxheight;
- b.minwidth = 32;
- b.minheight = 16;
+ b.minwidth = 160;
+ b.minheight = 120;
if (copy_to_user(arg, &b, sizeof(b)))
return -EFAULT;
if (copy_from_user(&p, arg, sizeof(p)))
return -EFAULT;
- if (p.palette != VIDEO_PALETTE_GREY &&
- p.palette != VIDEO_PALETTE_RGB24 &&
- p.palette != VIDEO_PALETTE_YUV422 &&
- p.palette != VIDEO_PALETTE_YUYV &&
- p.palette != VIDEO_PALETTE_YUV420 &&
- p.palette != VIDEO_PALETTE_YUV422P)
+ if (!ov511_get_depth(p.palette))
return -EINVAL;
if (ov7610_set_picture(ov511, &p))
case VIDIOCMCAPTURE:
{
struct video_mmap vm;
- int ret;
+ int ret, depth;
if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm)))
return -EFAULT;
PDEBUG(4, "frame: %d, size: %dx%d, format: %d",
vm.frame, vm.width, vm.height, vm.format);
- if (vm.format != VIDEO_PALETTE_RGB24 &&
- vm.format != VIDEO_PALETTE_YUV422 &&
- vm.format != VIDEO_PALETTE_YUYV &&
- vm.format != VIDEO_PALETTE_YUV420 &&
- vm.format != VIDEO_PALETTE_YUV422P &&
- vm.format != VIDEO_PALETTE_GREY)
+ depth = ov511_get_depth(vm.format);
+ if (!depth) {
+ err("VIDIOCMCAPTURE: invalid format (%d)", vm.format);
return -EINVAL;
+ }
- if ((vm.frame != 0) && (vm.frame != 1))
+ if ((vm.frame != 0) && (vm.frame != 1)) {
+ err("VIDIOCMCAPTURE: invalid frame (%d)", vm.frame);
return -EINVAL;
-
- if (vm.width > ov511->maxwidth || vm.height > ov511->maxheight)
+ }
+
+ if (vm.width > ov511->maxwidth || vm.height > ov511->maxheight) {
+ err("VIDIOCMCAPTURE: requested dimensions too big");
return -EINVAL;
+ }
if (ov511->frame[vm.frame].grabstate == FRAME_GRABBING)
return -EBUSY;
ov511->frame[vm.frame].format = vm.format;
ov511->frame[vm.frame].sub_flag = ov511->sub_flag;
ov511->frame[vm.frame].segsize = GET_SEGSIZE(vm.format);
- ov511->frame[vm.frame].depth = GET_DEPTH(vm.format);
+ ov511->frame[vm.frame].depth = depth;
/* Mark it as ready */
ov511->frame[vm.frame].grabstate = FRAME_READY;
goto redo;
}
case FRAME_DONE:
+ if (ov511->snap_enabled && !ov511->frame[frame].snapshot) {
+ int ret;
+ if ((ret = ov511_new_frame(ov511, frame)) < 0)
+ return ret;
+ goto redo;
+ }
+
ov511->frame[frame].grabstate = FRAME_UNUSED;
/* Reset the hardware snapshot button */
PDEBUG(4, "mmap: %ld (%lX) bytes", size, size);
- if (size > (((2 * MAX_DATA_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
+ if (size > (((OV511_NUMFRAMES * MAX_DATA_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
return -EINVAL;
pos = (unsigned long)ov511->fbuf;
init_waitqueue_head(&ov511->wq);
- if (video_register_device(&ov511->vdev, VFL_TYPE_GRABBER) == -1) {
+ if (video_register_device(&ov511->vdev, VFL_TYPE_GRABBER) < 0) {
err("video_register_device failed");
return -EBUSY;
}
*
***************************************************************************/
-static void* ov511_probe(struct usb_device *dev, unsigned int ifnum,
- const struct usb_device_id *id)
+static void * __devinit
+ov511_probe(struct usb_device *dev, unsigned int ifnum,
+ const struct usb_device_id *id)
{
struct usb_interface_descriptor *interface;
struct usb_ov511 *ov511;
interface = &dev->actconfig->interface[ifnum].altsetting[0];
- /* Checking vendor/product should be enough, but what the hell.
- USB ID matching cannot check for a bInterfaceSubclass value
- of 0, so we check it here. */
+ /* Checking vendor/product should be enough, but what the hell */
+ if (interface->bInterfaceClass != 0xFF)
+ return NULL;
if (interface->bInterfaceSubClass != 0x00)
return NULL;
ov511->bridge = BRG_OV511PLUS;
break;
case 0x0002:
+ if (dev->descriptor.idVendor != 0x0813)
+ goto error;
info("Intel Play Me2Cam (OV511+) found");
ov511->bridge = BRG_OV511PLUS;
break;
+ default:
+ err("Unknown product ID");
+ goto error;
}
ov511->customid = ov511_reg_read(dev, OV511_REG_SYSTEM_CUSTOM_ID);
}
-static void ov511_disconnect(struct usb_device *dev, void *ptr)
+static void __devexit
+ov511_disconnect(struct usb_device *dev, void *ptr)
{
struct usb_ov511 *ov511 = (struct usb_ov511 *) ptr;
int n;
static struct usb_driver ov511_driver = {
name: "ov511",
+ id_table: device_table,
probe: ov511_probe,
- disconnect: ov511_disconnect,
- id_table: ov511_table,
+ disconnect: ov511_disconnect
};
#include <linux/etherdevice.h>
#include <linux/usb.h>
#include <linux/module.h>
-
-
-static const char *version = __FILE__ ": v0.4.13 2000/10/13 (C) 1999-2000 Petko Manolov (petkan@dce.bg)";
+#include "pegasus.h"
#define PEGASUS_USE_INTR
+#define PEGASUS_WRITE_EEPROM
-
-#define PEGASUS_II 0x80000000
-#define HAS_HOME_PNA 0x40000000
-
-#define PEGASUS_MTU 1500
-#define PEGASUS_MAX_MTU 1536
-
-#define EPROM_WRITE 0x01
-#define EPROM_READ 0x02
-#define EPROM_DONE 0x04
-#define EPROM_WR_ENABLE 0x10
-#define EPROM_LOAD 0x20
-
-#define MII_BMCR 0x00
-#define MII_BMSR 0x01
-#define BMSR_MEDIA 0x7808
-#define MII_ANLPA 0x05
-#define ANLPA_100TX_FD 0x0100
-#define ANLPA_100TX_HD 0x0080
-#define ANLPA_10T_FD 0x0040
-#define ANLPA_10T_HD 0x0020
-#define PHY_DONE 0x80
-#define PHY_READ 0x40
-#define PHY_WRITE 0x20
-#define DEFAULT_GPIO_RESET 0x24
-#define LINKSYS_GPIO_RESET 0x24
-#define DEFAULT_GPIO_SET 0x26
-
-#define PEGASUS_PRESENT 0x00000001
-#define PEGASUS_RUNNING 0x00000002
-#define PEGASUS_TX_BUSY 0x00000004
-#define PEGASUS_RX_BUSY 0x00000008
-#define CTRL_URB_RUNNING 0x00000010
-#define CTRL_URB_SLEEP 0x00000020
-#define PEGASUS_UNPLUG 0x00000040
-#define ETH_REGS_CHANGE 0x40000000
-#define ETH_REGS_CHANGED 0x80000000
-
-#define RX_MULTICAST 2
-#define RX_PROMISCUOUS 4
-
-#define REG_TIMEOUT (HZ)
-#define PEGASUS_TX_TIMEOUT (HZ*10)
-
-#define TX_UNDERRUN 0x80
-#define EXCESSIVE_COL 0x40
-#define LATE_COL 0x20
-#define NO_CARRIER 0x10
-#define LOSS_CARRIER 0x08
-#define JABBER_TIMEOUT 0x04
-
-#define PEGASUS_REQT_READ 0xc0
-#define PEGASUS_REQT_WRITE 0x40
-#define PEGASUS_REQ_GET_REGS 0xf0
-#define PEGASUS_REQ_SET_REGS 0xf1
-#define PEGASUS_REQ_SET_REG PEGASUS_REQ_SET_REGS
-#define ALIGN(x) x __attribute__((aligned(L1_CACHE_BYTES)))
-
-enum pegasus_registers {
- EthCtrl0 = 0,
- EthCtrl1 = 1,
- EthCtrl2 = 2,
- EthID = 0x10,
- Reg1d = 0x1d,
- EpromOffset = 0x20,
- EpromData = 0x21, /* 0x21 low, 0x22 high byte */
- EpromCtrl = 0x23,
- PhyAddr = 0x25,
- PhyData = 0x26, /* 0x26 low, 0x27 high byte */
- PhyCtrl = 0x28,
- UsbStst = 0x2a,
- EthTxStat0 = 0x2b,
- EthTxStat1 = 0x2c,
- EthRxStat = 0x2d,
- Reg7b = 0x7b,
- Gpio0 = 0x7e,
- Gpio1 = 0x7f,
- Reg81 = 0x81,
-};
-
-
-typedef struct pegasus {
- struct usb_device *usb;
- struct net_device *net;
- struct net_device_stats stats;
- unsigned flags;
- unsigned features;
- int intr_interval;
- struct urb ctrl_urb, rx_urb, tx_urb, intr_urb;
- devrequest dr;
- wait_queue_head_t ctrl_wait;
- struct semaphore ctrl_sem;
- unsigned char ALIGN(rx_buff[PEGASUS_MAX_MTU]);
- unsigned char ALIGN(tx_buff[PEGASUS_MAX_MTU]);
- unsigned char ALIGN(intr_buff[8]);
- __u8 eth_regs[4];
- __u8 phy;
- __u8 gpio_res;
-} pegasus_t;
-
-struct usb_eth_dev {
- char *name;
- __u32 private; /* LSB is gpio reset value */
-};
+static const char *version = __FILE__ ": v0.4.17 2000/11/13 (C) 1999-2000 Petko Manolov (petkan@dce.bg)";
static int loopback = 0;
static int mii_mode = 0;
static int multicast_filter_limit = 32;
+static struct usb_eth_dev usb_dev_id[] = {
+#define PEGASUS_DEV(pn, vid, pid, flags) \
+ {name:pn, vendor:vid, device:pid, private:flags},
+#include "pegasus.h"
+#undef PEGASUS_DEV
+ {NULL, 0, 0, 0}
+};
+
+static struct usb_device_id pegasus_ids[] = {
+#define PEGASUS_DEV(pn, vid, pid, flags) {idVendor:vid, idProduct:pid},
+#include "pegasus.h"
+#undef PEGASUS_DEV
+ { }
+};
+
MODULE_AUTHOR("Petko Manolov <petkan@dce.bg>");
MODULE_DESCRIPTION("ADMtek AN986 Pegasus USB Ethernet driver");
MODULE_PARM(loopback, "i");
MODULE_PARM(mii_mode, "i");
MODULE_PARM_DESC(loopback, "Enable MAC loopback mode (bit 0)");
-MODULE_PARM_DESC(mii_mode, "Enable HomePNA mode (bit 0) - default = MII mode = 0");
-
-
-#define PEGASUS_DEV(product_name, vendor_id, product_id, flags) \
- {name: product_name, private: flags},
-
-static struct usb_eth_dev usb_dev_id[] = {
-
-# define PEGASUS_DEV(product_name, vendor_id, product_id, flags) \
- {name: product_name, private: flags},
-# include "pegasus_devs.h"
-# undef PEGASUS_DEV
-
- {NULL, 0 }
-};
-
-
-/* The entries in this table MUST CORRESPOND LINE-BY-LINE to the
- entries of usb_dev_id[]. */
-static struct usb_device_id pegasus_ids [] = {
-# define PEGASUS_DEV(product_name, vendor_id, product_id, flags) \
- {idVendor: vendor_id, idProduct: product_id },
-# include "pegasus_devs.h"
-# undef PEGASUS_DEV
-
- { } /* Terminating entry */
-};
+MODULE_PARM_DESC(mii_mode, "Enable HomePNA mode (bit 0),default=MII mode = 0");
MODULE_DEVICE_TABLE (usb, pegasus_ids);
+
static int update_eth_regs_async( pegasus_t * );
/* Aargh!!! I _really_ hate such tweaks */
static void ctrl_callback( urb_t *urb )
{
int ret;
- if ( pegasus->flags & ETH_REGS_CHANGED ) {
+ while ( pegasus->flags & ETH_REGS_CHANGED ) {
pegasus->flags |= CTRL_URB_SLEEP;
interruptible_sleep_on( &pegasus->ctrl_wait );
}
{
int ret;
- if ( pegasus->flags & ETH_REGS_CHANGED ) {
+ while ( pegasus->flags & ETH_REGS_CHANGED ) {
pegasus->flags |= CTRL_URB_SLEEP ;
interruptible_sleep_on( &pegasus->ctrl_wait );
}
{
int ret;
- if ( pegasus->flags & ETH_REGS_CHANGED ) {
+ while ( pegasus->flags & ETH_REGS_CHANGED ) {
pegasus->flags |= CTRL_URB_SLEEP;
interruptible_sleep_on( &pegasus->ctrl_wait );
}
}
-static int read_phy_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd )
+static int read_mii_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd )
{
int i;
__u8 data[4] = { phy, 0, 0, indx };
}
-static int write_phy_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 regd )
+static int write_mii_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 regd )
{
int i;
__u8 data[4] = { phy, 0, 0, indx };
}
if ( i == REG_TIMEOUT )
return 1;
+
+ if ( usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS ||
+ usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK1 ) {
+ __u16 auxmode;
+
+ read_mii_word( pegasus, 0, 0x1b, &auxmode );
+ write_mii_word( pegasus, 0, 0x1b, auxmode | 4 );
+ }
+
return 0;
}
pegasus_t *pegasus = dev->priv;
- if ( read_phy_word(pegasus, pegasus->phy, MII_BMSR, &bmsr) )
- return 2;
+ if ( read_mii_word(pegasus, pegasus->phy, MII_BMSR, &bmsr) )
+ return 1;
if ( !(bmsr & 0x20) && !loopback )
warn( "%s: link NOT established (0x%x) - check the cable.",
dev->name, bmsr );
- if ( read_phy_word(pegasus, pegasus->phy, MII_ANLPA, &linkpart) )
- return 4;
+ if ( read_mii_word(pegasus, pegasus->phy, MII_ANLPA, &linkpart) )
+ return 2;
if ( !(linkpart & 1) )
warn( "link partner stat %x", linkpart );
if ( pegasus->flags & PEGASUS_RX_BUSY ) {
pegasus->stats.rx_errors++;
+ dbg("pegasus Rx busy");
return;
}
pegasus->flags |= PEGASUS_RX_BUSY;
- rx_status = *(int *)(pegasus->rx_buff + count - 4);
-
- if (urb->status) {
- dbg("%s: RX status %d", net->name, urb->status);
- goto goon;
+ switch ( urb->status ) {
+ case USB_ST_NOERROR:
+ break;
+ case USB_ST_NORESPONSE:
+ dbg( "reset MAC" );
+ pegasus->flags &= ~PEGASUS_RX_BUSY;
+ break;
+ default:
+ dbg( "%s: RX status %d", net->name, urb->status );
+ goto goon;
}
if ( !count )
goto goon;
+ rx_status = *(int *)(pegasus->rx_buff + count - 4);
if ( rx_status & 0x000e0000 ) {
-
- dbg("%s: error receiving packet %x", net->name, rx_status & 0xe0000);
+ dbg("%s: RX packet error %x", net->name, rx_status & 0xe0000);
pegasus->stats.rx_errors++;
if ( rx_status & 0x060000 )
pegasus->stats.rx_length_errors++;
pegasus->stats.rx_crc_errors++;
if ( rx_status & 0x100000 )
pegasus->stats.rx_frame_errors++;
-
goto goon;
}
if ( !pegasus )
return;
+
+ switch ( urb->status ) {
+ case USB_ST_NOERROR:
+ break;
+ case USB_ST_URB_KILLED:
+ return;
+ default:
+ info("intr status %d", urb->status);
+ }
+
d = urb->transfer_buffer;
net = pegasus->net;
if ( d[0] & 0xfc ) {
if ( d[0] & (NO_CARRIER | LOSS_CARRIER) )
pegasus->stats.tx_carrier_errors++;
}
- switch ( urb->status ) {
- case USB_ST_NOERROR:
- break;
- case USB_ST_URB_KILLED:
- break;
- default:
- info("intr status %d", urb->status);
- }
}
#endif
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,48)
static void pegasus_tx_timeout( struct net_device *net )
{
pegasus_t *pegasus = net->priv;
usb_unlink_urb( &pegasus->tx_urb );
pegasus->stats.tx_errors++;
}
+#endif
static int pegasus_start_xmit( struct sk_buff *skb, struct net_device *net )
__u8 data[2];
read_eprom_word( pegasus, 4, (__u16 *)data );
+ if ( data[1] < 0x80 ) {
+ info( "intr interval will be changed from %ums to %ums",
+ data[1], 0x80 );
+ data[1] = 0x80;
+#ifdef PEGASUS_WRITE_EEPROM
+ write_eprom_word( pegasus, 4, *(__u16 *)data );
+#endif
+ }
pegasus->intr_interval = data[1];
}
if ( (res = usb_submit_urb(&pegasus->rx_urb)) )
warn( __FUNCTION__ " failed rx_urb %d", res );
#ifdef PEGASUS_USE_INTR
- get_interrupt_interval( pegasus );
FILL_INT_URB( &pegasus->intr_urb, pegasus->usb,
usb_rcvintpipe(pegasus->usb, 3),
pegasus->intr_buff, sizeof(pegasus->intr_buff),
case SIOCDEVPRIVATE:
data[0] = pegasus->phy;
case SIOCDEVPRIVATE+1:
- read_phy_word(pegasus, data[0], data[1]&0x1f, &data[3]);
+ read_mii_word(pegasus, data[0], data[1]&0x1f, &data[3]);
return 0;
case SIOCDEVPRIVATE+2:
if ( !capable(CAP_NET_ADMIN) )
return -EPERM;
- write_phy_word(pegasus, pegasus->phy, data[1] & 0x1f, data[2]);
+ write_mii_word(pegasus, pegasus->phy, data[1] & 0x1f, data[2]);
return 0;
default:
return -EOPNOTSUPP;
__u16 tmp;
for ( i=0; i < 32; i++ ) {
- read_phy_word( pegasus, i, MII_BMSR, &tmp );
+ read_mii_word( pegasus, i, MII_BMSR, &tmp );
if ( tmp == 0 || tmp == 0xffff || (tmp & BMSR_MEDIA) == 0 )
continue;
else
return i;
}
- return 0;
+ return 0xff;
}
static void * pegasus_probe( struct usb_device *dev, unsigned int ifnum,
const struct usb_device_id *id)
{
- struct net_device *net;
- pegasus_t *pegasus;
- const int dev_indx = id - pegasus_ids;
+ struct net_device *net;
+ pegasus_t *pegasus;
+ int dev_index = id - pegasus_ids;
if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) {
err("usb_set_configuration() failed");
usb_inc_dev_use( dev );
memset(pegasus, 0, sizeof(struct pegasus));
- init_MUTEX( &pegasus-> ctrl_sem );
+ pegasus->dev_index = dev_index;
init_waitqueue_head( &pegasus->ctrl_wait );
net = init_etherdev( NULL, 0 );
net->get_stats = pegasus_netdev_stats;
net->mtu = PEGASUS_MTU;
- pegasus->features = usb_dev_id[dev_indx].private;
+ pegasus->features = usb_dev_id[dev_index].private;
+#ifdef PEGASUS_USE_INTR
+ get_interrupt_interval( pegasus );
+#endif
if ( reset_mac(pegasus) ) {
err("can't reset MAC");
unregister_netdev( pegasus->net );
return NULL;
}
- info( "%s: %s", net->name, usb_dev_id[dev_indx].name );
+ info( "%s: %s", net->name, usb_dev_id[dev_index].name );
set_ethernet_addr( pegasus );
}
pegasus->phy = mii_phy_probe( pegasus );
- if ( !pegasus->phy ) {
+ if ( pegasus->phy == 0xff ) {
warn( "can't locate MII phy, using default" );
pegasus->phy = 1;
}
--- /dev/null
+/*
+ * Copyright (c) 1999,2000 Petko Manolov - Petkan (petkan@dce.bg)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef PEGASUS_DEV
+
+#define PEGASUS_II 0x80000000
+#define HAS_HOME_PNA 0x40000000
+
+#define PEGASUS_MTU 1500
+#define PEGASUS_MAX_MTU 1536
+
+#define EPROM_WRITE 0x01
+#define EPROM_READ 0x02
+#define EPROM_DONE 0x04
+#define EPROM_WR_ENABLE 0x10
+#define EPROM_LOAD 0x20
+
+#define MII_BMCR 0x00
+#define MII_BMSR 0x01
+#define BMSR_MEDIA 0x7808
+#define MII_ANLPA 0x05
+#define ANLPA_100TX_FD 0x0100
+#define ANLPA_100TX_HD 0x0080
+#define ANLPA_10T_FD 0x0040
+#define ANLPA_10T_HD 0x0020
+#define PHY_DONE 0x80
+#define PHY_READ 0x40
+#define PHY_WRITE 0x20
+#define DEFAULT_GPIO_RESET 0x24
+#define LINKSYS_GPIO_RESET 0x24
+#define DEFAULT_GPIO_SET 0x26
+
+#define PEGASUS_PRESENT 0x00000001
+#define PEGASUS_RUNNING 0x00000002
+#define PEGASUS_TX_BUSY 0x00000004
+#define PEGASUS_RX_BUSY 0x00000008
+#define CTRL_URB_RUNNING 0x00000010
+#define CTRL_URB_SLEEP 0x00000020
+#define PEGASUS_UNPLUG 0x00000040
+#define ETH_REGS_CHANGE 0x40000000
+#define ETH_REGS_CHANGED 0x80000000
+
+#define RX_MULTICAST 2
+#define RX_PROMISCUOUS 4
+
+#define REG_TIMEOUT (HZ)
+#define PEGASUS_TX_TIMEOUT (HZ*10)
+
+#define TX_UNDERRUN 0x80
+#define EXCESSIVE_COL 0x40
+#define LATE_COL 0x20
+#define NO_CARRIER 0x10
+#define LOSS_CARRIER 0x08
+#define JABBER_TIMEOUT 0x04
+
+#define PEGASUS_REQT_READ 0xc0
+#define PEGASUS_REQT_WRITE 0x40
+#define PEGASUS_REQ_GET_REGS 0xf0
+#define PEGASUS_REQ_SET_REGS 0xf1
+#define PEGASUS_REQ_SET_REG PEGASUS_REQ_SET_REGS
+#define ALIGN(x) x __attribute__((aligned(L1_CACHE_BYTES)))
+
+enum pegasus_registers {
+ EthCtrl0 = 0,
+ EthCtrl1 = 1,
+ EthCtrl2 = 2,
+ EthID = 0x10,
+ Reg1d = 0x1d,
+ EpromOffset = 0x20,
+ EpromData = 0x21, /* 0x21 low, 0x22 high byte */
+ EpromCtrl = 0x23,
+ PhyAddr = 0x25,
+ PhyData = 0x26, /* 0x26 low, 0x27 high byte */
+ PhyCtrl = 0x28,
+ UsbStst = 0x2a,
+ EthTxStat0 = 0x2b,
+ EthTxStat1 = 0x2c,
+ EthRxStat = 0x2d,
+ Reg7b = 0x7b,
+ Gpio0 = 0x7e,
+ Gpio1 = 0x7f,
+ Reg81 = 0x81,
+};
+
+
+typedef struct pegasus {
+ struct usb_device *usb;
+ struct net_device *net;
+ struct net_device_stats stats;
+ unsigned flags;
+ unsigned features;
+ int dev_index;
+ int intr_interval;
+ struct urb ctrl_urb, rx_urb, tx_urb, intr_urb;
+ devrequest dr;
+ wait_queue_head_t ctrl_wait;
+ struct semaphore ctrl_sem;
+ unsigned char ALIGN(rx_buff[PEGASUS_MAX_MTU]);
+ unsigned char ALIGN(tx_buff[PEGASUS_MAX_MTU]);
+ unsigned char ALIGN(intr_buff[8]);
+ __u8 eth_regs[4];
+ __u8 phy;
+ __u8 gpio_res;
+} pegasus_t;
+
+
+struct usb_eth_dev {
+ char *name;
+ __u16 vendor;
+ __u16 device;
+ __u32 private; /* LSB is gpio reset value */
+};
+
+
+#define VENDOR_ACCTON 0x083a
+#define VENDOR_ADMTEK 0x07a6
+#define VENDOR_BILLIONTON 0x08dd
+#define VENDOR_COREGA 0x07aa
+#define VENDOR_DLINK1 0x2001
+#define VENDOR_DLINK2 0x07b8
+#define VENDOR_IODATA 0x04bb
+#define VENDOR_LANEED 0x056e
+#define VENDOR_LINKSYS 0x066b
+#define VENDOR_MELCO 0x0411
+#define VENDOR_SMC 0x0707
+#define VENDOR_SOHOWARE 0x15e8
+
+
+#else /* PEGASUS_DEV */
+
+
+PEGASUS_DEV( "Accton USB 10/100 Ethernet Adapter", VENDOR_ACCTON, 0x1046,
+ DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "ADMtek ADM8511 \"Pegasus II\" USB Ethernet",
+ VENDOR_ADMTEK, 0x8511,
+ DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "ADMtek AN986 \"Pegasus\" USB Ethernet (eval board)",
+ VENDOR_ADMTEK, 0x0986,
+ DEFAULT_GPIO_RESET | HAS_HOME_PNA )
+PEGASUS_DEV( "Billionton USB-100", VENDOR_BILLIONTON, 0x0986,
+ DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "Billionton USBLP-100", VENDOR_BILLIONTON, 0x0987,
+ DEFAULT_GPIO_RESET | HAS_HOME_PNA )
+PEGASUS_DEV( "Billionton USBEL-100", VENDOR_BILLIONTON, 0x0988,
+ DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "Billionton USBE-100", VENDOR_BILLIONTON, 0x8511,
+ DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "Corega FEter USB-TX", VENDOR_COREGA, 0x0004,
+ DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK1, 0x4001,
+ LINKSYS_GPIO_RESET )
+PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK1, 0x4002,
+ LINKSYS_GPIO_RESET )
+PEGASUS_DEV( "D-Link DSB-650TX(PNA)", VENDOR_DLINK1, 0x4003,
+ DEFAULT_GPIO_RESET | HAS_HOME_PNA )
+PEGASUS_DEV( "D-Link DSB-650", VENDOR_DLINK1, 0xabc1,
+ DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "D-Link DU-E10", VENDOR_DLINK2, 0xabc1,
+ DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "D-Link DU-E100", VENDOR_DLINK2, 0x4002,
+ DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "IO DATA USB ET/TX", VENDOR_IODATA, 0x0904,
+ DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "LANEED USB Ethernet LD-USB/TX", VENDOR_LANEED, 0x4002,
+ DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "Linksys USB10TX", VENDOR_LINKSYS, 0x2202,
+ LINKSYS_GPIO_RESET )
+PEGASUS_DEV( "Linksys USB100TX", VENDOR_LINKSYS, 0x2203,
+ LINKSYS_GPIO_RESET )
+PEGASUS_DEV( "Linksys USB100TX", VENDOR_LINKSYS, 0x2204,
+ LINKSYS_GPIO_RESET | HAS_HOME_PNA )
+PEGASUS_DEV( "Linksys USB Ethernet Adapter", VENDOR_LINKSYS, 0x2206,
+ LINKSYS_GPIO_RESET )
+PEGASUS_DEV( "MELCO/BUFFALO LUA-TX", VENDOR_MELCO, 0x0001,
+ DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "SMC 202 USB Ethernet", VENDOR_SMC, 0x0200,
+ DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "SOHOware NUB100 Ethernet", VENDOR_SOHOWARE, 0x9100,
+ DEFAULT_GPIO_RESET )
+
+
+#endif /* PEGASUS_DEV */
+++ /dev/null
-/* This file is derived from pegasus.c.
- * Copyright (c) 1999,2000 Petko Manolov - Petkan (petkan@dce.bg)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-
-/* This file should be included after defining the macro PEGASUS_DEV. */
-
-PEGASUS_DEV( "Billionton USB-100", 0x08dd, 0x0986,
- DEFAULT_GPIO_RESET )
-
-PEGASUS_DEV( "Billionton USBLP-100", 0x08dd, 0x0987,
- DEFAULT_GPIO_RESET | HAS_HOME_PNA )
-
-PEGASUS_DEV( "Billionton USBEL-100", 0x08dd, 0x0988,
- DEFAULT_GPIO_RESET )
-
-PEGASUS_DEV( "Billionton USBE-100", 0x08dd, 0x8511,
- DEFAULT_GPIO_RESET | PEGASUS_II )
-
-PEGASUS_DEV( "Corega FEter USB-TX", 0x7aa, 0x0004,
- DEFAULT_GPIO_RESET )
-
-PEGASUS_DEV( "MELCO/BUFFALO LUA-TX", 0x0411, 0x0001,
- DEFAULT_GPIO_RESET )
-
-PEGASUS_DEV( "D-Link DSB-650TX", 0x2001, 0x4001,
- LINKSYS_GPIO_RESET )
-
-PEGASUS_DEV( "D-Link DSB-650TX", 0x2001, 0x4002,
- LINKSYS_GPIO_RESET )
-
-PEGASUS_DEV( "D-Link DSB-650TX(PNA)", 0x2001, 0x4003,
- DEFAULT_GPIO_RESET | HAS_HOME_PNA )
-
-PEGASUS_DEV( "D-Link DSB-650", 0x2001, 0xabc1,
- DEFAULT_GPIO_RESET )
-
-PEGASUS_DEV( "D-Link DU-E10", 0x07b8, 0xabc1,
- DEFAULT_GPIO_RESET )
-
-PEGASUS_DEV( "D-Link DU-E100", 0x07b8, 0x4002,
- DEFAULT_GPIO_RESET )
-
-PEGASUS_DEV( "Linksys USB10TX", 0x066b, 0x2202,
- LINKSYS_GPIO_RESET )
-
-PEGASUS_DEV( "Linksys USB100TX", 0x066b, 0x2203,
- LINKSYS_GPIO_RESET )
-
-PEGASUS_DEV( "Linksys USB100TX", 0x066b, 0x2204,
- LINKSYS_GPIO_RESET | HAS_HOME_PNA )
-
-PEGASUS_DEV( "Linksys USB Ethernet Adapter", 0x066b, 0x2206,
- LINKSYS_GPIO_RESET )
-
-PEGASUS_DEV( "SMC 202 USB Ethernet", 0x0707, 0x0200,
- DEFAULT_GPIO_RESET )
-
-PEGASUS_DEV( "ADMtek AN986 \"Pegasus\" USB Ethernet (eval board)", 0x07a6, 0x0986,
- DEFAULT_GPIO_RESET | HAS_HOME_PNA )
-
-PEGASUS_DEV( "Accton USB 10/100 Ethernet Adapter", 0x083a, 0x1046,
- DEFAULT_GPIO_RESET )
-
-PEGASUS_DEV( "IO DATA USB ET/TX", 0x04bb, 0x0904,
- DEFAULT_GPIO_RESET )
-
-PEGASUS_DEV( "LANEED USB Ethernet LD-USB/TX", 0x056e, 0x4002,
- DEFAULT_GPIO_RESET )
-
-PEGASUS_DEV( "SOHOware NUB100 Ethernet", 0x15e8, 0x9100,
- DEFAULT_GPIO_RESET )
-
-PEGASUS_DEV( "ADMtek ADM8511 \"Pegasus II\" USB Ethernet", 0x07a6, 0x8511,
- DEFAULT_GPIO_RESET | PEGASUS_II )
}
#ifdef DEBUG
- usblp_check_status(usblp);
+ usblp_check_status(usblp, 0);
#endif
info("usblp%d: USB %sdirectional printer dev %d if %d alt %d",
static int belkin_sa_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg);
static void belkin_sa_break_ctl (struct usb_serial_port *port, int break_state );
+
+static __devinitdata struct usb_device_id id_table_combined [] = {
+ { idVendor: BELKIN_SA_VID, idProduct: BELKIN_SA_PID },
+ { idVendor: BELKIN_OLD_VID, idProduct: BELKIN_OLD_PID },
+ { idVendor: PERACOM_VID, idProduct: PERACOM_PID },
+ { } /* Terminating entry */
+};
+
+static __devinitdata struct usb_device_id belkin_sa_table [] = {
+ { idVendor: BELKIN_SA_VID, idProduct: BELKIN_SA_PID },
+ { } /* Terminating entry */
+};
+
+static __devinitdata struct usb_device_id belkin_old_table [] = {
+ { idVendor: BELKIN_OLD_VID, idProduct: BELKIN_OLD_PID },
+ { } /* Terminating entry */
+};
+
+static __devinitdata struct usb_device_id peracom_table [] = {
+ { idVendor: PERACOM_VID, idProduct: PERACOM_PID },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, id_table_combined);
+
/* All of the device info needed for the Belkin serial converter */
-static __u16 belkin_sa_vendor_id = BELKIN_SA_VID;
-static __u16 belkin_sa_product_id = BELKIN_SA_PID;
struct usb_serial_device_type belkin_sa_device = {
name: "Belkin F5U103 USB Serial Adapter",
- idVendor: &belkin_sa_vendor_id, /* the Belkin vendor ID */
- idProduct: &belkin_sa_product_id, /* the Belkin F5U103 product id */
+ id_table: belkin_sa_table, /* the Belkin F5U103 device */
needs_interrupt_in: MUST_HAVE, /* this device must have an interrupt in endpoint */
needs_bulk_in: MUST_HAVE, /* this device must have a bulk in endpoint */
needs_bulk_out: MUST_HAVE, /* this device must have a bulk out endpoint */
/* This driver also supports the "old" school Belkin single port adaptor */
-static __u16 belkin_old_vendor_id = BELKIN_OLD_VID;
-static __u16 belkin_old_product_id = BELKIN_OLD_PID;
struct usb_serial_device_type belkin_old_device = {
name: "Belkin USB Serial Adapter",
- idVendor: &belkin_old_vendor_id, /* the Belkin vendor ID */
- idProduct: &belkin_old_product_id, /* the Belkin product id */
+ id_table: belkin_old_table, /* the old Belkin device */
needs_interrupt_in: MUST_HAVE, /* this device must have an interrupt in endpoint */
needs_bulk_in: MUST_HAVE, /* this device must have a bulk in endpoint */
needs_bulk_out: MUST_HAVE, /* this device must have a bulk out endpoint */
};
/* this driver also works for the Peracom single port adapter */
-static __u16 peracom_vendor_id = PERACOM_VID;
-static __u16 peracom_product_id = PERACOM_PID;
struct usb_serial_device_type peracom_device = {
name: "Peracom single port USB Serial Adapter",
- idVendor: &peracom_vendor_id, /* the Peracom vendor ID */
- idProduct: &peracom_product_id, /* the Peracom product id */
+ id_table: peracom_table, /* the Peracom device */
needs_interrupt_in: MUST_HAVE, /* this device must have an interrupt in endpoint */
needs_bulk_in: MUST_HAVE, /* this device must have a bulk in endpoint */
needs_bulk_out: MUST_HAVE, /* this device must have a bulk out endpoint */
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
struct belkin_sa_private *priv = (struct belkin_sa_private *)port->private;
struct usb_serial *serial;
- struct tty_struct *tty;
unsigned char *data = urb->transfer_buffer;
/* the urb might have been killed. */
unsigned int cflag = port->tty->termios->c_cflag;
unsigned int old_iflag = old_termios->c_iflag;
unsigned int old_cflag = old_termios->c_cflag;
- __u16 urb_value; /* Will hold the new flags */
+ __u16 urb_value = 0; /* Will hold the new flags */
/* Set the baud rate */
if( (cflag&CBAUD) != (old_cflag&CBAUD) ) {
*
* See Documentation/usb/usb-serial.txt for more information on using this driver
*
+ * (11/13/2000) Bill Ryder
+ * Added spinlock protected open code and close code.
+ * Multiple opens work (sort of - see webpage).
+ * Cleaned up comments. Removed multiple PID/VID definitions.
+ * Factorised cts/dtr code
+ * Made use of __FUNCTION__ in dbg's
+ *
* (11/01/2000) Adam J. Richter
* usb_device_id table support
*
#include "ftdi_sio.h"
-#define FTDI_VENDOR_ID 0x0403
-#define FTDI_SIO_SERIAL_CONVERTER_ID 0x8372
+#define FTDI_VENDOR_ID FTDI_VID
+#define FTDI_SIO_SERIAL_CONVERTER_ID FTDI_SIO_PID
+#define FTDI_8U232AM_PID 0x6001
-static __devinitdata struct usb_device_id id_table [] = {
- { idVendor: FTDI_VENDOR_ID, idProduct: FTDI_SIO_SERIAL_CONVERTER_ID },
+static __devinitdata struct usb_device_id id_table_sio [] = {
+ { idVendor: FTDI_VID, idProduct: FTDI_SIO_PID },
{ } /* Terminating entry */
};
-MODULE_DEVICE_TABLE (usb, id_table);
+MODULE_DEVICE_TABLE (usb, id_table_sio);
/* function prototypes for a FTDI serial converter */
/* All of the device info needed for the FTDI SIO serial converter */
struct usb_serial_device_type ftdi_sio_device = {
name: "FTDI SIO",
- id_table: id_table,
+ id_table: id_table_sio,
needs_interrupt_in: MUST_HAVE_NOT, /* this device must not have an interrupt in endpoint */
needs_bulk_in: MUST_HAVE, /* this device must have a bulk in endpoint */
needs_bulk_out: MUST_HAVE, /* this device must have a bulk out endpoint */
startup: ftdi_sio_startup,
};
-
/*
* ***************************************************************************
* FTDI SIO Serial Converter specific driver functions
* ***************************************************************************
*
- * Bill Ryder bryder@sgi.com of Silicon Graphics, Inc. did the FTDI_SIO code
- * Thanx to FTDI for so kindly providing details of the protocol required
- * to talk to the device - http://www.ftdi.co.uk
- *
- * Tested as at this version - other stuff might work
- * 23 March 2000
- * Works:
- * Baudrates - 9600, 38400,19200, 57600, 115200
- * TIOCMBIC - TIOCM_DTR / TIOCM_RTS
- * TIOCMBIS - TIOCM_DTR / TIOCM_RTS
- * TIOCMSET - DTR on/RTSon / DTR off, RTS off
- * no parity:CS8 even parity:CS7 odd parity:CS7
- * CRTSCTS flow control
- *
- * Pilot-xfer zillions of times
- *
- * cu works with dir option
- *
- * Not Tested (ie might not work):
- * xon/xoff flow control
- * ppp (modem handling in general)
- *
- * KNOWN BUGS:
- * Multiple Opens
- * ==============
- * Seems to have problem when opening an already open port,
- * Get I/O error on first attempt, then it lets you in.
- * Need to do proper usage counting - keep registered callbacks for first opener.
- *
- * Reproduce with:
- * cu -l /dev/ttyUSB0 dir
- * whilst cu is running do:
- * stty -a < /dev/ttyUSB0
- *
- * from stty get: 'bash: /dev/ttyUSB0: Invalid argument '
- * from cu get
- * write: Invalid argument
- *
- * Initialisation Problem
- * ======================
- * Pilot transfer required me to run the serial_loopback program before it would work.
- * Still working on this. See the webpage http://reality.sgi.com/bryder_wellington/ftdi_sio
+ * See the webpage http://reality.sgi.com/bryder_wellington/ftdi_sio for upto date
+ * testing information
+ *
*
*/
#define WDR_TIMEOUT (HZ * 5 ) /* default urb timeout */
+/* utility functions to set and unset dtr and rts */
+#define HIGH 1
+#define LOW 0
+static int set_rts(struct usb_device *dev,
+ unsigned int pipe,
+ int high_or_low)
+{
+ static char buf[1];
+ unsigned ftdi_high_or_low = (high_or_low? FTDI_SIO_SET_RTS_HIGH :
+ FTDI_SIO_SET_RTS_LOW);
+ return(usb_control_msg(dev, pipe,
+ FTDI_SIO_SET_MODEM_CTRL_REQUEST,
+ FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
+ ftdi_high_or_low, 0,
+ buf, 0, WDR_TIMEOUT));
+}
+static int set_dtr(struct usb_device *dev,
+ unsigned int pipe,
+ int high_or_low)
+{
+ static char buf[1];
+ unsigned ftdi_high_or_low = (high_or_low? FTDI_SIO_SET_DTR_HIGH :
+ FTDI_SIO_SET_DTR_LOW);
+ return(usb_control_msg(dev, pipe,
+ FTDI_SIO_SET_MODEM_CTRL_REQUEST,
+ FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
+ ftdi_high_or_low, 0,
+ buf, 0, WDR_TIMEOUT));
+}
+
+
/* do some startup allocations not currently performed by usb_serial_probe() */
static int ftdi_sio_startup (struct usb_serial *serial)
{
init_waitqueue_head(&serial->port[0].write_wait);
+
return (0);
}
{ /* ftdi_sio_open */
struct termios tmp_termios;
struct usb_serial *serial = port->serial;
+ unsigned long flags; /* Used for spinlock */
int result;
char buf[1]; /* Needed for the usb_control_msg I think */
- dbg("ftdi_sio_open port %d", port->number);
+ dbg(__FUNCTION__ " port %d", port->number);
- /* FIXME - multiple concurrent opens cause trouble */
- if (port->active) {
- err ("port already open");
- return -EINVAL;
- }
- port->active = 1; /* FIXME - For multiple open this should increment */
+ spin_lock_irqsave (&port->port_lock, flags);
+
+ MOD_INC_USE_COUNT;
+ ++port->open_count;
- /* See ftdi_sio.h for description of what is reset */
- usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE,
- FTDI_SIO_RESET_SIO,
- 0, buf, 0, WDR_TIMEOUT);
+ if (!port->active){
+ port->active = 1;
+ spin_unlock_irqrestore (&port->port_lock, flags);
- /* Setup termios */
- port->tty->termios->c_cflag =
- B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ /* See ftdi_sio.h for description of what is reset */
+ usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE,
+ FTDI_SIO_RESET_SIO,
+ 0, buf, 0, WDR_TIMEOUT);
-
- ftdi_sio_set_termios(port, &tmp_termios);
+ /* Setup termios */
+ port->tty->termios->c_cflag =
+ B9600 | CS8 | CREAD | HUPCL | CLOCAL;
- /* Disable flow control */
- if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- FTDI_SIO_SET_FLOW_CTRL_REQUEST,
- FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
- 0, 0,
- buf, 0, WDR_TIMEOUT) < 0) {
- err("error from flowcontrol urb");
- return(-EINVAL);
- }
-
- /* Turn on RTS and DTR since we are not flow controlling*/
- if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- FTDI_SIO_SET_MODEM_CTRL_REQUEST,
- FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
- (unsigned)FTDI_SIO_SET_DTR_HIGH, 0,
- buf, 0, WDR_TIMEOUT) < 0) {
- err("Error from DTR HIGH urb");
- }
- if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- FTDI_SIO_SET_MODEM_CTRL_REQUEST,
- FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
- (unsigned)FTDI_SIO_SET_RTS_HIGH, 0,
- buf, 0, WDR_TIMEOUT) < 0) {
- err("Error from RTS HIGH urb");
- }
+ /* ftdi_sio_set_termios will send usb control messages */
+ /* ftdi_sio_set_termios will set up port according to above list */
+
+ ftdi_sio_set_termios(port, &tmp_termios);
+
+ /* Turn on RTS and DTR since we are not flow controlling*/
+ if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0),HIGH) < 0) {
+ err("Error from DTR HIGH urb");
+ }
+ if (set_rts(serial->dev, usb_sndctrlpipe(serial->dev, 0),HIGH) < 0){
+ err("Error from RTS HIGH urb");
+ }
- /* Start reading from the device */
- FILL_BULK_URB(port->read_urb, serial->dev,
- usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
- port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
- ftdi_sio_read_bulk_callback, port);
- result = usb_submit_urb(port->read_urb);
- if (result)
- err(__FUNCTION__ " - failed submitting read urb, error %d", result);
+ /* Start reading from the device */
+ FILL_BULK_URB(port->read_urb, serial->dev,
+ usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
+ port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
+ ftdi_sio_read_bulk_callback, port);
+ result = usb_submit_urb(port->read_urb);
+ if (result)
+ err(__FUNCTION__ " - failed submitting read urb, error %d", result);
+ } else { /* the port was already active - so no initialisation was done */
+ spin_unlock_irqrestore (&port->port_lock, flags);
+ }
return (0);
} /* ftdi_sio_open */
struct usb_serial *serial = port->serial;
unsigned int c_cflag = port->tty->termios->c_cflag;
char buf[1];
+ unsigned long flags;
+
+ dbg( __FUNCTION__ " port %d", port->number);
+
+ spin_lock_irqsave (&port->port_lock, flags);
+ --port->open_count;
+
+ if (port->open_count <= 0) {
+ spin_unlock_irqrestore (&port->port_lock, flags);
+ if (c_cflag & HUPCL){
+ /* Disable flow control */
+ if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ FTDI_SIO_SET_FLOW_CTRL_REQUEST,
+ FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
+ 0, 0,
+ buf, 0, WDR_TIMEOUT) < 0) {
+ err("error from flowcontrol urb");
+ }
+
+ /* drop DTR */
+ if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0), LOW) < 0){
+ err("Error from DTR LOW urb");
+ }
+ /* drop RTS */
+ if (set_rts(serial->dev, usb_sndctrlpipe(serial->dev, 0),LOW) < 0) {
+ err("Error from RTS LOW urb");
+ }
+ } /* Note change no line is hupcl is off */
+
+ /* shutdown our bulk reads and writes */
+ usb_unlink_urb (port->write_urb);
+ usb_unlink_urb (port->read_urb);
+ port->active = 0;
+ port->open_count = 0;
+ } else {
+ spin_unlock_irqrestore (&port->port_lock, flags);
+ }
+
+ MOD_DEC_USE_COUNT;
- dbg("ftdi_sio_close port %d", port->number);
-
- if (c_cflag & HUPCL){
- /* Disable flow control */
- if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- FTDI_SIO_SET_FLOW_CTRL_REQUEST,
- FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
- 0, 0,
- buf, 0, WDR_TIMEOUT) < 0) {
- err("error from flowcontrol urb");
- }
- /* drop DTR */
- if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- FTDI_SIO_SET_MODEM_CTRL_REQUEST,
- FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
- (unsigned)FTDI_SIO_SET_DTR_LOW, 0,
- buf, 0, WDR_TIMEOUT) < 0) {
- err("Error from DTR LOW urb");
- }
- /* drop RTS */
- if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- FTDI_SIO_SET_MODEM_CTRL_REQUEST,
- FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
- (unsigned)FTDI_SIO_SET_RTS_LOW, 0,
- buf, 0, WDR_TIMEOUT) < 0) {
- err("Error from RTS LOW urb");
- }
- }
- /* shutdown our bulk reads and writes */
- usb_unlink_urb (port->write_urb);
- usb_unlink_urb (port->read_urb);
- port->active = 0;
} /* ftdi_sio_close */
int result;
DECLARE_WAITQUEUE(wait, current);
- dbg("ftdi_sio_serial_write port %d, %d bytes", port->number, count);
+ dbg(__FUNCTION__ " port %d, %d bytes", port->number, count);
if (count == 0) {
err("write request of 0 bytes");
add_wait_queue(&port->write_wait, &wait);
set_current_state (TASK_INTERRUPTIBLE);
while (port->write_urb->status == -EINPROGRESS) {
- dbg("ftdi_sio - write in progress - retrying");
+ dbg(__FUNCTION__ " write in progress - retrying");
if (0 /* file->f_flags & O_NONBLOCK */) {
rc = -EAGAIN;
goto err;
first_byte = port->write_urb->transfer_buffer;
*first_byte = 1 | ((count-data_offset) << 2) ;
- dbg("Bytes: %d, Control Byte: 0o%03o",count, first_byte[0]);
+ dbg(__FUNCTION__ "Bytes: %d, Control Byte: 0o%03o",count, first_byte[0]);
usb_serial_debug_data (__FILE__, __FUNCTION__, count, first_byte);
/* send the data out the bulk port */
return 0;
}
- dbg("write returning: %d", count - data_offset);
+ dbg(__FUNCTION__ " write returning: %d", count - data_offset);
return (count - data_offset);
}
int i;
int result;
- dbg("ftdi_sio read callback");
+ dbg(__FUNCTION__);
if (port_paranoia_check (port, "ftdi_sio_read_bulk_callback")) {
return;
if (serial_paranoia_check (serial, "ftdi_sio_read_bulk_callback")) {
return;
}
-
- /* TO DO -- check for hung up line and handle appropriately: */
- /* send hangup (need to find out how to do this) */
-
if (urb->status) {
/* This will happen at close every time so it is a dbg not an err */
dbg("Just status");
}
+ /* TO DO -- check for hung up line and handle appropriately: */
+ /* send hangup (need to find out how to do this) */
+ /* See acm.c - you do a tty_hangup - eg tty_hangup(tty) */
+ /* if CD is dropped and the line is not CLOCAL then we should hangup */
+
+
if (urb->actual_length > data_offset) {
for (i = data_offset ; i < urb->actual_length ; ++i) {
tty_insert_flip_char(tty, data[i], 0);
{ /* ftdi_sio_set_termios */
struct usb_serial *serial = port->serial;
unsigned int cflag = port->tty->termios->c_cflag;
- __u16 urb_value; /* Will hold the new flags */
+ __u16 urb_value; /* will hold the new flags */
char buf[1]; /* Perhaps I should dynamically alloc this? */
- dbg("ftdi_sio_set_termios port %d", port->number);
+ dbg(__FUNCTION__ " port %d", port->number);
/* FIXME -For this cut I don't care if the line is really changing or
case B38400: urb_value = ftdi_sio_b38400; dbg("Set to 38400") ; break;
case B57600: urb_value = ftdi_sio_b57600; dbg("Set to 57600") ; break;
case B115200: urb_value = ftdi_sio_b115200; dbg("Set to 115200") ; break;
- default: dbg("FTDI_SIO does not support the baudrate requested");
+ default: dbg(__FUNCTION__ "FTDI_SIO does not support the baudrate requested");
/* FIXME - how to return an error for this? */ break;
}
if ((cflag & CBAUD) == B0 ) {
err("error from disable flowcontrol urb");
}
/* Drop RTS and DTR */
- if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- FTDI_SIO_SET_MODEM_CTRL_REQUEST,
- FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
- (unsigned)FTDI_SIO_SET_DTR_LOW, 0,
- buf, 0, WDR_TIMEOUT) < 0) {
+ if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0),LOW) < 0){
err("Error from DTR LOW urb");
}
- if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- FTDI_SIO_SET_MODEM_CTRL_REQUEST,
- FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
- (unsigned)FTDI_SIO_SET_RTS_LOW, 0,
- buf, 0, WDR_TIMEOUT) < 0) {
+ if (set_rts(serial->dev, usb_sndctrlpipe(serial->dev, 0),LOW) < 0){
err("Error from RTS LOW urb");
}
/* Set flow control */
/* Note device also supports DTR/CD (ugh) and Xon/Xoff in hardware */
if (cflag & CRTSCTS) {
- dbg("Setting to CRTSCTS flow control");
+ dbg(__FUNCTION__ "Setting to CRTSCTS flow control");
if (usb_control_msg(serial->dev,
usb_sndctrlpipe(serial->dev, 0),
FTDI_SIO_SET_FLOW_CTRL_REQUEST,
} else {
/* CHECK Assuming XON/XOFF handled by stack - not by device */
/* Disable flow control */
- dbg("Turning off hardware flow control");
+ dbg(__FUNCTION__ "Turning off hardware flow control");
if (usb_control_msg(serial->dev,
usb_sndctrlpipe(serial->dev, 0),
FTDI_SIO_SET_FLOW_CTRL_REQUEST,
char buf[1];
int ret, mask;
- dbg("ftdi_sio_ioctl - cmd 0x%04x", cmd);
+ dbg(__FUNCTION__ " cmd 0x%04x", cmd);
/* Based on code from acm.c and others */
switch (cmd) {
case TIOCMGET:
- dbg("TIOCMGET");
+ dbg(__FUNCTION__ "TIOCMGET");
/* Request the status from the device */
if ((ret = usb_control_msg(serial->dev,
usb_rcvctrlpipe(serial->dev, 0),
FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
0, 0,
buf, 1, HZ * 5)) < 0 ) {
- dbg("Get not get modem status of device");
+ dbg(__FUNCTION__ "Get not get modem status of device");
return(ret);
}
break;
case TIOCMSET: /* Turns on and off the lines as specified by the mask */
- dbg("TIOCMSET");
+ dbg(__FUNCTION__ "TIOCMSET");
if ((ret = get_user(mask, (unsigned long *) arg))) return ret;
urb_value = ((mask & TIOCM_DTR) ? FTDI_SIO_SET_DTR_HIGH : FTDI_SIO_SET_DTR_LOW);
if ((ret = usb_control_msg(serial->dev,
break;
case TIOCMBIS: /* turns on (Sets) the lines as specified by the mask */
- dbg("TIOCMBIS");
+ dbg(__FUNCTION__ "TIOCMBIS");
if ((ret = get_user(mask, (unsigned long *) arg))) return ret;
if (mask & TIOCM_DTR){
- if ((ret = usb_control_msg(serial->dev,
- usb_sndctrlpipe(serial->dev, 0),
- FTDI_SIO_SET_MODEM_CTRL_REQUEST,
- FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
- FTDI_SIO_SET_DTR_HIGH , 0,
- buf, 0, WDR_TIMEOUT)) < 0){
+ if ((ret = set_dtr(serial->dev,
+ usb_sndctrlpipe(serial->dev, 0),
+ HIGH)) < 0) {
err("Urb to set DTR failed");
return(ret);
- }
}
- if (mask & TIOCM_RTS) {
- if ((ret = usb_control_msg(serial->dev,
- usb_sndctrlpipe(serial->dev, 0),
- FTDI_SIO_SET_MODEM_CTRL_REQUEST,
- FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
- FTDI_SIO_SET_RTS_HIGH , 0,
- buf, 0, WDR_TIMEOUT)) < 0){
+ }
+ if (mask & TIOCM_RTS) {
+ if ((ret = set_rts(serial->dev,
+ usb_sndctrlpipe(serial->dev, 0),
+ HIGH)) < 0){
err("Urb to set RTS failed");
return(ret);
}
break;
case TIOCMBIC: /* turns off (Clears) the lines as specified by the mask */
- dbg("TIOCMBIC");
+ dbg(__FUNCTION__ "TIOCMBIC");
if ((ret = get_user(mask, (unsigned long *) arg))) return ret;
if (mask & TIOCM_DTR){
- if ((ret = usb_control_msg(serial->dev,
- usb_sndctrlpipe(serial->dev, 0),
- FTDI_SIO_SET_MODEM_CTRL_REQUEST,
- FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
- FTDI_SIO_SET_DTR_LOW , 0,
- buf, 0, WDR_TIMEOUT)) < 0){
+ if ((ret = set_dtr(serial->dev,
+ usb_sndctrlpipe(serial->dev, 0),
+ LOW)) < 0){
err("Urb to unset DTR failed");
return(ret);
}
}
if (mask & TIOCM_RTS) {
- if ((ret = usb_control_msg(serial->dev,
- usb_sndctrlpipe(serial->dev, 0),
- FTDI_SIO_SET_MODEM_CTRL_REQUEST,
- FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
- FTDI_SIO_SET_RTS_LOW , 0,
- buf, 0, WDR_TIMEOUT)) < 0){
+ if ((ret = set_rts(serial->dev,
+ usb_sndctrlpipe(serial->dev, 0),
+ LOW)) < 0){
err("Urb to unset RTS failed");
return(ret);
}
/* This is not an error - turns out the higher layers will do
* some ioctls itself (see comment above)
*/
- dbg("ftdi_sio ioctl arg not supported - it was 0x%04x",cmd);
+ dbg(__FUNCTION__ "arg not supported - it was 0x%04x",cmd);
return(-ENOIOCTLCMD);
break;
}
- dbg("ftdi_sio_ioctl returning 0");
+ dbg(__FUNCTION__ " returning 0");
return 0;
} /* ftdi_sio_ioctl */
break;
}
}
+ /* if driver not bound, leave defaults unchanged */
+ if (private == NULL)
+ interface->act_altsetting = 0;
}
else /* "old style" driver */
private = driver->probe(dev, ifnum, NULL);
}
-#if defined(CONFIG_KMOD)
+#ifdef CONFIG_HOTPLUG
/*
* USB hotplugging invokes what /proc/sys/kernel/hotplug says
* (normally /sbin/hotplug) when USB devices get added or removed.
- */
-
-static int to_bcd (char *buf, __u16 *bcdValue)
-{
- int retval = 0;
- char *value = (char *) bcdValue;
- int temp;
-
- /* digits are 0-9 then ":;<=>?" for devices using
- * non-bcd (non-standard!) values here ... */
-
- /* No leading (or later, trailing) zeroes since scripts do
- * literal matches, and that's how they're doing them. */
- if ((temp = value [1] & 0xf0) != 0) {
- temp >>= 4;
- temp += '0';
- *buf++ = (char) temp;
- retval++;
- }
-
- temp = value [1] & 0x0f;
- temp += '0';
- *buf++ = (char) temp;
- retval++;
-
- *buf++ = '.';
- retval++;
-
- temp = value [0] & 0xf0;
- temp >>= 4;
- temp += '0';
- *buf++ = (char) temp;
- retval++;
-
- if ((temp = value [0] & 0x0f) != 0) {
- temp += '0';
- *buf++ = (char) temp;
- retval++;
- }
- *buf++ = 0;
-
- return retval;
-}
-
-/*
+ *
* This invokes a user mode policy agent, typically helping to load driver
- * or other modules, configure the device, or both.
+ * or other modules, configure the device, and more. Drivers can provide
+ * a MODULE_DEVICE_TABLE to help with module loading subtasks.
*
* Some synchronization is important: removes can't start processing
* before the add-device processing completes, and vice versa. That keeps
* all the device descriptors we don't tell them about. Or
* even act as usermode drivers.
*
- * XXX how little intelligence can we hardwire?
- * (a) mount point: /devfs, /dev, /proc/bus/usb etc.
- * (b) naming convention: bus1/device3, 001/003 etc.
+ * FIXME reduce hardwired intelligence here
*/
envp [i++] = "DEVFS=/proc/bus/usb";
envp [i++] = scratch;
dev->bus->busnum, dev->devnum) + 1;
#endif
- /* per-device configuration hacks are often necessary */
+ /* per-device configuration hacks are common */
envp [i++] = scratch;
- scratch += sprintf (scratch, "PRODUCT=%x/%x/",
+ scratch += sprintf (scratch, "PRODUCT=%x/%x/%x",
dev->descriptor.idVendor,
- dev->descriptor.idProduct);
- scratch += to_bcd (scratch, &dev->descriptor.bcdDevice) + 1;
+ dev->descriptor.idProduct,
+ dev->descriptor.bcdDevice) + 1;
- /* otherwise, use a simple (so far) generic driver binding model */
+ /* class-based driver binding models */
envp [i++] = scratch;
scratch += sprintf (scratch, "TYPE=%d/%d/%d",
dev->descriptor.bDeviceClass,
if (dev->descriptor.bDeviceClass == 0) {
int alt = dev->actconfig->interface [0].act_altsetting;
- /* simple/common case: one config, one interface, one driver
- * unsimple cases: everything else
+ /* a simple/common case: one config, one interface, one driver
+ * with current altsetting being a reasonable setting.
+ * everything needs a smart agent and usbdevfs; or can rely on
+ * device-specific binding policies.
*/
+ envp [i++] = scratch;
scratch += sprintf (scratch, "INTERFACE=%d/%d/%d",
dev->actconfig->interface [0].altsetting [alt].bInterfaceClass,
dev->actconfig->interface [0].altsetting [alt].bInterfaceSubClass,
dev->actconfig->interface [0].altsetting [alt].bInterfaceProtocol)
+ 1;
/* INTERFACE-0, INTERFACE-1, ... ? */
- } else {
- /* simple/common case: generic device, handled generically */
}
envp [i++] = 0;
/* assert: (scratch - buf) < sizeof buf */
/*
- * linux/drivers/video/fbdev.c - nVidia RIVA 128/TNT/TNT2 fb driver
+ * linux/drivers/video/riva/fbdev.c - nVidia RIVA 128/TNT/TNT2 fb driver
*
* Maintained by Ani Joshi <ajoshi@shell.unixbox.com>
*
/* Recently Update by v1.09.50 */
+#include <linux/config.h>
#include "sis_300.h"
#if defined(ALLOC_PRAGMA)
return(data);
}
-#endif /* CONFIG_FB_SIS_LINUXBIOS */
\ No newline at end of file
+#endif /* CONFIG_FB_SIS_LINUXBIOS */
+#include <linux/config.h>
#include "initdef.h"
USHORT DRAMType[17][5]={{0x0C,0x0A,0x02,0x40,0x39},{0x0D,0x0A,0x01,0x40,0x48},
extern u8 GetReg1(u16 port, u16 index);
extern u8 GetReg2(u16 port);
extern u32 GetReg3(u16 port);
-extern void ClearDAC(u16 port);
\ No newline at end of file
+extern void ClearDAC(u16 port);
/* Recently Update by v1.09.50 */
+#include <linux/config.h>
#include "sis_301.h"
#ifndef CONFIG_FB_SIS_LINUXBIOS
return 1;
}
-#endif
\ No newline at end of file
+#endif
+#include <linux/config.h>
#include "initdef.h"
USHORT SetFlag,RVBHCFACT,RVBHCMAX,VGAVT,VGAHT,VT,HT,VGAVDE,VGAHDE;
#define EXPORT_SYMTAB
#undef SISFBDEBUG
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
- * $Id: intrep.c,v 1.69 2000/08/24 09:35:47 dwmw2 Exp $
+ * - Based on Id: intrep.c,v 1.71 2000/10/27 16:51:29 dwmw2 Exp
+ * - With the ctype() changes from v1.77.
*
* Ported to Linux 2.3.x and MTD:
* Copyright (C) 2000 Alexander Larsson (alex@cendio.se), Cendio Systems AB
#include <linux/version.h>
#include <linux/smp_lock.h>
#include <linux/sched.h>
-
+#include <linux/ctype.h>
#include "intrep.h"
#include "jffs_fm.h"
-#if LINUX_VERSION_CODE < 0x20300
-#define set_current_state(x) do{current->state = x;} while (0)
-#endif
-
#if defined(JFFS_MEMORY_DEBUG) && JFFS_MEMORY_DEBUG
long no_jffs_file = 0;
long no_jffs_node = 0;
static __u8 flash_read_u8(struct mtd_info *mtd, loff_t from);
#if 1
-#define _U 01
-#define _L 02
-#define _N 04
-#define _S 010
-#define _P 020
-#define _C 040
-#define _X 0100
-#define _B 0200
-
-const unsigned char jffs_ctype_[1 + 256] = {
- 0,
- _C, _C, _C, _C, _C, _C, _C, _C,
- _C, _C|_S, _C|_S, _C|_S, _C|_S, _C|_S, _C, _C,
- _C, _C, _C, _C, _C, _C, _C, _C,
- _C, _C, _C, _C, _C, _C, _C, _C,
- _S|_B, _P, _P, _P, _P, _P, _P, _P,
- _P, _P, _P, _P, _P, _P, _P, _P,
- _N, _N, _N, _N, _N, _N, _N, _N,
- _N, _N, _P, _P, _P, _P, _P, _P,
- _P, _U|_X, _U|_X, _U|_X, _U|_X, _U|_X, _U|_X, _U,
- _U, _U, _U, _U, _U, _U, _U, _U,
- _U, _U, _U, _U, _U, _U, _U, _U,
- _U, _U, _U, _P, _P, _P, _P, _P,
- _P, _L|_X, _L|_X, _L|_X, _L|_X, _L|_X, _L|_X, _L,
- _L, _L, _L, _L, _L, _L, _L, _L,
- _L, _L, _L, _L, _L, _L, _L, _L,
- _L, _L, _L, _P, _P, _P, _P, _C
-};
-
-#define jffs_isalpha(c) ((jffs_ctype_+1)[(int)c]&(_U|_L))
-#define jffs_isupper(c) ((jffs_ctype_+1)[(int)c]&_U)
-#define jffs_islower(c) ((jffs_ctype_+1)[(int)c]&_L)
-#define jffs_isdigit(c) ((jffs_ctype_+1)[(int)c]&_N)
-#define jffs_isxdigit(c) ((jffs_ctype_+1)[(int)c]&(_X|_N))
-#define jffs_isspace(c) ((jffs_ctype_+1)[(int)c]&_S)
-#define jffs_ispunct(c) ((jffs_ctype_+1)[(int)c]&_P)
-#define jffs_isalnum(c) ((jffs_ctype_+1)[(int)c]&(_U|_L|_N))
-#define jffs_isprint(c) ((jffs_ctype_+1)[(int)c]&(_P|_U|_L|_N|_B))
-#define jffs_isgraph(c) ((jffs_ctype_+1)[(int)c]&(_P|_U|_L|_N))
-#define jffs_iscntrl(c) ((jffs_ctype_+1)[(int)c]&_C)
-
-void
+static void
jffs_hexdump(struct mtd_info *mtd, loff_t pos, int size)
{
char line[16];
printk(" ");
for (i = 0; i < j; i++) {
- if (jffs_isgraph(line[i])) {
+ if (isgraph(line[i])) {
printk("%c", line[i]);
}
else {
size_t retlen;
int res;
+ D3(printk(KERN_NOTICE "flash_safe_read(%p, %08x, %p, %08x)\n",
+ mtd, from, buf, count));
+
res = MTD_READ(mtd, from, count, &retlen, buf);
if (retlen != count) {
- printk("Didn't read all bytes in flash_safe_read(). Returned %d\n", res);
+ panic("Didn't read all bytes in flash_safe_read(). Returned %d\n", res);
}
return res?res:retlen;
}
{
__u32 sum = 0;
loff_t ptr = start;
- while (size-- > 0) {
- sum += flash_read_u8(mtd, ptr++);
+ __u8 *read_buf;
+ int i, length;
+
+ /* Allocate read buffer */
+ read_buf = (__u8 *) kmalloc (sizeof(__u8) * 4096, GFP_KERNEL);
+
+ /* Loop until checksum done */
+ while (size) {
+ /* Get amount of data to read */
+ if (size < 4096)
+ length = size;
+ else
+ length = 4096;
+
+ /* Perform flash read */
+ D3(printk(KERN_NOTICE "jffs_checksum_flash\n"));
+ flash_safe_read(mtd, ptr, &read_buf[0], length);
+
+ /* Compute checksum */
+ for (i=0; i < length ; i++)
+ sum += read_buf[i];
+
+ /* Update pointer and size */
+ size -= length;
+ ptr += length;
}
+
+ /* Free read buffer */
+ kfree (read_buf);
+
+ /* Return result */
D3(printk("checksum result: 0x%08x\n", sum));
return sum;
}
loff_t pos = fmc->flash_start;
loff_t start;
loff_t end = fmc->flash_start + fmc->flash_size;
+ __u8 *read_buf;
+ int i, len, retlen;
D1(printk("jffs_scan_flash(): start pos = 0x%lx, end = 0x%lx\n",
(long)pos, (long)end));
flash_safe_acquire(fmc->mtd);
+ /* Allocate read buffer */
+ read_buf = (__u8 *) kmalloc (sizeof(__u8) * 4096, GFP_KERNEL);
+
/* Start the scan. */
while (pos < end) {
deleted_file = 0;
something else than 0xff is found. */
D1(printk("jffs_scan_flash(): 0xff at pos 0x%lx.\n",
(long)pos));
- for (; pos < end
- && JFFS_EMPTY_BITMASK == flash_read_u32(fmc->mtd, pos);
- pos += 4);
+
+ len = end - pos < 4096 ? end - pos : 4096;
+
+ retlen = flash_safe_read(fmc->mtd, pos,
+ &read_buf[0], len);
+
+ retlen &= ~3;
+
+ for (i=0 ; i < retlen ; i+=4, pos += 4) {
+ if(*((__u32 *) &read_buf[i]) !=
+ JFFS_EMPTY_BITMASK)
+ break;
+ }
+ if (i == retlen)
+ continue;
+
D1(printk("jffs_scan_flash(): 0xff ended at "
"pos 0x%lx.\n", (long)pos));
if (!(node = (struct jffs_node *)
kmalloc(sizeof(struct jffs_node),
GFP_KERNEL))) {
+ /* Free read buffer */
+ kfree (read_buf);
+
+ /* Release the flash device */
flash_safe_release(fmc->mtd);
+
return -ENOMEM;
}
DJM(no_jffs_node++);
D(printk("jffs_scan_flash(): !node->fm\n"));
kfree(node);
DJM(no_jffs_node--);
+
+ /* Free read buffer */
+ kfree (read_buf);
+
+ /* Release the flash device */
flash_safe_release(fmc->mtd);
+
return -ENOMEM;
}
if ((err = jffs_insert_node(c, 0, &raw_inode,
D(printk("jffs_scan_flash: !dl\n"));
kfree(node);
DJM(no_jffs_node--);
+
+ /* Release the flash device */
flash_safe_release(fmc->flash_part);
+
+ /* Free read buffer */
+ kfree (read_buf);
+
return -ENOMEM;
}
dl->ino = deleted_file;
DJM(no_jffs_node--);
}
jffs_build_end(fmc);
+
+ /* Free read buffer */
+ kfree (read_buf);
+
+ /* Return happy */
D3(printk("jffs_scan_flash(): Leaving...\n"));
flash_safe_release(fmc->mtd);
return 0;
f->name, node->ino, node->version, node_offset));
r = jffs_min(avail, max_size);
+ D3(printk(KERN_NOTICE "jffs_get_node_data\n"));
flash_safe_read(fmc->mtd, pos, buf, r);
D3(printk(" jffs_get_node_data(): Read %u byte%s.\n",
case NCP_IOC_CONN_LOGGED_IN:
- if ((permission(inode, MAY_WRITE) != 0)
- && (current->uid != server->m.mounted_uid)) {
+ if (!capable(CAP_SYS_ADMIN))
return -EACCES;
- }
if (!(server->m.int_flags & NCP_IMOUNT_LOGGEDIN_POSSIBLE))
return -EINVAL;
if (server->root_setuped)
struct nw_info_struct i;
struct dentry* dentry;
- if ( (permission(inode, MAY_WRITE) != 0)
- && (current->uid != server->m.mounted_uid))
+ if (!capable(CAP_SYS_ADMIN))
{
return -EACCES;
}
* Thanks Petr Vandrovec for idea and many hints.
*/
case NCP_IOC_SETCHARSETS:
- if ((permission(inode, MAY_WRITE) != 0) &&
- (current->uid != server->m.mounted_uid))
+ if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (server->root_setuped)
return -EBUSY;
/* this is wrong! */
vname_cc = kmalloc(vlen, GFP_KERNEL);
+ if (!vname_cc)
+ return -ENOMEM;
for (i = 0; i < vlen; i++)
vname_cc[i] = ncp_tolower(in, vname[i]);
vname = vname_cc;
task->start_time,
vsize,
mm ? mm->rss : 0, /* you might want to shift this left 3 */
- task->rlim ? task->rlim[RLIMIT_RSS].rlim_cur : 0,
+ task->rlim[RLIMIT_RSS].rlim_cur,
mm ? mm->start_code : 0,
mm ? mm->end_code : 0,
mm ? mm->start_stack : 0,
#include <asm/current.h>
#include <asm/system.h>
#include <asm/atomic.h>
+#include <asm/compiler.h> /* __builtin_expect */
#define DEBUG_SEMAPHORE 0
#define DEBUG_RW_SEMAPHORE 0
#endif
}
-/*
- * B step AMD K6 before B 9730xxxx have hardware bugs that can cause
- * misexecution of code under Linux. Owners of such processors should
- * contact AMD for precise details and a CPU swap.
- *
- * See http://www.mygale.com/~poulot/k6bug.html
- * http://www.amd.com/K6/k6docs/revgd.html
- *
- * The following test is erm.. interesting. AMD neglected to up
- * the chip setting when fixing the bug but they also tweaked some
- * performance at the same time..
- */
-
-extern void vide(void);
-__asm__(".align 4\nvide: ret");
-
-static void __init check_amd_k6(void)
-{
- if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD &&
- boot_cpu_data.x86 == 5 &&
- boot_cpu_data.x86_model == 6 &&
- boot_cpu_data.x86_mask == 1)
- {
- int n;
- void (*f_vide)(void);
- unsigned long d, d2;
-
- printk(KERN_INFO "AMD K6 stepping B detected - ");
-
-#define K6_BUG_LOOP 1000000
-
- /*
- * It looks like AMD fixed the 2.6.2 bug and improved indirect
- * calls at the same time.
- */
-
- n = K6_BUG_LOOP;
- f_vide = vide;
- rdtscl(d);
- while (n--)
- f_vide();
- rdtscl(d2);
- d = d2-d;
-
- /* Knock these two lines out if it debugs out ok */
- printk(KERN_INFO "K6 BUG %ld %d (Report these if test report is incorrect)\n", d, 20*K6_BUG_LOOP);
- printk(KERN_INFO "AMD K6 stepping B detected - ");
- /* -- cut here -- */
- if (d > 20*K6_BUG_LOOP)
- printk("system stability may be impaired when more than 32 MB are used.\n");
- else
- printk("probably OK (after B9730xxxx).\n");
- printk(KERN_INFO "Please see http://www.mygale.com/~poulot/k6bug.html\n");
- }
-}
-
-/*
- * All current models of Pentium and Pentium with MMX technology CPUs
- * have the F0 0F bug, which lets nonpriviledged users lock up the system:
- */
-
-#ifndef CONFIG_M686
-extern void trap_init_f00f_bug(void);
-
-static void __init check_pentium_f00f(void)
-{
- /*
- * Pentium and Pentium MMX
- */
- boot_cpu_data.f00f_bug = 0;
- if (boot_cpu_data.x86 == 5 && boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) {
- printk(KERN_INFO "Intel Pentium with F0 0F bug - workaround enabled.\n");
- boot_cpu_data.f00f_bug = 1;
- trap_init_f00f_bug();
- }
-}
-#endif
-
-/*
- * Perform the Cyrix 5/2 test. A Cyrix won't change
- * the flags, while other 486 chips will.
- */
-
-static inline int test_cyrix_52div(void)
-{
- unsigned int test;
-
- __asm__ __volatile__(
- "sahf\n\t" /* clear flags (%eax = 0x0005) */
- "div %b2\n\t" /* divide 5 by 2 */
- "lahf" /* store flags into %ah */
- : "=a" (test)
- : "0" (5), "q" (2)
- : "cc");
-
- /* AH is 0x02 on Cyrix after the divide.. */
- return (unsigned char) (test >> 8) == 0x02;
-}
-
-/*
- * Fix cpuid problems with Cyrix CPU's:
- * -- on the Cx686(L) the cpuid is disabled on power up.
- * -- braindamaged BIOS disable cpuid on the Cx686MX.
- */
-
-extern unsigned char Cx86_dir0_msb; /* exported HACK from cyrix_model() */
-
-static void __init check_cx686_cpuid(void)
-{
- if (boot_cpu_data.cpuid_level == -1 &&
- ((Cx86_dir0_msb == 5) || (Cx86_dir0_msb == 3))) {
- int eax, dummy;
- unsigned char ccr3, ccr4;
- __u32 old_cap;
-
- cli();
- ccr3 = getCx86(CX86_CCR3);
- setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */
- ccr4 = getCx86(CX86_CCR4);
- setCx86(CX86_CCR4, ccr4 | 0x80); /* enable cpuid */
- setCx86(CX86_CCR3, ccr3); /* disable MAPEN */
- sti();
-
- /* we have up to level 1 available on the Cx6x86(L|MX) */
- boot_cpu_data.cpuid_level = 1;
- /* Need to preserve some externally computed capabilities */
- old_cap = boot_cpu_data.x86_capability & X86_FEATURE_MTRR;
- cpuid(1, &eax, &dummy, &dummy,
- &boot_cpu_data.x86_capability);
- boot_cpu_data.x86_capability |= old_cap;
-
- boot_cpu_data.x86 = (eax >> 8) & 15;
- /*
- * we already have a cooked step/rev number from DIR1
- * so we don't use the cpuid-provided ones.
- */
- }
-}
-
-/*
- * Reset the slow-loop (SLOP) bit on the 686(L) which is set by some old
- * BIOSes for compatability with DOS games. This makes the udelay loop
- * work correctly, and improves performance.
- */
-
-extern void calibrate_delay(void) __init;
-
-static void __init check_cx686_slop(void)
-{
- if (Cx86_dir0_msb == 3) {
- unsigned char ccr3, ccr5;
-
- cli();
- ccr3 = getCx86(CX86_CCR3);
- setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */
- ccr5 = getCx86(CX86_CCR5);
- if (ccr5 & 2)
- setCx86(CX86_CCR5, ccr5 & 0xfd); /* reset SLOP */
- setCx86(CX86_CCR3, ccr3); /* disable MAPEN */
- sti();
-
- if (ccr5 & 2) { /* possible wrong calibration done */
- printk(KERN_INFO "Recalibrating delay loop with SLOP bit reset\n");
- calibrate_delay();
- boot_cpu_data.loops_per_sec = loops_per_sec;
- }
- }
-}
-
-/*
- * Cyrix CPUs without cpuid or with cpuid not yet enabled can be detected
- * by the fact that they preserve the flags across the division of 5/2.
- * PII and PPro exhibit this behavior too, but they have cpuid available.
- */
-
-static void __init check_cyrix_cpu(void)
-{
- if ((boot_cpu_data.cpuid_level == -1) && (boot_cpu_data.x86 == 4)
- && test_cyrix_52div()) {
-
- strcpy(boot_cpu_data.x86_vendor_id, "CyrixInstead");
- }
-}
-
-/*
- * In setup.c's cyrix_model() we have set the boot_cpu_data.coma_bug
- * on certain processors that we know contain this bug and now we
- * enable the workaround for it.
- */
-
-static void __init check_cyrix_coma(void)
-{
-}
-
/*
* Check whether we are able to run this kernel safely on SMP.
*
*/
#if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_X86_GOOD_APIC)
if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL
- && boot_cpu_data.x86_capability & X86_FEATURE_APIC
+ && test_bit(X86_FEATURE_APIC, &boot_cpu_data.x86_capability)
&& boot_cpu_data.x86 == 5
&& boot_cpu_data.x86_model == 2
&& (boot_cpu_data.x86_mask < 6 || boot_cpu_data.x86_mask == 11))
static void __init check_bugs(void)
{
- check_cyrix_cpu();
identify_cpu(&boot_cpu_data);
- check_cx686_cpuid();
- check_cx686_slop();
#ifndef CONFIG_SMP
printk("CPU: ");
print_cpu_info(&boot_cpu_data);
check_fpu();
check_hlt();
check_popad();
- check_amd_k6();
-#ifndef CONFIG_M686
- check_pentium_f00f();
-#endif
- check_cyrix_coma();
system_utsname.machine[1] = '0' + (boot_cpu_data.x86 > 6 ? 6 : boot_cpu_data.x86);
}
--- /dev/null
+/*
+ * cpufeature.h
+ *
+ * Defines x86 CPU feature bits
+ */
+
+#ifndef __ASM_I386_CPUFEATURE_H
+#define __ASM_I386_CPUFEATURE_H
+
+/* Sample usage: CPU_FEATURE_P(cpu.x86_capability, FPU) */
+#define CPU_FEATURE_P(CAP, FEATURE) test_bit(CAP, X86_FEATURE_##FEATURE ##_BIT)
+
+#define NCAPINTS 4 /* Currently we have 4 32-bit words worth of info */
+
+/* Intel-defined CPU features, CPUID level 0x00000001, word 0 */
+#define X86_FEATURE_FPU (0*32+ 0) /* Onboard FPU */
+#define X86_FEATURE_VME (0*32+ 1) /* Virtual Mode Extensions */
+#define X86_FEATURE_DE (0*32+ 2) /* Debugging Extensions */
+#define X86_FEATURE_PSE (0*32+ 3) /* Page Size Extensions */
+#define X86_FEATURE_TSC (0*32+ 4) /* Time Stamp Counter */
+#define X86_FEATURE_MSR (0*32+ 5) /* Model-Specific Registers, RDMSR, WRMSR */
+#define X86_FEATURE_PAE (0*32+ 6) /* Physical Address Extensions */
+#define X86_FEATURE_MCE (0*32+ 7) /* Machine Check Architecture */
+#define X86_FEATURE_CX8 (0*32+ 8) /* CMPXCHG8 instruction */
+#define X86_FEATURE_APIC (0*32+ 9) /* Onboard APIC */
+#define X86_FEATURE_SEP (0*32+11) /* SYSENTER/SYSEXIT */
+#define X86_FEATURE_MTRR (0*32+12) /* Memory Type Range Registers */
+#define X86_FEATURE_PGE (0*32+13) /* Page Global Enable */
+#define X86_FEATURE_MCA (0*32+14) /* Machine Check Architecture */
+#define X86_FEATURE_CMOV (0*32+15) /* CMOV instruction (FCMOVCC and FCOMI too if FPU present) */
+#define X86_FEATURE_PAT (0*32+16) /* Page Attribute Table */
+#define X86_FEATURE_PSE36 (0*32+17) /* 36-bit PSEs */
+#define X86_FEATURE_PN (0*32+18) /* Processor serial number */
+#define X86_FEATURE_CLFLSH (0*32+19) /* Supports the CLFLUSH instruction */
+#define X86_FEATURE_DTES (0*32+21) /* Debug Trace Store */
+#define X86_FEATURE_ACPI (0*32+22) /* ACPI via MSR */
+#define X86_FEATURE_MMX (0*32+23) /* Multimedia Extensions */
+#define X86_FEATURE_FXSR (0*32+24) /* FXSAVE and FXRSTOR instructions (fast save and restore */
+ /* of FPU context), and CR4.OSFXSR available */
+#define X86_FEATURE_XMM (0*32+25) /* Streaming SIMD Extensions */
+#define X86_FEATURE_XMM2 (0*32+26) /* Streaming SIMD Extensions-2 */
+#define X86_FEATURE_SELFSNOOP (0*32+27) /* CPU self snoop */
+#define X86_FEATURE_ACC (0*32+29) /* Automatic clock control */
+#define X86_FEATURE_IA64 (0*32+30) /* IA-64 processor */
+
+/* AMD-defined CPU features, CPUID level 0x80000001, word 1 */
+/* Don't duplicate feature flags which are redundant with Intel! */
+#define X86_FEATURE_SYSCALL (1*32+11) /* SYSCALL/SYSRET */
+#define X86_FEATURE_MMXEXT (1*32+22) /* AMD MMX extensions */
+#define X86_FEATURE_LM (1*32+29) /* Long Mode (x86-64) */
+#define X86_FEATURE_3DNOWEXT (1*32+30) /* AMD 3DNow! extensions */
+#define X86_FEATURE_3DNOW (1*32+31) /* 3DNow! */
+
+/* Transmeta-defined CPU features, CPUID level 0x80860001, word 2 */
+#define X86_FEATURE_RECOVERY (2*32+ 0) /* CPU in recovery mode */
+#define X86_FEATURE_LONGRUN (2*32+ 1) /* Longrun power control */
+#define X86_FEATURE_LRTI (2*32+ 3) /* LongRun table interface */
+
+/* Other features, Linux-defined mapping, word 3 */
+/* This range is used for feature bits which conflict or are synthesized */
+#define X86_FEATURE_CXMMX (3*32+ 0) /* Cyrix MMX extensions */
+#define X86_FEATURE_K6_MTRR (3*32+ 1) /* AMD K6 nonstandard MTRRs */
+#define X86_FEATURE_CYRIX_ARR (3*32+ 2) /* Cyrix ARRs (= MTRRs) */
+#define X86_FEATURE_CENTAUR_MCR (3*32+ 3) /* Centaur MCRs (= MTRRs) */
+
+#endif /* __ASM_I386_CPUFEATURE_H */
+
+/*
+ * Local Variables:
+ * mode:c
+ * comment-column:42
+ * End:
+ */
#include <asm/ptrace.h>
#include <asm/user.h>
+#include <linux/utsname.h>
+
typedef unsigned long elf_greg_t;
#define ELF_NGREG (sizeof (struct user_regs_struct) / sizeof(elf_greg_t))
instruction set this CPU supports. This could be done in user space,
but it's not easy, and we've already done it here. */
-#define ELF_HWCAP (boot_cpu_data.x86_capability)
+#define ELF_HWCAP (boot_cpu_data.x86_capability[0])
/* This yields a string that ld.so will use to load implementation
specific libraries for optimization. This is more specific in
For the moment, we have only optimizations for the Intel generations,
but that could change... */
-#define ELF_PLATFORM ("i386\0i486\0i586\0i686"+(((boot_cpu_data.x86>6?6:boot_cpu_data.x86)-3)*5))
+#define ELF_PLATFORM (system_utsname.machine)
#ifdef __KERNEL__
#define SET_PERSONALITY(ex, ibcs2) set_personality((ibcs2)?PER_SVR4:PER_LINUX)
#include <asm/page.h>
#include <asm/types.h>
#include <asm/sigcontext.h>
+#include <asm/cpufeature.h>
#include <linux/config.h>
#include <linux/threads.h>
char hlt_works_ok; /* Problems on some 486Dx4's and old 386's */
char hard_math;
char rfu;
- int cpuid_level; /* Maximum supported CPUID level, -1=no CPUID */
- __u32 x86_capability;
+ int cpuid_level; /* Maximum supported CPUID level, -1=no CPUID */
+ __u32 x86_capability[NCAPINTS];
char x86_vendor_id[16];
char x86_model_id[64];
int x86_cache_size; /* in KB - valid for CPUS which support this
* capabilities of CPUs
*/
-#define X86_FEATURE_FPU 0x00000001 /* onboard FPU */
-#define X86_FEATURE_VME 0x00000002 /* Virtual Mode Extensions */
-#define X86_FEATURE_DE 0x00000004 /* Debugging Extensions */
-#define X86_FEATURE_PSE 0x00000008 /* Page Size Extensions */
-#define X86_FEATURE_TSC 0x00000010 /* Time Stamp Counter */
-#define X86_FEATURE_MSR 0x00000020 /* Model-Specific Registers, RDMSR, WRMSR */
-#define X86_FEATURE_PAE 0x00000040 /* Physical Address Extensions */
-#define X86_FEATURE_MCE 0x00000080 /* Machine Check Exceptions */
-#define X86_FEATURE_CX8 0x00000100 /* CMPXCHG8 instruction */
-#define X86_FEATURE_APIC 0x00000200 /* onboard APIC */
-#define X86_FEATURE_10 0x00000400
-#define X86_FEATURE_SEP 0x00000800 /* Fast System Call */
-#define X86_FEATURE_MTRR 0x00001000 /* Memory Type Range Registers */
-#define X86_FEATURE_PGE 0x00002000 /* Page Global Enable */
-#define X86_FEATURE_MCA 0x00004000 /* Machine Check Architecture */
-#define X86_FEATURE_CMOV 0x00008000 /* CMOV instruction (FCMOVCC and FCOMI too if FPU present) */
-#define X86_FEATURE_PAT 0x00010000 /* Page Attribute Table */
-#define X86_FEATURE_PSE36 0x00020000 /* 36-bit PSEs */
-#define X86_FEATURE_PN 0x00040000
-#define X86_FEATURE_19 0x00080000
-#define X86_FEATURE_20 0x00100000
-#define X86_FEATURE_21 0x00200000
-#define X86_FEATURE_22 0x00400000
-#define X86_FEATURE_MMX 0x00800000 /* Multimedia Extensions */
-#define X86_FEATURE_FXSR 0x01000000 /* FXSAVE and FXRSTOR instructions (fast save and restore of FPU context), and CR4.OSFXSR (OS uses these instructions) available */
-#define X86_FEATURE_XMM 0x02000000 /* Streaming SIMD Extensions */
-#define X86_FEATURE_26 0x04000000
-#define X86_FEATURE_27 0x08000000
-#define X86_FEATURE_28 0x10000000
-#define X86_FEATURE_29 0x20000000
-#define X86_FEATURE_30 0x40000000
-#define X86_FEATURE_AMD3D 0x80000000
-
extern struct cpuinfo_x86 boot_cpu_data;
extern struct tss_struct init_tss[NR_CPUS];
#endif
#define cpu_has_pge \
- (boot_cpu_data.x86_capability & X86_FEATURE_PGE)
+ (test_bit(X86_FEATURE_PGE, &boot_cpu_data.x86_capability))
#define cpu_has_pse \
- (boot_cpu_data.x86_capability & X86_FEATURE_PSE)
+ (test_bit(X86_FEATURE_PSE, &boot_cpu_data.x86_capability))
#define cpu_has_pae \
- (boot_cpu_data.x86_capability & X86_FEATURE_PAE)
+ (test_bit(X86_FEATURE_PAE, &boot_cpu_data.x86_capability))
#define cpu_has_tsc \
- (boot_cpu_data.x86_capability & X86_FEATURE_TSC)
+ (test_bit(X86_FEATURE_TSC, &boot_cpu_data.x86_capability))
#define cpu_has_de \
- (boot_cpu_data.x86_capability & X86_FEATURE_DE)
+ (test_bit(X86_FEATURE_DE, &boot_cpu_data.x86_capability))
#define cpu_has_vme \
- (boot_cpu_data.x86_capability & X86_FEATURE_VME)
+ (test_bit(X86_FEATURE_VME, &boot_cpu_data.x86_capability))
#define cpu_has_fxsr \
- (boot_cpu_data.x86_capability & X86_FEATURE_FXSR)
+ (test_bit(X86_FEATURE_FXSR, &boot_cpu_data.x86_capability))
#define cpu_has_xmm \
- (boot_cpu_data.x86_capability & X86_FEATURE_XMM)
+ (test_bit(X86_FEATURE_XMM, &boot_cpu_data.x86_capability))
extern char ignore_irq13;
extern void dodgy_tsc(void);
/*
- * Generic CPUID function
+ * EFLAGS bits
+ */
+#define X86_EFLAGS_CF 0x00000001 /* Carry Flag */
+#define X86_EFLAGS_PF 0x00000004 /* Parity Flag */
+#define X86_EFLAGS_AF 0x00000010 /* Auxillary carry Flag */
+#define X86_EFLAGS_ZF 0x00000040 /* Zero Flag */
+#define X86_EFLAGS_SF 0x00000080 /* Sign Flag */
+#define X86_EFLAGS_TF 0x00000100 /* Trap Flag */
+#define X86_EFLAGS_IF 0x00000200 /* Interrupt Flag */
+#define X86_EFLAGS_DF 0x00000400 /* Direction Flag */
+#define X86_EFLAGS_OF 0x00000800 /* Overflow Flag */
+#define X86_EFLAGS_IOPL 0x00003000 /* IOPL mask */
+#define X86_EFLAGS_NT 0x00004000 /* Nested Task */
+#define X86_EFLAGS_RF 0x00010000 /* Resume Flag */
+#define X86_EFLAGS_VM 0x00020000 /* Virtual Mode */
+#define X86_EFLAGS_AC 0x00040000 /* Alignment Check */
+#define X86_EFLAGS_VIF 0x00080000 /* Virtual Interrupt Flag */
+#define X86_EFLAGS_VIP 0x00100000 /* Virtual Interrupt Pending */
+#define X86_EFLAGS_ID 0x00200000 /* CPUID detection flag */
+
+/*
+ * Generic CPUID function
*/
extern inline void cpuid(int op, int *eax, int *ebx, int *ecx, int *edx)
{
: "a" (op));
}
+/*
+ * CPUID functions returning a single datum
+ */
+extern inline unsigned int cpuid_eax(unsigned int op)
+{
+ unsigned int eax, ebx, ecx, edx;
+
+ __asm__("cpuid"
+ : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx)
+ : "a" (op));
+ return eax;
+}
+extern inline unsigned int cpuid_ebx(unsigned int op)
+{
+ unsigned int eax, ebx, ecx, edx;
+
+ __asm__("cpuid"
+ : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx)
+ : "a" (op));
+ return ebx;
+}
+extern inline unsigned int cpuid_ecx(unsigned int op)
+{
+ unsigned int eax, ebx, ecx, edx;
+
+ __asm__("cpuid"
+ : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx)
+ : "a" (op));
+ return ecx;
+}
+extern inline unsigned int cpuid_edx(unsigned int op)
+{
+ unsigned int eax, ebx, ecx, edx;
+
+ __asm__("cpuid"
+ : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx)
+ : "a" (op));
+ return edx;
+}
/*
* Intel CPU features in CR4
int divert_ioctl(unsigned int cmd, struct divert_cf *arg);
void divert_frame(struct sk_buff *skb);
-#endif __KERNEL__
+#endif
#endif /* _LINUX_DIVERT_H */
#define OSIOCSNETADDR _IOW(DECNET_IOCTL_BASE, 0xe0, int)
#define OSIOCGNETADDR _IOR(DECNET_IOCTL_BASE, 0xe1, int)
-/*
- * An unofficial structure used to set/get routes.
- * Be warned, this will probably change as the routing
- * evolves. Also this is only for use with the ioctl()
- * and the routing will use rtnetlink eventually.
- */
-struct dn_fib_rtinfo {
- unsigned long flags; /* Flags */
-#define DN_FIB_RTINFO_F_REPLACE 0x0001 /* Replace any existing route */
-#define DN_FIB_RTINFO_F_DEVCOST 0x0002 /* Add cost of device */
- unsigned long timeout; /* Time in seconds route should last */
- unsigned short src; /* Source Address, 0 = any */
- unsigned short dst; /* Destination Address */
- unsigned short nhp; /* Next Hop Address */
- unsigned short hops; /* Hops on path */
- unsigned short cost; /* Cost of path */
- char device[16];
-};
-
#endif /* _LINUX_DN_H */
-/* $Id: ethtool.h,v 1.1 2000/11/10 05:44:33 davem Exp $
+/* $Id: ethtool.h,v 1.2 2000/11/12 10:05:57 davem Exp $
* ethtool.h: Defines for Linux ethtool.
*
* Copyright (C) 1998 David S. Miller (davem@redhat.com)
#define PORT_AUI 0x01
#define PORT_MII 0x02
#define PORT_FIBRE 0x03
+#define PORT_BNC 0x04
/* Which tranceiver to use. */
#define XCVR_INTERNAL 0x00
* Mark functions and data as being only used at initialization
* or exit time.
*/
-#define __init /* __attribute__ ((__section__ (".text.init"))) */
+#define __init __attribute__ ((__section__ (".text.init")))
#define __exit __attribute__ ((unused, __section__(".text.exit")))
#define __initdata __attribute__ ((__section__ (".data.init")))
#define __exitdata __attribute__ ((unused, __section__ (".data.exit")))
#define NF_DN_ROUTE 6
#define NF_DN_NUMHOOKS 7
+enum nf_dn_hook_priorities {
+ NF_DN_PRI_FIRST = INT_MIN,
+ NF_DN_PRI_CONNTRACK = -200,
+ NF_DN_PRI_MANGLE = -150,
+ NF_DN_PRI_NAT_DST = -100,
+ NF_DN_PRI_FILTER = 0,
+ NF_DN_PRI_NAT_SRC = 100,
+ NF_DN_PRI_DNRTMSG = 200,
+ NF_DN_PRI_LAST = INT_MAX,
+};
+
+struct nf_dn_rtmsg {
+ int nfdn_ifindex;
+};
+
+#define NFDN_RTMSG(r) ((unsigned char *)(r) + NLMSG_ALIGN(sizeof(struct nf_dn_rtmsg)))
+
+#define DNRMG_L1_GROUP 0x01
+#define DNRMG_L2_GROUP 0x02
+
#endif /*__LINUX_DECNET_NETFILTER_H*/
#define NETLINK_ARPD 8
#define NETLINK_ROUTE6 11 /* af_inet6 route comm channel */
#define NETLINK_IP6_FW 13
+#define NETLINK_DNRTMSG 14 /* DECnet routing messages */
#define NETLINK_TAPBASE 16 /* 16 to 31 are ethertap */
#define MAX_LINKS 32
#define MD_BUG(x...) { printk("md: bug in file %s, line %d\n", __FILE__, __LINE__); md_print_devices(); }
-#endif _MD_H
+#endif
/* 001 */
extern __inline__ int md_cpu_has_mmx(void)
{
- return boot_cpu_data.x86_capability & X86_FEATURE_MMX;
+ return test_bit(X86_FEATURE_MMX, &boot_cpu_data.x86_capability);
}
#endif
/* END */
-#endif _MD_COMPATIBLE_H
+#endif
__wait_event_lock_irq(wq, condition, lock); \
} while (0)
-#endif _MD_K_H
+#endif
return (ev<<32)| sb->events_lo;
}
-#endif _MD_P_H
+#endif
int max_fault; /* unused for now */
} mdu_param_t;
-#endif _MD_U_H
+#endif
#define RTPROT_MRT 10 /* Merit MRT */
#define RTPROT_ZEBRA 11 /* Zebra */
#define RTPROT_BIRD 12 /* BIRD */
+#define RTPROT_DNROUTED 13 /* DECnet routing daemon */
/* rtm_scope
#undef ifdebug
#ifdef RPC_DEBUG
# define ifdebug(fac) if (rpc_debug & RPCDBG_##fac)
-# define dfprintk(fac, args...) do { ifdebug(fac) printk(## args); } while(0)
+# define dfprintk(fac, args...) do { ifdebug(fac) printk(args); } while(0)
# define RPC_IFDEBUG(x) x
#else
# define dfprintk(fac, args...) do ; while (0)
#ifndef _LINUX_SYSCTL_H
#define _LINUX_SYSCTL_H
+#include <linux/kernel.h>
+#include <linux/types.h>
#include <linux/list.h>
+struct file;
+
#define CTL_MAXNAME 10
struct __sysctl_args {
extern struct sk_buff *dn_alloc_skb(struct sock *sk, int size, int pri);
extern struct sk_buff *dn_alloc_send_skb(struct sock *sk, int *size, int noblock, int *err);
-#define NSP_REASON_NR 1
-#define NSP_REASON_DC 42
-#define NSP_REASON_NL 41
+#define NSP_REASON_OK 0 /* No error */
+#define NSP_REASON_NR 1 /* No resources */
+#define NSP_REASON_UN 2 /* Unrecognised node name */
+#define NSP_REASON_SD 3 /* Node shutting down */
+#define NSP_REASON_ID 4 /* Invalid destination end user */
+#define NSP_REASON_ER 5 /* End user lacks resources */
+#define NSP_REASON_OB 6 /* Object too busy */
+#define NSP_REASON_US 7 /* Unspecified error */
+#define NSP_REASON_TP 8 /* Third-Party abort */
+#define NSP_REASON_EA 9 /* End user has aborted the link */
+#define NSP_REASON_IF 10 /* Invalid node name format */
+#define NSP_REASON_LS 11 /* Local node shutdown */
+#define NSP_REASON_LL 32 /* Node lacks logical-link resources */
+#define NSP_REASON_LE 33 /* End user lacks logical-link resources */
+#define NSP_REASON_UR 34 /* Unacceptable RQSTRID or PASSWORD field */
+#define NSP_REASON_UA 36 /* Unacceptable ACCOUNT field */
+#define NSP_REASON_TM 38 /* End user timed out logical link */
+#define NSP_REASON_NU 39 /* Node unreachable */
+#define NSP_REASON_NL 41 /* No-link message */
+#define NSP_REASON_DC 42 /* Disconnect confirm */
+#define NSP_REASON_IO 43 /* Image data field overflow */
#define NSP_DISCINIT 0x38
#define NSP_DISCCONF 0x48
/* sysctl_net_x25.c */
extern void x25_register_sysctl(void);
extern void x25_unregister_sysctl(void);
-
+struct x25_skb_cb {
+ unsigned flags;
+};
+#define X25_SKB_CB(s) ((struct x25_skb_cb *) ((s)->cb))
#endif
/*
* The tasklist_lock protects the linked list of processes.
*
- * The scheduler lock is protecting against multiple entry
- * into the scheduling code, and doesn't need to worry
- * about interrupts (because interrupts cannot call the
- * scheduler).
- *
- * The run-queue lock locks the parts that actually access
+ * The runqueue_lock locks the parts that actually access
* and change the run-queues, and have to be interrupt-safe.
+ *
+ * If both locks are to be concurrently held, the runqueue_lock
+ * nests inside the tasklist_lock.
*/
-spinlock_t runqueue_lock __cacheline_aligned = SPIN_LOCK_UNLOCKED; /* second */
-rwlock_t tasklist_lock __cacheline_aligned = RW_LOCK_UNLOCKED; /* third */
+spinlock_t runqueue_lock __cacheline_aligned = SPIN_LOCK_UNLOCKED; /* inner */
+rwlock_t tasklist_lock __cacheline_aligned = RW_LOCK_UNLOCKED; /* outer */
static LIST_HEAD(runqueue_head);
/*
* This is ugly, but reschedule_idle() is very timing-critical.
- * We enter with the runqueue spinlock held, but we might end
- * up unlocking it early, so the caller must not unlock the
- * runqueue, it's always done by reschedule_idle().
- *
- * This function must be inline as anything that saves and restores
- * flags has to do so within the same register window on sparc (Anton)
+ * We `are called with the runqueue spinlock held and we must
+ * not claim the tasklist_lock.
*/
static FASTCALL(void reschedule_idle(struct task_struct * p));
/*
* We play safe to avoid deadlocks.
*/
- spin_lock_irq(&runqueue_lock);
- read_lock(&tasklist_lock);
+ read_lock_irq(&tasklist_lock);
+ spin_lock(&runqueue_lock);
p = find_process_by_pid(pid);
current->need_resched = 1;
out_unlock:
- read_unlock(&tasklist_lock);
- spin_unlock_irq(&runqueue_lock);
+ spin_unlock(&runqueue_lock);
+ read_unlock_irq(&tasklist_lock);
out_nounlock:
return retval;
*/
static struct task_struct * select_bad_process(void)
{
- int points = 0, maxpoints = 0;
+ int maxpoints = 0;
struct task_struct *p = NULL;
struct task_struct *chosen = NULL;
read_lock(&tasklist_lock);
- for_each_task(p)
- {
- if (p->pid)
- points = badness(p);
- if (points > maxpoints) {
- chosen = p;
- maxpoints = points;
+ for_each_task(p) {
+ if (p->pid) {
+ int points = badness(p);
+ if (points > maxpoints) {
+ chosen = p;
+ maxpoints = points;
+ }
}
}
read_unlock(&tasklist_lock);
memset(ax25_table, 0x00, ax25_table_size);
for (n = 0, ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next) {
- ctl_table *child = kmalloc(sizeof(ax25_param_table, GFP_ATOMIC);
+ ctl_table *child = kmalloc(sizeof(ax25_param_table), GFP_ATOMIC);
if (!child) {
while (n--)
kfree(ax25_table[n].child);
case SIOCSIFHWBROADCAST:
case SIOCSIFTXQLEN:
case SIOCSIFNAME:
+ case SIOCETHTOOL:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
dev_load(ifr.ifr_name);
*/
default:
- if ((cmd >= SIOCDEVPRIVATE &&
- cmd <= SIOCDEVPRIVATE + 15) ||
- cmd == SIOCETHTOOL) {
+ if (cmd >= SIOCDEVPRIVATE &&
+ cmd <= SIOCDEVPRIVATE + 15) {
dev_load(ifr.ifr_name);
rtnl_lock();
ret = dev_ifsioc(&ifr, cmd);
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
bool ' DECnet: router support (EXPERIMENTAL)' CONFIG_DECNET_ROUTER
if [ "$CONFIG_DECNET_ROUTER" = "y" ]; then
- bool ' DECnet: use FWMARK value as routing key (EXPERIMENTAL)' CONFIG_DECNET_ROUTE_FWMARK
+ if [ "$CONFIG_NETFILTER" = "y" ]; then
+ bool ' DECnet: use FWMARK value as routing key (EXPERIMENTAL)' CONFIG_DECNET_ROUTE_FWMARK
+ fi
fi
fi
send/recvmsg() calls should simply be a vector of set/getsockopt()
calls]
- o check MSG_TRUNC, MSG_CTRUNC are set where they should be.
+ o check MSG_CTRUNC is set where it should be.
o Start to hack together user level software and add more DECnet support
in ifconfig for example.
- o Fix conninit_rx to check out each CI before queuing it. Support code is
- now in place, so this should be easy.
-
- o Work out which errors we can return from conninit_rx. Support code is
- now in place, so this should be easy.
-
- o Check out receiving of errors in the light of what conninit_rx can return
-
o Test adding/deleting of routes
o Test route lookup
allow DECnet support in netstat.
o Make sure that returned connect messages are generated when they should
- be, and that the correct error messages are sent too. Ensure that the
- conninit receiving routine does not accept conninits with parameters
- that we cannot handle.
+ be, and that the correct error messages are sent too.
+
+ o Add the routing message grabbing netfilter module [written, tested,
+ awaiting merge]
+
+ o Add perfect socket hashing - an idea suggested by Paul Koning [part written,
+ awaiting debugging and merge]
* David S. Miller: New socket locking
* Steve Whitehouse: Socket list hashing/locking
* Arnaldo C. Melo: use capable, not suser
+ * Steve Whitehouse: Removed unused code. Fix to use sk->allocation
+ * when required.
*/
switch(scp->state) {
case DN_DN:
- dn_nsp_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC, GFP_KERNEL);
+ dn_nsp_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC, sk->allocation);
scp->persist_fxn = dn_destroy_timer;
scp->persist = dn_nsp_persist(sk);
break;
case DN_DI:
case DN_DR:
disc_reject:
- dn_nsp_send_disc(sk, NSP_DISCINIT, 0, GFP_KERNEL);
+ dn_nsp_send_disc(sk, NSP_DISCINIT, 0, sk->allocation);
case DN_NC:
case DN_NR:
case DN_RJ:
cb = (struct dn_skb_cb *)skb->cb;
- if ((newsk = dn_alloc_sock(newsock, GFP_KERNEL)) == NULL) {
+ if ((newsk = dn_alloc_sock(newsock, sk->allocation)) == NULL) {
release_sock(sk);
kfree_skb(skb);
return -ENOBUFS;
if (newsk->protinfo.dn.accept_mode == ACC_IMMED) {
newsk->protinfo.dn.state = DN_CC;
- dn_send_conn_conf(newsk, GFP_KERNEL);
+ dn_send_conn_conf(newsk, newsk->allocation);
err = dn_wait_accept(newsock, flags);
}
return -EINVAL;
scp->state = DN_CC;
- dn_send_conn_conf(sk, GFP_KERNEL);
+ dn_send_conn_conf(sk, sk->allocation);
err = dn_wait_accept(sock, sock->file->f_flags);
return err;
scp->state = DN_DR;
sk->shutdown = SHUTDOWN_MASK;
- dn_nsp_send_disc(sk, 0x38, 0, GFP_KERNEL);
+ dn_nsp_send_disc(sk, 0x38, 0, sk->allocation);
break;
default:
case DN_CR:
scp->state = DN_CC;
- dn_send_conn_conf(sk, GFP_KERNEL);
+ dn_send_conn_conf(sk, sk->allocation);
return dn_wait_accept(sk->socket, (flags & MSG_DONTWAIT) ? O_NONBLOCK : 0);
case DN_CI:
case DN_CC:
dn_dn2eth(decnet_ether_address, dn_ntohs(decnet_address));
#endif
- printk(KERN_INFO "NET4: DECnet for Linux: V.2.3.49s (C) 1995-2000 Linux DECnet Project Team\n");
+ printk(KERN_INFO "NET4: DECnet for Linux: V.2.4.0-test10s (C) 1995-2000 Linux DECnet Project Team\n");
sock_register(&dn_family_ops);
dev_add_pack(&dn_dix_packet_type);
#ifdef CONFIG_SYSCTL
dn_register_sysctl();
#endif /* CONFIG_SYSCTL */
+
+ /*
+ * Prevent DECnet module unloading until its fixed properly.
+ * Requires an audit of the code to check for memory leaks and
+ * initialisation problems etc.
+ */
+ MOD_INC_USE_COUNT;
+
return 0;
}
{
unsigned short area = simple_strtoul(str, &str, 0);
unsigned short node = simple_strtoul(*str > 0 ? ++str : str, &str, 0);
- /* unsigned short type = simple_strtoul(*str > 0 ? ++str : str, &str, 0); */
decnet_address = dn_htons(area << 10 | node);
dn_dn2eth(decnet_ether_address, dn_ntohs(decnet_address));
* Steve Whitehouse : SIOCGIFCONF is now a compile time option
* Steve Whitehouse : /proc/sys/net/decnet/conf/<sys>/forwarding
* Steve Whitehouse : Removed timer1 - its a user space issue now
+ * Patrick Caulfield : Fixed router hello message format
*/
#include <linux/config.h>
DN_RT_INFO_L1RT : DN_RT_INFO_L2RT;
*((unsigned short *)ptr) = dn_htons(dn_db->parms.blksize);
ptr += 2;
- *ptr++ = 0; /* Priority */
+ *ptr++ = dn_db->parms.priority; /* Priority */
*ptr++ = 0; /* Area: Reserved */
*((unsigned short *)ptr) = dn_htons((unsigned short)dn_db->parms.t3);
ptr += 2;
*ptr++ = 0; /* MPD: Reserved */
i1 = ptr++;
memset(ptr, 0, 7); /* Name: Reserved */
+ ptr += 7;
i2 = ptr++;
n = dn_neigh_elist(dev, ptr, n);
*i2 = 7 * n;
*i1 = 8 + *i2;
- skb_trim(skb, (26 + *i2));
+ skb_trim(skb, (27 + *i2));
pktlen = (unsigned short *)skb_push(skb, 2);
*pktlen = dn_htons(skb->len - 2);
-
/*
* DECnet An implementation of the DECnet protocol suite for the LINUX
* operating system. DECnet is implemented using the BSD Socket
* Steve Whitehouse: Fixed lockup when socket filtering was enabled.
* Paul Koning: Fix to push CC sockets into RUN when acks are
* received.
+ * Steve Whitehouse:
+ * Patrick Caulfield: Checking conninits for correctness & sending of error
+ * responses.
*/
/******************************************************************************
#include <net/dn_dev.h>
#include <net/dn_route.h>
+extern int decnet_log_martians;
+
+static void dn_log_martian(struct sk_buff *skb, const char *msg)
+{
+ if (decnet_log_martians && net_ratelimit()) {
+ char *devname = skb->rx_dev ? skb->rx_dev->name : "???";
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+ printk(KERN_INFO "DECnet: Martian packet (%s) rx_dev=%s src=0x%04hx dst=0x%04hx srcport=0x%04hx dstport=0x%04hx\n", msg, devname, cb->src, cb->dst, cb->src_port, cb->dst_port);
+ }
+}
/*
* For this function we've flipped the cross-subchannel bit
unsigned short type = ((ack >> 12) & 0x0003);
int wakeup = 0;
- /* printk(KERN_DEBUG "dn_ack: %hd 0x%04hx\n", type, ack); */
-
switch(type) {
case 0: /* ACK - Data */
if (after(ack, scp->ackrcv_dat)) {
}
+/**
+ * dn_check_idf - Check an image data field format is correct.
+ * @pptr: Pointer to pointer to image data
+ * @len: Pointer to length of image data
+ * @max: The maximum allowed length of the data in the image data field
+ * @follow_on: Check that this many bytes exist beyond the end of the image data
+ *
+ * Returns: 0 if ok, -1 on error
+ */
+static inline int dn_check_idf(unsigned char **pptr, int *len, unsigned char max, unsigned char follow_on)
+{
+ unsigned char *ptr = *pptr;
+ unsigned char flen = *ptr++;
+
+ (*len)--;
+ if (flen > max)
+ return -1;
+ if ((flen + follow_on) > *len)
+ return -1;
+
+ *len -= flen;
+ *pptr = ptr + flen;
+ return 0;
+}
+
+/*
+ * Table of reason codes to pass back to node which sent us a badly
+ * formed message, plus text messages for the log. A zero entry in
+ * the reason field means "don't reply" otherwise a disc init is sent with
+ * the specified reason code.
+ */
+static struct {
+ unsigned short reason;
+ const char *text;
+} ci_err_table[] = {
+ { 0, "CI: Truncated message" },
+ { NSP_REASON_ID, "CI: Destination username error" },
+ { NSP_REASON_ID, "CI: Destination username type" },
+ { NSP_REASON_US, "CI: Source username error" },
+ { 0, "CI: Truncated at menuver" },
+ { 0, "CI: Truncated before access or user data" },
+ { NSP_REASON_IO, "CI: Access data format error" },
+ { NSP_REASON_IO, "CI: User data format error" }
+};
+
/*
* This function uses a slightly different lookup method
* to find its sockets, since it searches on object name/number
- * rather than port numbers
+ * rather than port numbers. Various tests are done to ensure that
+ * the incoming data is in the correct format before it is queued to
+ * a socket.
*/
-static struct sock *dn_find_listener(struct sk_buff *skb)
+static struct sock *dn_find_listener(struct sk_buff *skb, unsigned short *reason)
{
struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
struct nsp_conn_init_msg *msg = (struct nsp_conn_init_msg *)skb->data;
- struct sockaddr_dn addr;
+ struct sockaddr_dn dstaddr;
+ struct sockaddr_dn srcaddr;
unsigned char type = 0;
+ int dstlen;
+ int srclen;
+ unsigned char *ptr;
+ int len;
+ int err = 0;
+ unsigned char menuver;
- memset(&addr, 0, sizeof(struct sockaddr_dn));
+ memset(&dstaddr, 0, sizeof(struct sockaddr_dn));
+ memset(&srcaddr, 0, sizeof(struct sockaddr_dn));
+ /*
+ * 1. Decode & remove message header
+ */
cb->src_port = msg->srcaddr;
cb->dst_port = msg->dstaddr;
cb->services = msg->services;
cb->info = msg->info;
cb->segsize = dn_ntohs(msg->segsize);
+ if (skb->len < sizeof(*msg))
+ goto err_out;
+
skb_pull(skb, sizeof(*msg));
- /* printk(KERN_DEBUG "username2sockaddr 1\n"); */
- if (dn_username2sockaddr(skb->data, skb->len, &addr, &type) < 0)
+ len = skb->len;
+ ptr = skb->data;
+
+ /*
+ * 2. Check destination end username format
+ */
+ dstlen = dn_username2sockaddr(ptr, len, &dstaddr, &type);
+ err++;
+ if (dstlen < 0)
goto err_out;
+ err++;
if (type > 1)
goto err_out;
- /* printk(KERN_DEBUG "looking for listener...\n"); */
- return dn_sklist_find_listener(&addr);
+ len -= dstlen;
+ ptr += dstlen;
+
+ /*
+ * 3. Check source end username format
+ */
+ srclen = dn_username2sockaddr(ptr, len, &srcaddr, &type);
+ err++;
+ if (srclen < 0)
+ goto err_out;
+
+ len -= srclen;
+ ptr += srclen;
+ err++;
+ if (len < 1)
+ goto err_out;
+
+ menuver = *ptr;
+ ptr++;
+ len--;
+
+ /*
+ * 4. Check that optional data actually exists if menuver says it does
+ */
+ err++;
+ if ((menuver & (DN_MENUVER_ACC | DN_MENUVER_USR)) && (len < 1))
+ goto err_out;
+
+ /*
+ * 5. Check optional access data format
+ */
+ err++;
+ if (menuver & DN_MENUVER_ACC) {
+ if (dn_check_idf(&ptr, &len, 39, 1))
+ goto err_out;
+ if (dn_check_idf(&ptr, &len, 39, 1))
+ goto err_out;
+ if (dn_check_idf(&ptr, &len, 39, (menuver & DN_MENUVER_USR) ? 1 : 0))
+ goto err_out;
+ }
+
+ /*
+ * 6. Check optional user data format
+ */
+ err++;
+ if (menuver & DN_MENUVER_USR) {
+ if (dn_check_idf(&ptr, &len, 16, 0))
+ goto err_out;
+ }
+
+ /*
+ * 7. Look up socket based on destination end username
+ */
+ return dn_sklist_find_listener(&dstaddr);
err_out:
+ dn_log_martian(skb, ci_err_table[err].text);
+ *reason = ci_err_table[err].reason;
return NULL;
}
+
static void dn_nsp_conn_init(struct sock *sk, struct sk_buff *skb)
{
- /* printk(KERN_DEBUG "checking backlog...\n"); */
if (sk->ack_backlog >= sk->max_ack_backlog) {
kfree_skb(skb);
return;
}
- /* printk(KERN_DEBUG "waking up socket...\n"); */
sk->ack_backlog++;
skb_queue_tail(&sk->receive_queue, skb);
sk->state_change(sk);
kfree_skb(skb);
}
-static void dn_nsp_no_socket(struct sk_buff *skb)
+static void dn_nsp_no_socket(struct sk_buff *skb, unsigned short reason)
{
struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
- switch(cb->nsp_flags) {
- case 0x28: /* Connect Confirm */
- dn_nsp_return_disc(skb, NSP_DISCCONF, NSP_REASON_NL);
+ if ((reason != NSP_REASON_OK) && ((cb->nsp_flags & 0x0c) == 0x08)) {
+ switch(cb->nsp_flags & 0x70) {
+ case 0x10:
+ case 0x60: /* (Retransmitted) Connect Init */
+ dn_nsp_return_disc(skb, NSP_DISCINIT, reason);
+ break;
+ case 0x20: /* Connect Confirm */
+ dn_nsp_return_disc(skb, NSP_DISCCONF, reason);
+ break;
+ }
}
kfree_skb(skb);
struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
struct sock *sk = NULL;
unsigned char *ptr = (unsigned char *)skb->data;
+ unsigned short reason = NSP_REASON_NL;
skb->h.raw = skb->data;
cb->nsp_flags = *ptr++;
goto free_out;
case 0x10:
case 0x60:
- sk = dn_find_listener(skb);
+ sk = dn_find_listener(skb, &reason);
goto got_it;
}
}
return ret;
}
- dn_nsp_no_socket(skb);
+ dn_nsp_no_socket(skb, reason);
return 1;
free_out:
* Control packet.
*/
if ((cb->nsp_flags & 0x0c) == 0x08) {
- /* printk(KERN_DEBUG "control type\n"); */
switch(cb->nsp_flags & 0x70) {
case 0x10:
case 0x60:
continue;
}
- if ((skb = dn_alloc_skb(sk, len, GFP_KERNEL)) == NULL)
+ if ((skb = dn_alloc_skb(sk, len, sk->allocation)) == NULL)
continue;
*size = len - 11;
struct sk_buff *skb = NULL;
struct nsp_conn_ack_msg *msg;
- if ((skb = dn_alloc_skb(sk, 3, GFP_KERNEL)) == NULL)
+ if ((skb = dn_alloc_skb(sk, 3, sk->allocation)) == NULL)
return;
msg = (struct nsp_conn_ack_msg *)skb_put(skb, 3);
struct dn_skb_cb *cb;
unsigned char type = 1;
- if ((skb = dn_alloc_skb(sk, 200, (msgflg == NSP_CI) ? GFP_KERNEL : GFP_ATOMIC)) == NULL)
+ if ((skb = dn_alloc_skb(sk, 200, (msgflg == NSP_CI) ? sk->allocation : GFP_ATOMIC)) == NULL)
return;
cb = (struct dn_skb_cb *)skb->cb;
key.oif = 0;
key.scope = RT_SCOPE_UNIVERSE;
-#ifdef CONFIG_DECNET_ROUTE_FWMASK
- key.fwmark = skb->fwmark;
+#ifdef CONFIG_DECNET_ROUTE_FWMARK
+ key.fwmark = skb->nfmark;
#else
key.fwmark = 0;
#endif
if ((rt->key.saddr == cb->src) &&
(rt->key.daddr == cb->dst) &&
(rt->key.oif == 0) &&
-#ifdef CONFIG_DECNET_ROUTE_FWMASK
+#ifdef CONFIG_DECNET_ROUTE_FWMARK
(rt->key.fwmark == skb->nfmark) &&
#endif
(rt->key.iif == cb->iif)) {
int decnet_dn_count = 1;
int decnet_di_count = 3;
int decnet_dr_count = 3;
+int decnet_log_martians = 1;
#ifdef CONFIG_SYSCTL
extern int decnet_dst_gc_interval;
*
********************************************************************/
+#include <linux/config.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
* and exchange frames with IrTTP.
*/
+#include <linux/config.h>
#include "irnet_irda.h" /* Private header */
/************************* CONTROL CHANNEL *************************/
/***************************** INCLUDES *****************************/
+#include <linux/config.h>
#include "irnet.h" /* Module global include */
/************************ CONSTANTS & MACROS ************************/
EXPORT_SYMBOL(irtty_set_packet_mode);
#endif
-static int __init irda_init(void)
+int __init irda_init(void)
{
IRDA_DEBUG(0, __FUNCTION__ "()\n");
********************************************************************/
#include <asm/system.h>
+#include <linux/config.h>
#include <linux/delay.h>
#include <net/irda/timer.h>
* X.25 001 Jonathan Naylor Started coding.
* X.25 002 Jonathan Naylor Centralised disconnect handling.
* New timer architecture.
- * 2000-11-03 Henner Eisen MSG_EOR handling more POSIX compliant.
- * 2000-22-03 Daniela Squassoni Allowed disabling/enabling of
+ * 2000-03-11 Henner Eisen MSG_EOR handling more POSIX compliant.
+ * 2000-03-22 Daniela Squassoni Allowed disabling/enabling of
* facilities negotiation and increased
* the throughput upper limit.
- * 2000-27-08 Arnaldo C. Melo s/suser/capable/ + micro cleanups
- * 2000-04-09 Henner Eisen Set sock->state in x25_accept().
+ * 2000-08-27 Arnaldo C. Melo s/suser/capable/ + micro cleanups
+ * 2000-09-04 Henner Eisen Set sock->state in x25_accept().
* Fixed x25_output() related skb leakage.
+ * 2000-10-02 Henner Eisen Made x25_kick() single threaded per socket.
+ * 2000-10-27 Henner Eisen MSG_DONTWAIT for fragment allocation.
*/
#include <linux/config.h>
sock->ops = &x25_proto_ops;
sk->protocol = protocol;
+ sk->backlog_rcv = x25_process_rx_frame;
x25->t21 = sysctl_x25_call_request_timeout;
x25->t22 = sysctl_x25_reset_request_timeout;
if ((skb = sock_alloc_send_skb(sk, size, 0, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL)
return err;
+ X25_SKB_CB(skb)->flags = msg->msg_flags;
skb_reserve(skb, X25_MAX_L2_LEN + X25_EXT_MIN_LEN);
if (msg->msg_flags & MSG_OOB) {
skb_queue_tail(&sk->protinfo.x25->interrupt_out_queue, skb);
} else {
- err = x25_output(sk, skb);
- if(err){
- len = err;
+ len = x25_output(sk, skb);
+ if(len<0){
kfree_skb(skb);
+ } else {
+ if(sk->protinfo.x25->qbitincl) len++;
}
}
+ /*
+ * lock_sock() is currently only used to serialize this x25_kick()
+ * against input-driven x25_kick() calls. It currently only blocks
+ * incoming packets for this socket and does not protect against
+ * any other socket state changes and is not called from anywhere
+ * else. As x25_kick() cannot block and as long as all socket
+ * operations are BKL-wrapped, we don't need take to care about
+ * purging the backlog queue in x25_release().
+ *
+ * Using lock_sock() to protect all socket operations entirely
+ * (and making the whole x25 stack SMP aware) unfortunately would
+ * require major changes to {send,recv}msg and skb allocation methods.
+ * -> 2.5 ;)
+ */
+ lock_sock(sk);
x25_kick(sk);
+ release_sock(sk);
return len;
}
* Find an existing socket.
*/
if ((sk = x25_find_socket(lci, neigh)) != NULL) {
+ int queued = 1;
+
skb->h.raw = skb->data;
- return x25_process_rx_frame(sk, skb);
+ bh_lock_sock(sk);
+ if (!sk->lock.users) {
+ queued = x25_process_rx_frame(sk, skb);
+ } else {
+ sk_add_backlog(sk, skb);
+ }
+ bh_unlock_sock(sk);
+ return queued;
}
/*
* X.25 001 Jonathan Naylor Started coding.
* X.25 002 Jonathan Naylor Centralised disconnection code.
* New timer architecture.
- * mar/20/00 Daniela Squassoni Disabling/enabling of facilities
+ * 2000-03-20 Daniela Squassoni Disabling/enabling of facilities
* negotiation.
+ * 2000-11-10 Henner Eisen Check and reset for out-of-sequence
+ * i-frames.
*/
#include <linux/config.h>
case X25_DATA: /* XXX */
sk->protinfo.x25->condition &= ~X25_COND_PEER_RX_BUSY;
- if (!x25_validate_nr(sk, nr)) {
+ if ((ns!=sk->protinfo.x25->vr) ||
+ !x25_validate_nr(sk, nr)) {
x25_clear_queues(sk);
x25_write_internal(sk, X25_RESET_REQUEST);
x25_start_t22timer(sk);
* History
* X.25 001 Jonathan Naylor Started coding.
* X.25 002 Jonathan Naylor New timer architecture.
- * 2000-09-04 Henner Eisen Prevented x25_output() skb leakage.
+ * 2000-09-04 Henner Eisen Prevented x25_output() skb leakage.
+ * 2000-10-27 Henner Eisen MSG_DONTWAIT for fragment allocation.
+ * 2000-11-10 Henner Eisen x25_send_iframe(): re-queued frames
+ * needed cleaned seq-number fields.
*/
#include <linux/config.h>
}
/*
- * This is where all X.25 information frames pass;
+ * This is where all X.25 information frames pass.
+ *
+ * Returns the amount of user data bytes sent on success
+ * or a negative error code on failure.
*/
int x25_output(struct sock *sk, struct sk_buff *skb)
{
struct sk_buff *skbn;
unsigned char header[X25_EXT_MIN_LEN];
int err, frontlen, len, header_len, max_len;
+ int sent=0, noblock = X25_SKB_CB(skb)->flags & MSG_DONTWAIT;
header_len = (sk->protinfo.x25->neighbour->extended) ? X25_EXT_MIN_LEN : X25_STD_MIN_LEN;
max_len = x25_pacsize_to_bytes(sk->protinfo.x25->facilities.pacsize_out);
frontlen = skb_headroom(skb);
while (skb->len > 0) {
- if ((skbn = sock_alloc_send_skb(sk, frontlen + max_len, 0, 0, &err)) == NULL){
- int unsent = skb->len - header_len;
- SOCK_DEBUG(sk, "x25_output: framgent allocation failed, err=%d, %d bytes unsent\n", err, unsent);
- return err;
+ if ((skbn = sock_alloc_send_skb(sk, frontlen + max_len, 0, noblock, &err)) == NULL){
+ if(err == -EWOULDBLOCK && noblock){
+ kfree_skb(skb);
+ return sent;
}
+ SOCK_DEBUG(sk, "x25_output: fragment allocation failed, err=%d, %d bytes sent\n", err, sent);
+ return err;
+ }
skb_reserve(skbn, frontlen);
}
skb_queue_tail(&sk->write_queue, skbn);
+ sent += len;
}
kfree_skb(skb);
} else {
skb_queue_tail(&sk->write_queue, skb);
+ sent = skb->len - header_len;
}
- return 0;
+ return sent;
}
/*
return;
if (sk->protinfo.x25->neighbour->extended) {
- skb->data[2] |= (sk->protinfo.x25->vs << 1) & 0xFE;
+ skb->data[2] = (sk->protinfo.x25->vs << 1) & 0xFE;
+ skb->data[3] &= X25_EXT_M_BIT;
skb->data[3] |= (sk->protinfo.x25->vr << 1) & 0xFE;
} else {
+ skb->data[2] &= X25_STD_M_BIT;
skb->data[2] |= (sk->protinfo.x25->vs << 1) & 0x0E;
skb->data[2] |= (sk->protinfo.x25->vr << 5) & 0xE0;
}