diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/Documentation/filesystems/driverfs.txt linux-2.5-bk/Documentation/filesystems/driverfs.txt --- linux-2.5.45/Documentation/filesystems/driverfs.txt Wed Oct 30 16:43:34 2002 +++ linux-2.5-bk/Documentation/filesystems/driverfs.txt Wed Dec 31 16:00:00 1969 @@ -1,336 +0,0 @@ - -driverfs - The Device Driver Filesystem - -Patrick Mochel - -2 August 2002 - - -What it is: -~~~~~~~~~~~ -driverfs is a ram-based filesystem. It was created by copying -ramfs/inode.c to driverfs/inode.c and doing a little search-and-replace. - -driverfs is a means to export kernel data structures, their -attributes, and the linkages between them to userspace. - -driverfs provides a unified interface for exporting attributes to -userspace. Currently, this interface is available only to device and -bus drivers. - - -Using driverfs -~~~~~~~~~~~~~~ -driverfs is always compiled in. You can access it by doing something like: - - mount -t driverfs driverfs /devices - - -Top Level Directory Layout -~~~~~~~~~~~~~~~~~~~~~~~~~~ -The driverfs directory arrangement exposes the relationship of kernel -data structures. - -The top level driverfs diretory looks like: - -bus/ -root/ - -root/ contains a filesystem representation of the device tree. It maps -directly to the internal kernel device tree, which is a hierarchy of -struct device. - -bus/ contains flat directory layout of the various bus types in the -kernel. Each bus's directory contains two subdirectories: - - devices/ - drivers/ - -devices/ contains symlinks for each device discovered in the system -that point to the device's directory under root/. - -drivers/ contains a directory for each device driver that is loaded -for devices on that particular bus (this assmumes that drivers do not -span multiple bus types). - - -More information can device-model specific features can be found in -Documentation/device-model/. - - -Directory Contents -~~~~~~~~~~~~~~~~~~ -Each object that is represented in driverfs gets a directory, rather -than a file, to make it simple to export attributes of that object. -Attributes are exported via ASCII text files. The programming -interface is discussed below. - -Instead of having monolithic files that are difficult to parse, all -files are intended to export one attribute. The name of the attribute -is the name of the file. The value of the attribute are the contents -of the file. - -There should be few, if any, exceptions to this rule. You should not -violate it, for fear of public humilation. - - -The Two-Tier Model -~~~~~~~~~~~~~~~~~~ - -driverfs is a very simple, low-level interface. In order for kernel -objects to use it, there must be an intermediate layer in place for -each object type. - -All calls in driverfs are intended to be as type-safe as possible. -In order to extend driverfs to support multiple data types, a layer of -abstraction was required. This intermediate layer converts between the -generic calls and data structures of the driverfs core to the -subsystem-specific objects and calls. - - -The Subsystem Interface -~~~~~~~~~~~~~~~~~~~~~~~ - -The subsystems bear the responsibility of implementing driverfs -extensions for the objects they control. Fortunately, it's intended to -be really easy to do so. - -It's divided into three sections: directories, files, and operations. - - -Directories -~~~~~~~~~~~ - -struct driver_dir_entry { - char * name; - struct dentry * dentry; - mode_t mode; - struct driverfs_ops * ops; -}; - - -int -driverfs_create_dir(struct driver_dir_entry *, struct driver_dir_entry *); - -void -driverfs_remove_dir(struct driver_dir_entry * entry); - -The directory structure should be statically allocated, and reside in -a subsystem-specific data structure: - -struct device { - ... - struct driver_dir_entry dir; -}; - -The subsystem is responsible for initializing the name, mode, and ops -fields of the directory entry. (More on struct driverfs_ops later) - - -Files -~~~~~ - -struct attribute { - char * name; - mode_t mode; -}; - - -int -driverfs_create_file(struct attribute * attr, struct driver_dir_entry * parent); - -void -driverfs_remove_file(struct driver_dir_entry *, const char * name); - - -The attribute structure is a simple, common token that the driverfs -core handles. It has little use on its own outside of the -core. Objects cannot use a plain struct attribute to export -attributes, since there are no callbacks for reading and writing data. - -Therefore, the subsystem is required to define a data structure that -encapsulates the attribute structure, and provides type-safe callbacks -for reading and writing data. - -An example looks like this: - -struct device_attribute { - struct attribute attr; - ssize_t (*show)(struct device * dev, char * buf, size_t count, loff_t off); - ssize_t (*store)(struct device * dev, const char * buf, size_t count, loff_t off); -}; - - -Note that there is a struct attribute embedded in the structure. In -order to relieve pain in declaring attributes, the subsystem should -also define a macro, like: - -#define DEVICE_ATTR(_name,_mode,_show,_store) \ -struct device_attribute dev_attr_##_name = { \ - .attr = {.name = __stringify(_name) , .mode = _mode }, \ - .show = _show, \ - .store = _store, \ -}; - -This hides the initialization of the embedded struct, and in general, -the internals of each structure. It yields a structure by the name of -dev_attr_. - -In order for objects to create files, the subsystem should create -wrapper functions, like this: - -int device_create_file(struct device *device, struct device_attribute * entry); -void device_remove_file(struct device * dev, struct device_attribute * attr); - -..and forward the call on to the driverfs functions. - -Note that there is no unique information in the attribute structures, -so the same structure can be used to describe files of several -different object instances. - - -Operations -~~~~~~~~~~ - -struct driverfs_ops { - int (*open)(struct driver_dir_entry *); - int (*close)(struct driver_dir_entry *); - ssize_t (*show)(struct driver_dir_entry *, struct attribute *,char *, size_t, loff_t); - ssize_t (*store)(struct driver_dir_entry *,struct attribute *,const char *, size_t, loff_t); -}; - - -Subsystems are required to implement this set of callbacks. Their -purpose is to translate the generic data structures into the specific -objects, and operate on them. This can be done by defining macros like -this: - -#define to_dev_attr(_attr) container_of(_attr,struct device_attribute,attr) - -#define to_device(d) container_of(d, struct device, dir) - - -Since the directories are statically allocated in the object, you can -derive the pointer to the object that owns the file. Ditto for the -attribute structures. - -Current Interfaces -~~~~~~~~~~~~~~~~~~ - -The following interface layers currently exist in driverfs: - - -- devices (include/linux/device.h) ----------------------------------- -Structure: - -struct device_attribute { - struct attribute attr; - ssize_t (*show)(struct device * dev, char * buf, size_t count, loff_t off); - ssize_t (*store)(struct device * dev, const char * buf, size_t count, loff_t off); -}; - -Declaring: - -DEVICE_ATTR(_name,_str,_mode,_show,_store); - -Creation/Removal: - -int device_create_file(struct device *device, struct device_attribute * entry); -void device_remove_file(struct device * dev, struct device_attribute * attr); - - -- bus drivers (include/linux/device.h) --------------------------------------- -Structure: - -struct bus_attribute { - struct attribute attr; - ssize_t (*show)(struct bus_type *, char * buf, size_t count, loff_t off); - ssize_t (*store)(struct bus_type *, const char * buf, size_t count, loff_t off); -}; - -Declaring: - -BUS_ATTR(_name,_mode,_show,_store) - -Creation/Removal: - -int bus_create_file(struct bus_type *, struct bus_attribute *); -void bus_remove_file(struct bus_type *, struct bus_attribute *); - - -- device drivers (include/linux/device.h) ------------------------------------------ - -Structure: - -struct driver_attribute { - struct attribute attr; - ssize_t (*show)(struct device_driver *, char * buf, size_t count, loff_t off); - ssize_t (*store)(struct device_driver *, const char * buf, size_t count, loff_t off); -}; - -Declaring: - -DRIVER_ATTR(_name,_mode,_show,_store) - -Creation/Removal: - -int driver_create_file(struct device_driver *, struct driver_attribute *); -void driver_remove_file(struct device_driver *, struct driver_attribute *); - - -Reading/Writing Data -~~~~~~~~~~~~~~~~~~~~ -The callback functionality is similar to the way procfs works. When a -user performs a read(2) or write(2) on the file, it first calls a -driverfs function. This calls to the subsystem, which then calls to -the object's show() or store() function. - -The buffer pointer, offset, and length should be passed to each -function. The downstream callback should fill the buffer and return -the number of bytes read/written. - - -What driverfs is not: -~~~~~~~~~~~~~~~~~~~~~ -It is not a replacement for either devfs or procfs. - -It does not handle device nodes, like devfs is intended to do. I think -this functionality is possible, but indeed think that integration of -the device nodes and control files should be done. Whether driverfs or -devfs, or something else, is the place to do it, I don't know. - -It is not intended to be a replacement for all of the procfs -functionality. I think that many of the driver files should be moved -out of /proc (and maybe a few other things as well ;). - - - -Limitations: -~~~~~~~~~~~~ -The driverfs functions assume that at most a page is being either read -or written each time. - -There is a race condition that is really, really hard to fix; if not -impossible. There exists a race between a driverfs file being opened -and the object that owns the file going away. During the driverfs -open() callback, the reference count for the owning object needs to be -incremented. - -For drivers, we can put a struct module * owner in struct driver_dir_entry -and do try_inc_mod_count() when we open a file. However, this won't -work for devices, that aren't tied to a module. And, it is still not -guaranteed to solve the race. - -I'm looking into fixing this, but it may not be doable without making -a separate filesystem instance for each object. It's fun stuff. Please -mail me with creative ideas that you know will work. - - -Possible bugs: -~~~~~~~~~~~~~~ -It may not deal with offsets and/or seeks very well, especially if -they cross a page boundary. - diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/Documentation/filesystems/proc.txt linux-2.5-bk/Documentation/filesystems/proc.txt --- linux-2.5.45/Documentation/filesystems/proc.txt Wed Oct 30 16:42:21 2002 +++ linux-2.5-bk/Documentation/filesystems/proc.txt Thu Oct 31 22:12:35 2002 @@ -130,6 +130,7 @@ stat Process status statm Process memory status information status Process status in human readable form + wchan If CONFIG_KALLSYMS is set, a pre-decoded wchan .............................................................................. For example, to get the status information of a process, all you have to do is diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/Documentation/video4linux/bttv/CARDLIST linux-2.5-bk/Documentation/video4linux/bttv/CARDLIST --- linux-2.5.45/Documentation/video4linux/bttv/CARDLIST Wed Oct 30 16:41:55 2002 +++ linux-2.5-bk/Documentation/video4linux/bttv/CARDLIST Thu Oct 31 22:12:36 2002 @@ -2,13 +2,13 @@ card=0 - *** UNKNOWN/GENERIC *** card=1 - MIRO PCTV card=2 - Hauppauge (bt848) - card=3 - STB + card=3 - STB, Gateway P/N 6000699 (bt848) card=4 - Intel Create and Share PCI/ Smart Video Recorder III card=5 - Diamond DTV2000 card=6 - AVerMedia TVPhone card=7 - MATRIX-Vision MV-Delta card=8 - Lifeview FlyVideo II (Bt848) LR26 - card=9 - IXMicro TurboTV + card=9 - IMS/IXmicro TurboTV card=10 - Hauppauge (bt878) card=11 - MIRO PCTV pro card=12 - ADS Technologies Channel Surfer TV (bt848) @@ -24,22 +24,22 @@ card=22 - Askey CPH050/ Phoebe Tv Master + FM card=23 - Modular Technology MM205 PCTV, bt878 card=24 - Askey CPH05X/06X (bt878) [many vendors] - card=25 - Terratec Terra TV+ Version 1.0 (Bt848)/Vobis TV-Boostar + card=25 - Terratec TerraTV+ Version 1.0 (Bt848)/ Terra TValue Version 1.0/ Vobis TV-Boostar card=26 - Hauppauge WinCam newer (bt878) card=27 - Lifeview FlyVideo 98/ MAXI TV Video PCI2 LR50 - card=28 - Terratec TerraTV+ + card=28 - Terratec TerraTV+ Version 1.1 (bt878) card=29 - Imagenation PXC200 card=30 - Lifeview FlyVideo 98 LR50 card=31 - Formac iProTV card=32 - Intel Create and Share PCI/ Smart Video Recorder III - card=33 - Terratec TerraTValue - card=34 - Leadtek WinFast 2000 + card=33 - Terratec TerraTValue Version Bt878 + card=34 - Leadtek WinFast 2000/ WinFast 2000 XP card=35 - Lifeview FlyVideo 98 LR50 / Chronos Video Shuttle II card=36 - Lifeview FlyVideo 98FM LR50 / Typhoon TView TV/FM Tuner card=37 - Prolink PixelView PlayTV pro card=38 - Askey CPH06X TView99 card=39 - Pinnacle PCTV Studio/Rave - card=40 - STB2 + card=40 - STB TV PCI FM, Gateway P/N 6000704 (bt878) card=41 - AVerMedia TVPhone 98 card=42 - ProVideo PV951 card=43 - Little OnAir TV @@ -78,14 +78,17 @@ card=76 - Canopus WinDVR PCI (COMPAQ Presario 3524JP, 5112JP) card=77 - GrandTec Multi Capture Card (Bt878) card=78 - Jetway TV/Capture JW-TV878-FBK, Kworld KW-TV878RF + card=79 - DSP Design TCVIDEO + card=80 - Hauppauge WinTV PVR + card=81 - GV-BCTV5/PCI tuner.o type=0 - Temic PAL (4002 FH5) - type=1 - Philips PAL_I - type=2 - Philips NTSC - type=3 - Philips SECAM + type=1 - Philips PAL_I (FI1246 and compatibles) + type=2 - Philips NTSC (FI1236 and compatibles) + type=3 - Philips (SECAM+PAL_BG) (FI1216MF, FM1216MF, FR1216MF) type=4 - NoTuner - type=5 - Philips PAL + type=5 - Philips PAL_BG (FI1216 and compatibles) type=6 - Temic NTSC (4032 FY5) type=7 - Temic PAL_I (4062 FY5) type=8 - Temic NTSC (4036 FY5) @@ -103,7 +106,7 @@ type=20 - Temic PAL_BG (4009 FR5) or PAL_I (4069 FR5) type=21 - Temic NTSC (4039 FR5) type=22 - Temic PAL/SECAM multi (4046 FM5) - type=23 - Philips PAL_DK + type=23 - Philips PAL_DK (FI1256 and compatibles) type=24 - Philips PAL/SECAM multi (FQ1216ME) type=25 - LG PAL_I+FM (TAPC-I001D) type=26 - LG PAL_I (TAPC-I701D) @@ -118,3 +121,5 @@ type=35 - Temic PAL_DK/SECAM_L (4012 FY5) type=36 - Temic NTSC (4136 FY5) type=37 - LG PAL (newer TAPC series) + type=38 - Philips PAL/SECAM multi (FM1216ME MK3) + type=39 - LG NTSC (newer TAPC series) diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/Documentation/video4linux/bttv/Cards linux-2.5-bk/Documentation/video4linux/bttv/Cards --- linux-2.5.45/Documentation/video4linux/bttv/Cards Wed Oct 30 16:42:19 2002 +++ linux-2.5-bk/Documentation/video4linux/bttv/Cards Thu Oct 31 22:12:36 2002 @@ -157,6 +157,7 @@ Flyvideo 2000S (Bt878) w/Stereo TV (Package incl. LR91 daughterboard) LR91 = Stereo daughter card for LR90 LR97 = Flyvideo DVBS + LR99 Rev.E = Low profile card for OEM integration (only internal audio!) bt878 LR136 = Flyvideo 2100/3100 (Low profile, SAA7130/SAA7134) LR137 = Flyvideo DV2000/DV3000 (SAA7130/SAA7134 + IEEE1394) LR138 Rev.C= Flyvideo 2000 (SAA7130) @@ -236,6 +237,7 @@ Further Cards: PV-BT878P+rev.9B (Play TV Pro, opt. w/FM w/NICAM) PV-BT878P+rev.2F + PV-BT878P Rev.1D (bt878, capture only) Video Conferencing: PixelView Meeting PAK - (Model: PV-BT878P) @@ -273,6 +275,7 @@ WinView 601 (Bt848) WinView 610 (Zoran) WinFast2000 + WinFast2000 XP KNC One ------- @@ -282,15 +285,19 @@ TV-Station FM (+Radio) TV-Station RDS (+RDS) -Provideo PV951 --------------- - These are sold as: + newer Cards have saa7134, but model name stayed the same? + +Provideo +-------- + PV951 or PV-951 (also are sold as: Boeder TV-FM Video Capture Card Titanmedia Supervision TV-2400 Provideo PV951 TF 3DeMon PV951 MediaForte TV-Vision PV951 Yoko PV951 + ) + PV-148 (capture only) Highscreen ---------- @@ -320,15 +327,47 @@ PCB PCI-ID Model-Name Eeprom Tuner Sound Country -------------------------------------------------------------------- - M1A8-A -- AVer TV-Phone FM1216 -- + M101.C ISA ! + M108-B Bt848 -- FR1236 US (2),(3) + M1A8-A Bt848 AVer TV-Phone FM1216 -- M168-T 1461:0003 AVerTV Studio 48:17 FM1216 TDA9840T D (1) w/FM w/Remote M168-U 1461:0004 TVCapture98 40:11 FI1216 -- D w/Remote M168II-B 1461:0003 Medion MD9592 48:16 FM1216 TDA9873H D w/FM (1) Daughterboard MB68-A with TDA9820T and TDA9840T + (2) Sony NE41S soldered (stereo sound?) + (3) Daughterboard M118-A w/ pic 16c54 and 4 MHz quartz + + US site has different drivers for (as of 09/2002): + EZ Capture/InterCam PCI (BT-848 chip) + EZ Capture/InterCam PCI (BT-878 chip) + TV-Phone (BT-848 chip) + TV98 (BT-848 chip) + TV98 With Remote (BT-848 chip) + TV98 (BT-878 chip) + TV98 With Remote (BT-878) + TV/FM98 (BT-878 chip) + AVerTV + AverTV Stereo + AVerTV Studio + + DE hat diverse Treiber fuer diese Modelle (Stand 09/2002): + TVPhone (848) mit Philips tuner FR12X6 (w/ FM radio) + TVPhone (848) mit Philips tuner FM12X6 (w/ FM radio) + TVCapture (848) w/Philips tuner FI12X6 + TVCapture (848) non-Philips tuner + TVCapture98 (Bt878) + TVPhone98 (Bt878) + AVerTV und TVCapture98 w/VCR (Bt 878) + AVerTVStudio und TVPhone98 w/VCR (Bt878) + AVerTV GO Serie (Kein SVideo Input) + AVerTV98 (BT-878 chip) + AVerTV98 mit Fernbedienung (BT-878 chip) + AVerTV/FM98 (BT-878 chip) Aimslab ------- + Video Highway or "Video Highway TR200" (ISA) Video Highway Xtreme (aka "VHX") (Bt848, FM w/ TEA5757) IXMicro (former: IMS=Integrated Micro Solutions) @@ -364,6 +403,9 @@ LR74 is a newer PCB revision of ceb105 (both incl. connector for Active Radio Upgrade) + Cinergy 400 (saa7134), "E877 11(S)", "PM820092D" printed on PCB + Cinergy 600 (saa7134) + Technisat --------- Discos ADR PC-Karte ISA (no TV!) @@ -373,7 +415,7 @@ Mediafocus I (zr36120/zr36125, drp3510, Sat. analog + ADR Radio) Mediafocus II (saa7146, Sat. analog) SatADR Rev 2.1 (saa7146a, saa7113h, stv0056a, msp3400c, drp3510a, BSKE3-307A) - SkyStar 1 DVB (AV7110) + SkyStar 1 DVB (AV7110) = Technotrend Premium SkyStar 2 DVB (B2C2) (=Sky2PC) Siemens @@ -387,6 +429,9 @@ Powercolor ---------- MTV878 + Package comes with different contents: + a) pcb "MTV878" (CARD=75) + b) Pixelview Rev. 4_ MTV878R w/Remote Control MTV878F w/Remote Control w/FM radio @@ -394,10 +439,14 @@ -------- Mirovideo PCTV (Bt848) Mirovideo PCTV SE (Bt848) - Mirovideo PCTV Pro (Bt848 + Daughterboard) + Mirovideo PCTV Pro (Bt848 + Daughterboard for TV Stereo and FM) + Studio PCTV Rave (Bt848 Version = Mirovideo PCTV) Studio PCTV Rave (Bt878 package w/o infrared) Studio PCTV (Bt878) Studio PCTV Pro (Bt878 stereo w/ FM) + Pinnacle PCTV (Bt878, MT2032) + Pinnacle PCTV Pro (Bt878, MT2032) + Pinncale PCTV Sat M(J)PEG capture and playback: DC1+ (ISA) @@ -495,8 +544,10 @@ STB --- - TV PCI (Temic4032FY5, tda9850??) - other variants? + STB bt878 == Gateway 6000704 + STB Gateway 6000699 (bt848) + STB Gateway 6000402 (bt848) + STB TV130 PCI Videologic ---------- @@ -507,6 +558,16 @@ ------------ TT-SAT PCI (PCB "Sat-PCI Rev.:1.3.1"; zr36125, vpx3225d, stc0056a, Tuner:BSKE6-155A TT-DVB-Sat + This card is sold as OEM from: + Siemens DVB-s Card + Hauppauge WinTV DVB-S + Technisat SkyStar 1 DVB + Galaxis DVB Sat + Now this card is called TT-PCline Premium Family + TT-Budget + This card is sold as OEM from: + Hauppauge WinTV Nova + Satelco Standard PCI (DVB-S) TT-DVB-C PCI Teles @@ -546,10 +607,13 @@ Hauppauge --------- many many WinTV models ... - WinTV DVBs - WinTV NOVA + WinTV DVBs = Tehcnotrend Premium + WinTV NOVA = Technotrend Budget WinTV NOVA-CI WinTV-Nexus-s + WinTV PVR + WinTV PVR 250 + WinTV PVR 450 Matrix-Vision ------------- @@ -615,7 +679,7 @@ NoBrand ------- - TV Excel = Australian Name for "PV-BT878P+ 8E" or so + TV Excel = Australian Name for "PV-BT878P+ 8E" or "878TV Rev.3_" Mach www.machspeed.com ---- @@ -638,9 +702,51 @@ Satelco ------- TV-FM =KNC1 saa7134 + Standard PCI (DVB-S) = Technotrend Budget + Standard PCI (DVB-S) w/ CI + Satelco Hoghend PCI (DVB-S) = Technotrend Premium + Sensoray www.sensoray.com -------- Sensoray 311 (PC/104 bus) Sensoray 611 (PCI) +CEI (Chartered Electronics Industries Pte Ltd [CEI] [FCC ID HBY]) +--- + TV Tuner - HBY-33A-RAFFLES Brooktree Bt848KPF + Philips + TV Tuner MG9910 - HBY33A-TVO CEI + Philips SAA7110 + OKI M548262 + ST STV8438CV + Primetime TV (ISA) + acquired by Singapore Technologies + now operating as Chartered Semiconductor Manufacturing + Manufacturer of video cards is listed as: + Cogent Electronics Industries [CEI] + +AITech +------ + AITech WaveWatcher TV-PCI = LR26 + WaveWatcher TVR-202 TV/FM Radio Card (ISA) + +MAXRON +------ + Maxron MaxTV/FM Radio (KW-TV878-FNT) = Kworld or JW-TV878-FBK + +www.ids-imaging.de +------------------ + Falcon Series (capture only) + In USA: http://www.theimagingsource.com/ + DFG/LC1 + +www.sknet-web.co.jp +------------------- + SKnet Monster TV (saa7134) + +A-Max www.amaxhk.com (Colormax, Amax, Napa) +------------------- + APAC Viewcomp 878 + +Cybertainment +------------- + CyberMail AV Video Email Kit w/ PCI Capture Card (capture only) + CyberMail Xtreme + These are Flyvideo diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/Documentation/video4linux/bttv/README linux-2.5-bk/Documentation/video4linux/bttv/README --- linux-2.5.45/Documentation/video4linux/bttv/README Wed Oct 30 16:41:43 2002 +++ linux-2.5-bk/Documentation/video4linux/bttv/README Thu Oct 31 22:12:36 2002 @@ -76,6 +76,12 @@ video but no sound you've very likely specified the wrong (or no) card type. A list of supported cards is in CARDLIST. +For the WinTV/PVR you need one firmware file from the driver CD: +hcwamc.rbf. The file is in the pvr45xxx.exe archive (self-extracting +zip file, unzip can unpack it). Put it into the /etc/pvr directory or +use the firm_altera= insmod option to point the driver to the +location of the file. + If your card isn't listed in CARDLIST or if you have trouble making audio work, you should read the Sound-FAQ. diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/Documentation/video4linux/bttv/README.freeze linux-2.5-bk/Documentation/video4linux/bttv/README.freeze --- linux-2.5.45/Documentation/video4linux/bttv/README.freeze Wed Oct 30 16:41:38 2002 +++ linux-2.5-bk/Documentation/video4linux/bttv/README.freeze Thu Oct 31 22:12:36 2002 @@ -61,6 +61,9 @@ other ----- +If you use some binary-only yunk (like nvidia module) try to reproduce +the problem without. + IRQ sharing is known to cause problems in some cases. It works just fine in theory and many configurations. Neverless it might be worth a try to shuffle around the PCI cards to give bttv another IRQ or make diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/Documentation/video4linux/bttv/Tuners linux-2.5-bk/Documentation/video4linux/bttv/Tuners --- linux-2.5.45/Documentation/video4linux/bttv/Tuners Wed Oct 30 16:41:31 2002 +++ linux-2.5-bk/Documentation/video4linux/bttv/Tuners Thu Oct 31 22:12:36 2002 @@ -1,3 +1,16 @@ +1) Tuner Programming +==================== +There are some flavors of Tuner programming APIs. +These differ mainly by the bandswitch byte. + + L= LG_API (VHF_LO=0x01, VHF_HI=0x02, UHF=0x08, radio=0x04) + P= PHILIPS_API (VHF_LO=0xA0, VHF_HI=0x90, UHF=0x30, radio=0x04) + T= TEMIC_API (VHF_LO=0x02, VHF_HI=0x04, UHF=0x01) + A= ALPS_API (VHF_LO=0x14, VHF_HI=0x12, UHF=0x11) + M= PHILIPS_MK3 (VHF_LO=0x01, VHF_HI=0x02, UHF=0x04, radio=0x19) + +2) Tuner Manufacturers +====================== SAMSUNG Tuner identification: (e.g. TCPM9091PD27) TCP [ABCJLMNQ] 90[89][125] [DP] [ACD] 27 [ABCD] @@ -24,6 +37,8 @@ [ABCD]: 3-wire/I2C tuning, 2-band/3-band + These Tuners are PHILIPS_API compatible. + Philips Tuner identification: (e.g. FM1216MF) F[IRMQ]12[1345]6{MF|ME|MP} F[IRMQ]: @@ -39,15 +54,17 @@ 1246: PAL I 1256: Pal DK {MF|ME|MP} - MF: w/ Secam + MF: BG LL w/ Secam (Multi France) ME: BG DK I LL (Multi Europe) MP: BG DK I (Multi PAL) MR: BG DK M (?) MG: BG DKI M (?) + MK2 series PHILIPS_API, most tuners are compatible to this one ! + MK3 series introduced in 2002 w/ PHILIPS_MK3_API Temic Tuner identification: (.e.g 4006FH5) 4[01][0136][269]F[HYNR]5 - 40x2: Tuner (5V/33V), different I2C programming from Philips ! + 40x2: Tuner (5V/33V), TEMIC_API. 40x6: Tuner 5V 41xx: Tuner compact 40x9: Tuner+FM compact @@ -62,6 +79,7 @@ FN5: multistandard FR5: w/ FM radio 3X xxxx: order number with specific connector + Note: Only 40x2 series has TEMIC_API, all newer tuners have PHILIPS_API. LG Innotek Tuner: TPI8NSR11 : NTSC J/M (TPI8NSR01 w/FM) (P,210/497) @@ -78,12 +96,6 @@ TADC-H002F: NTSC (L,175/410?; 2-B, C-W+11, W+12-69) TADC-M201D: PAL D/K+B/G+I (L,143/425) (sound control at I2C address 0xc8) TADC-T003F: NTSC Taiwan (L,175/410?; 2-B, C-W+11, W+12-69) - - (API,Lo-Hi-takeover/Hi-UHF-takeover) - I2C APIs: - L= LG programming (VHF_LO=0x01, VHF_HI=0x02, UHF=0x08, radio=0x04) - P= Philips progr. (VHF_LO=0xA0, VHF_HI=0x90, UHF=0x30, radio=0x04) - T= Temic progr. (VHF_LO=0x02, VHF_HI=0x04, UHF=0x01) Suffix: P= Standard phono female socket D= IEC female socket @@ -93,3 +105,11 @@ TCL2002MB-1 : PAL BG + DK =TUNER_LG_PAL_NEW_TAPC TCL2002MB-1F: PAL BG + DK w/FM =PHILIPS_PAL TCL2002MI-2 : PAL I = ?? + +ALPS Tuners: + Most are LG_API compatible + TSCH6 has ALPS_API (TSCH5 ?) + TSBE1 has extra API 05,02,08 Control_byte=0xCB Source:(1) + +Lit. +(1) conexant100029b-PCI-Decoder-ApplicationNote.pdf diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/alpha/kernel/irq.c linux-2.5-bk/arch/alpha/kernel/irq.c --- linux-2.5.45/arch/alpha/kernel/irq.c Wed Oct 30 16:43:36 2002 +++ linux-2.5-bk/arch/alpha/kernel/irq.c Thu Oct 31 22:12:36 2002 @@ -528,7 +528,7 @@ #else for (j = 0; j < NR_CPUS; j++) if (cpu_online(j)) - seq_printf(p, "%10u ", kstat.irqs[j][i]); + seq_printf(p, "%10u ", kstat_cpu(i).irqs[j]); #endif seq_printf(p, " %14s", irq_desc[i].handler->typename); seq_printf(p, " %c%s", @@ -590,7 +590,7 @@ } irq_enter(); - kstat.irqs[cpu][irq]++; + kstat_cpu(i).irqs[irq]++; spin_lock_irq(&desc->lock); /* mask also the higher prio events */ desc->handler->ack(irq); /* diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/alpha/kernel/irq_alpha.c linux-2.5-bk/arch/alpha/kernel/irq_alpha.c --- linux-2.5.45/arch/alpha/kernel/irq_alpha.c Wed Oct 30 16:42:20 2002 +++ linux-2.5-bk/arch/alpha/kernel/irq_alpha.c Thu Oct 31 22:12:36 2002 @@ -59,7 +59,7 @@ smp_percpu_timer_interrupt(®s); cpu = smp_processor_id(); if (cpu != boot_cpuid) { - kstat.irqs[cpu][RTC_IRQ]++; + kstat_cpu(cpu).irqs[RTC_IRQ]++; } else { handle_irq(RTC_IRQ, ®s); } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/alpha/kernel/irq_impl.h linux-2.5-bk/arch/alpha/kernel/irq_impl.h --- linux-2.5.45/arch/alpha/kernel/irq_impl.h Wed Oct 30 16:43:47 2002 +++ linux-2.5-bk/arch/alpha/kernel/irq_impl.h Thu Oct 31 22:12:36 2002 @@ -10,6 +10,7 @@ #include #include +#include #define RTC_IRQ 8 diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/arm/kernel/irq.c linux-2.5-bk/arch/arm/kernel/irq.c --- linux-2.5.45/arch/arm/kernel/irq.c Wed Oct 30 16:41:31 2002 +++ linux-2.5-bk/arch/arm/kernel/irq.c Thu Oct 31 22:12:36 2002 @@ -217,7 +217,7 @@ desc->triggered = 1; - kstat.irqs[cpu][irq]++; + kstat_cpu(cpu).irqs[irq]++; action = desc->action; if (action) @@ -253,7 +253,7 @@ */ desc->running = 1; - kstat.irqs[cpu][irq]++; + kstat_cpu(cpu).irqs[irq]++; do { struct irqaction *action; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/cris/kernel/irq.c linux-2.5-bk/arch/cris/kernel/irq.c --- linux-2.5.45/arch/cris/kernel/irq.c Wed Oct 30 16:43:44 2002 +++ linux-2.5-bk/arch/cris/kernel/irq.c Thu Oct 31 22:12:37 2002 @@ -234,7 +234,7 @@ if (!action) continue; seq_printf(p, "%2d: %10u %c %s", - i, kstat.irqs[0][i], + i, kstat_cpu(0).irqs[i], (action->flags & SA_INTERRUPT) ? '+' : ' ', action->name); for (action = action->next; action; action = action->next) { @@ -261,7 +261,7 @@ cpu = smp_processor_id(); irq_enter(cpu); - kstat.irqs[cpu][irq]++; + kstat_cpu(cpu).irqs[irq]++; action = irq_action[irq]; if (action) { diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/i386/kernel/cpu/proc.c linux-2.5-bk/arch/i386/kernel/cpu/proc.c --- linux-2.5.45/arch/i386/kernel/cpu/proc.c Wed Oct 30 16:43:39 2002 +++ linux-2.5-bk/arch/i386/kernel/cpu/proc.c Thu Oct 31 22:12:37 2002 @@ -74,6 +74,13 @@ /* Cache size */ if (c->x86_cache_size >= 0) seq_printf(m, "cache size\t: %d KB\n", c->x86_cache_size); +#ifdef CONFIG_SMP + if (cpu_has_ht) { + extern int phys_proc_id[NR_CPUS]; + seq_printf(m, "physical id\t: %d\n", phys_proc_id[n]); + seq_printf(m, "siblings\t: %d\n", smp_num_siblings); + } +#endif /* 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 || cpu_has_fpu); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/i386/kernel/edd.c linux-2.5-bk/arch/i386/kernel/edd.c --- linux-2.5.45/arch/i386/kernel/edd.c Wed Oct 30 16:42:26 2002 +++ linux-2.5-bk/arch/i386/kernel/edd.c Thu Oct 31 22:12:37 2002 @@ -44,7 +44,7 @@ #include #include #include -#include +#include #include #include #include @@ -63,20 +63,9 @@ #define left (count - (p - buf) - 1) -/* - * bios_dir may go away completely, - * and it definitely won't be at the root - * of driverfs forever. - */ -static struct driver_dir_entry bios_dir = { - .name = "bios", - .mode = (S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO), -}; - struct edd_device { - char name[EDD_DEVICE_NAME_SIZE]; struct edd_info *info; - struct driver_dir_entry dir; + struct kobject kobj; }; struct edd_attribute { @@ -112,13 +101,13 @@ } #define to_edd_attr(_attr) container_of(_attr,struct edd_attribute,attr) -#define to_edd_device(_dir) container_of(_dir,struct edd_device,dir) +#define to_edd_device(obj) container_of(obj,struct edd_device,kobj) static ssize_t -edd_attr_show(struct driver_dir_entry *dir, struct attribute *attr, +edd_attr_show(struct kobject * kobj, struct attribute *attr, char *buf, size_t count, loff_t off) { - struct edd_device *dev = to_edd_device(dir); + struct edd_device *dev = to_edd_device(kobj); struct edd_attribute *edd_attr = to_edd_attr(attr); ssize_t ret = 0; @@ -127,7 +116,7 @@ return ret; } -static struct driverfs_ops edd_attr_ops = { +static struct sysfs_ops edd_attr_ops = { .show = edd_attr_show, }; @@ -586,89 +575,26 @@ static EDD_DEVICE_ATTR(host_bus, 0444, edd_show_host_bus, edd_has_edd30); -static struct edd_attribute * def_attrs[] = { - &edd_attr_raw_data, - &edd_attr_version, - &edd_attr_extensions, - &edd_attr_info_flags, - &edd_attr_sectors, - &edd_attr_default_cylinders, - &edd_attr_default_heads, - &edd_attr_default_sectors_per_track, - &edd_attr_interface, - &edd_attr_host_bus, +static struct attribute * def_attrs[] = { + &edd_attr_raw_data.attr, + &edd_attr_version.attr, + &edd_attr_extensions.attr, + &edd_attr_info_flags.attr, + &edd_attr_sectors.attr, + &edd_attr_default_cylinders.attr, + &edd_attr_default_heads.attr, + &edd_attr_default_sectors_per_track.attr, + &edd_attr_interface.attr, + &edd_attr_host_bus.attr, NULL, }; -/* edd_get_devpath_length(), edd_fill_devpath(), and edd_device_link() - were taken from linux/drivers/base/fs/device.c. When these - or similar are exported to generic code, remove these. -*/ - -static int -edd_get_devpath_length(struct device *dev) -{ - int length = 1; - struct device *parent = dev; - - /* walk up the ancestors until we hit the root. - * Add 1 to strlen for leading '/' of each level. - */ - do { - length += strlen(parent->bus_id) + 1; - parent = parent->parent; - } while (parent); - return length; -} - -static void -edd_fill_devpath(struct device *dev, char *path, int length) -{ - struct device *parent; - --length; - for (parent = dev; parent; parent = parent->parent) { - int cur = strlen(parent->bus_id); - - /* back up enough to print this bus id with '/' */ - length -= cur; - strncpy(path + length, parent->bus_id, cur); - *(path + --length) = '/'; - } -} - -static int -edd_device_symlink(struct edd_device *edev, struct device *dev, char *name) -{ - char *path; - int length; - int error = 0; - - if (!dev->bus || !name) - return 0; - - length = edd_get_devpath_length(dev); - - /* now add the path from the edd_device directory - * It should be '../..' (one to get to the 'bios' directory, - * and one to get to the root of the fs.) - */ - length += strlen("../../root"); - - if (length > PATH_MAX) - return -ENAMETOOLONG; - - if (!(path = kmalloc(length, GFP_KERNEL))) - return -ENOMEM; - memset(path, 0, length); - - /* our relative position */ - strcpy(path, "../../root"); +static struct subsystem edd_subsys = { + .kobj = { .name = "edd" }, + .sysfs_ops = &edd_attr_ops, + .default_attrs = def_attrs, +}; - edd_fill_devpath(dev, path, length); - error = driverfs_create_symlink(&edev->dir, name, path); - kfree(path); - return error; -} /** * edd_dev_is_type() - is this EDD device a 'type' device? @@ -721,7 +647,7 @@ struct pci_dev *pci_dev = edd_get_pci_dev(edev); if (!pci_dev) return 1; - return edd_device_symlink(edev, &pci_dev->dev, "pci_dev"); + return sysfs_create_link(&edev->kobj,&pci_dev->dev.kobj,"pci_dev"); } /** @@ -833,61 +759,16 @@ return 1; get_device(&sdev->sdev_driverfs_dev); - rc = edd_device_symlink(edev, &sdev->sdev_driverfs_dev, "disc"); + rc = sysfs_create_link(&edev->kobj,&sdev->sdev_driverfs_dev.kobj, "disc"); put_device(&sdev->sdev_driverfs_dev); return rc; } -static inline int -edd_create_file(struct edd_device *edev, struct edd_attribute *attr) -{ - return driverfs_create_file(&attr->attr, &edev->dir); -} - static inline void edd_device_unregister(struct edd_device *edev) { - driverfs_remove_dir(&edev->dir); -} - -static int -edd_populate_dir(struct edd_device *edev) -{ - struct edd_attribute *attr; - int i; - int error = 0; - - for (i = 0; (attr=def_attrs[i]); i++) { - if (!attr->test || (attr->test && !attr->test(edev))) { - if ((error = edd_create_file(edev, attr))) { - break; - } - } - } - - if (error) - return error; - - edd_create_symlink_to_pcidev(edev); - edd_create_symlink_to_scsidev(edev); - - return 0; -} - -static int -edd_make_dir(struct edd_device *edev) -{ - int error; - - edev->dir.name = edev->name; - edev->dir.mode = (S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO); - edev->dir.ops = &edd_attr_ops; - - error = driverfs_create_dir(&edev->dir, &bios_dir); - if (!error) - error = edd_populate_dir(edev); - return error; + kobject_unregister(&edev->kobj); } static int @@ -899,9 +780,15 @@ return 1; memset(edev, 0, sizeof (*edev)); edd_dev_set_info(edev, &edd[i]); - snprintf(edev->name, EDD_DEVICE_NAME_SIZE, "int13_dev%02x", + kobject_init(&edev->kobj); + snprintf(edev->kobj.name, EDD_DEVICE_NAME_SIZE, "int13_dev%02x", edd[i].device); - error = edd_make_dir(edev); + edev->kobj.subsys = &edd_subsys; + error = kobject_register(&edev->kobj); + if (!error) { + edd_create_symlink_to_pcidev(edev); + edd_create_symlink_to_scsidev(edev); + } return error; } @@ -926,7 +813,7 @@ return 1; } - rc = driverfs_create_dir(&bios_dir, NULL); + rc = firmware_register(&edd_subsys); if (rc) return rc; @@ -943,12 +830,9 @@ edd_devices[i] = edev; } - if (rc) { - driverfs_remove_dir(&bios_dir); - return rc; - } - - return 0; + if (rc) + firmware_unregister(&edd_subsys); + return rc; } static void __exit @@ -963,8 +847,7 @@ kfree(edev); } } - - driverfs_remove_dir(&bios_dir); + firmware_unregister(&edd_subsys); } late_initcall(edd_init); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/i386/kernel/entry.S linux-2.5-bk/arch/i386/kernel/entry.S --- linux-2.5.45/arch/i386/kernel/entry.S Wed Oct 30 16:42:21 2002 +++ linux-2.5-bk/arch/i386/kernel/entry.S Thu Oct 31 22:12:37 2002 @@ -740,6 +740,7 @@ .long sys_epoll_create .long sys_epoll_ctl /* 255 */ .long sys_epoll_wait + .long sys_remap_file_pages .rept NR_syscalls-(.-sys_call_table)/4 diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/i386/kernel/irq.c linux-2.5-bk/arch/i386/kernel/irq.c --- linux-2.5.45/arch/i386/kernel/irq.c Wed Oct 30 16:41:50 2002 +++ linux-2.5-bk/arch/i386/kernel/irq.c Thu Oct 31 22:12:37 2002 @@ -153,7 +153,7 @@ for (j = 0; j < NR_CPUS; j++) if (cpu_online(j)) p += seq_printf(p, "%10u ", - kstat.irqs[j][i]); + kstat_cpu(j).irqs[i]); #endif seq_printf(p, " %14s", irq_desc[i].handler->typename); seq_printf(p, " %s", action->name); @@ -345,7 +345,7 @@ } } #endif - kstat.irqs[cpu][irq]++; + kstat_cpu(cpu).irqs[irq]++; spin_lock(&desc->lock); desc->handler->ack(irq); /* diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/i386/kernel/process.c linux-2.5-bk/arch/i386/kernel/process.c --- linux-2.5.45/arch/i386/kernel/process.c Wed Oct 30 16:41:30 2002 +++ linux-2.5-bk/arch/i386/kernel/process.c Thu Oct 31 22:12:37 2002 @@ -247,6 +247,7 @@ struct task_struct *tsk = current; memset(tsk->thread.debugreg, 0, sizeof(unsigned long)*8); + memset(tsk->thread.tls_array, 0, sizeof(tsk->thread.tls_array)); /* * Forget coprocessor state.. */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/i386/kernel/smpboot.c linux-2.5-bk/arch/i386/kernel/smpboot.c --- linux-2.5.45/arch/i386/kernel/smpboot.c Wed Oct 30 16:42:55 2002 +++ linux-2.5-bk/arch/i386/kernel/smpboot.c Thu Oct 31 22:12:37 2002 @@ -58,7 +58,7 @@ /* Number of siblings per CPU package */ int smp_num_siblings = 1; -int __initdata phys_proc_id[NR_CPUS]; /* Package ID of each logical CPU */ +int phys_proc_id[NR_CPUS]; /* Package ID of each logical CPU */ /* Bitmask of currently online CPUs */ unsigned long cpu_online_map; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/i386/kernel/sys_i386.c linux-2.5-bk/arch/i386/kernel/sys_i386.c --- linux-2.5.45/arch/i386/kernel/sys_i386.c Wed Oct 30 16:42:54 2002 +++ linux-2.5-bk/arch/i386/kernel/sys_i386.c Thu Oct 31 22:12:37 2002 @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -248,91 +249,68 @@ } #ifdef CONFIG_HUGETLB_PAGE -#define HPAGE_ALIGN(x) (((unsigned long)x + (HPAGE_SIZE -1)) & HPAGE_MASK) -extern long sys_munmap(unsigned long, size_t); /* get_addr function gets the currently unused virtaul range in - * current process's address space. It returns the LARGE_PAGE_SIZE + * current process's address space. It returns the HPAGE_SIZE * aligned address (in cases of success). Other kernel generic - * routines only could gurantee that allocated address is PAGE_SIZSE aligned. + * routines only could gurantee that allocated address is PAGE_SIZE aligned. */ -static unsigned long -get_addr(unsigned long addr, unsigned long len) +static unsigned long get_addr(unsigned long addr, unsigned long len) { - struct vm_area_struct *vma; + struct vm_area_struct *vma; if (addr) { - addr = HPAGE_ALIGN(addr); + addr = (addr + HPAGE_SIZE - 1) & HPAGE_MASK; vma = find_vma(current->mm, addr); - if (((TASK_SIZE - len) >= addr) && - (!vma || addr + len <= vma->vm_start)) + if (TASK_SIZE > addr + len && !(vma && addr + len >= vma->vm_start)) goto found_addr; } - addr = HPAGE_ALIGN(TASK_UNMAPPED_BASE); - for (vma = find_vma(current->mm, addr); ; vma = vma->vm_next) { - if (TASK_SIZE - len < addr) - return -ENOMEM; - if (!vma || ((addr + len) < vma->vm_start)) + addr = TASK_UNMAPPED_BASE; + for (vma = find_vma(current->mm, addr); TASK_SIZE > addr + len; vma = vma->vm_next) { + if (!vma || addr + len < vma->vm_start) goto found_addr; - addr = HPAGE_ALIGN(vma->vm_end); + addr = (vma->vm_end + HPAGE_SIZE - 1) & HPAGE_MASK; } + return -ENOMEM; found_addr: return addr; } -asmlinkage unsigned long -sys_alloc_hugepages(int key, unsigned long addr, unsigned long len, int prot, int flag) +asmlinkage unsigned long sys_alloc_hugepages(int key, unsigned long addr, unsigned long len, int prot, int flag) { struct mm_struct *mm = current->mm; unsigned long raddr; int retval = 0; extern int alloc_hugetlb_pages(int, unsigned long, unsigned long, int, int); - if (!(cpu_has_pse)) - return -EINVAL; - if (key < 0) - return -EINVAL; - if (len & (HPAGE_SIZE - 1)) + if (!cpu_has_pse || key < 0 || len & ~HPAGE_MASK) return -EINVAL; down_write(&mm->mmap_sem); raddr = get_addr(addr, len); - if (raddr == -ENOMEM) - goto raddr_out; - retval = alloc_hugetlb_pages(key, raddr, len, prot, flag); - -raddr_out: up_write(&mm->mmap_sem); - if (retval < 0) - return (unsigned long) retval; - return raddr; + if (raddr != -ENOMEM) + retval = alloc_hugetlb_pages(key, raddr, len, prot, flag); + up_write(&mm->mmap_sem); + return (retval < 0) ? (unsigned long)retval : raddr; } -asmlinkage int -sys_free_hugepages(unsigned long addr) +asmlinkage int sys_free_hugepages(unsigned long addr) { struct mm_struct *mm = current->mm; - struct vm_area_struct *vma; - int retval; - extern int free_hugepages(struct vm_area_struct *); + struct vm_area_struct *vma; + int retval; vma = find_vma(current->mm, addr); - if ((!vma) || (!is_vm_hugetlb_page(vma)) || (vma->vm_start!=addr)) + if (!vma || !(vma->vm_flags & VM_HUGETLB) || vma->vm_start != addr) return -EINVAL; down_write(&mm->mmap_sem); - spin_lock(&mm->page_table_lock); - retval = free_hugepages(vma); - spin_unlock(&mm->page_table_lock); + retval = do_munmap(vma->vm_mm, addr, vma->vm_end - addr); up_write(&mm->mmap_sem); return retval; } - #else - -asmlinkage unsigned long -sys_alloc_hugepages(int key, unsigned long addr, size_t len, int prot, int flag) +asmlinkage unsigned long sys_alloc_hugepages(int key, unsigned long addr, size_t len, int prot, int flag) { return -ENOSYS; } -asmlinkage int -sys_free_hugepages(unsigned long addr) +asmlinkage int sys_free_hugepages(unsigned long addr) { return -ENOSYS; } - #endif diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/i386/kernel/timers/timer_pit.c linux-2.5-bk/arch/i386/kernel/timers/timer_pit.c --- linux-2.5.45/arch/i386/kernel/timers/timer_pit.c Wed Oct 30 16:43:39 2002 +++ linux-2.5-bk/arch/i386/kernel/timers/timer_pit.c Thu Oct 31 22:12:37 2002 @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/i386/mach-visws/visws_apic.c linux-2.5-bk/arch/i386/mach-visws/visws_apic.c --- linux-2.5.45/arch/i386/mach-visws/visws_apic.c Wed Oct 30 16:41:43 2002 +++ linux-2.5-bk/arch/i386/mach-visws/visws_apic.c Thu Oct 31 22:12:37 2002 @@ -324,7 +324,7 @@ /* * handle this 'virtual interrupt' as a Cobalt one now. */ - kstat.irqs[smp_processor_id()][irq]++; + kstat_cpu(smp_processor_id()).irqs[irq]++; do_cobalt_IRQ(realirq, regs); spin_lock(&irq_controller_lock); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/i386/mm/hugetlbpage.c linux-2.5-bk/arch/i386/mm/hugetlbpage.c --- linux-2.5.45/arch/i386/mm/hugetlbpage.c Wed Oct 30 16:43:42 2002 +++ linux-2.5-bk/arch/i386/mm/hugetlbpage.c Thu Oct 31 22:12:38 2002 @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -17,7 +18,7 @@ #include #include -static struct vm_operations_struct hugetlb_vm_ops; +struct vm_operations_struct hugetlb_vm_ops; struct list_head htlbpage_freelist; spinlock_t htlbpage_lock = SPIN_LOCK_UNLOCKED; extern long htlbpagemem; @@ -30,8 +31,7 @@ int key; } htlbpagek[MAX_ID]; -static struct inode * -find_key_inode(int key) +static struct inode *find_key_inode(int key) { int i; @@ -41,8 +41,8 @@ } return NULL; } -static struct page * -alloc_hugetlb_page(void) + +static struct page *alloc_hugetlb_page(void) { int i; struct page *page; @@ -63,36 +63,7 @@ return page; } -static void -free_hugetlb_page(struct page *page) -{ - spin_lock(&htlbpage_lock); - if ((page->mapping != NULL) && (page_count(page) == 2)) { - struct inode *inode = page->mapping->host; - int i; - - ClearPageDirty(page); - remove_from_page_cache(page); - set_page_count(page, 1); - if ((inode->i_size -= HPAGE_SIZE) == 0) { - for (i = 0; i < MAX_ID; i++) - if (htlbpagek[i].key == inode->i_ino) { - htlbpagek[i].key = 0; - htlbpagek[i].in = NULL; - break; - } - kfree(inode); - } - } - if (put_page_testzero(page)) { - list_add(&page->list, &htlbpage_freelist); - htlbpagemem++; - } - spin_unlock(&htlbpage_lock); -} - -static pte_t * -huge_pte_alloc(struct mm_struct *mm, unsigned long addr) +static pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr) { pgd_t *pgd; pmd_t *pmd = NULL; @@ -102,8 +73,7 @@ return (pte_t *) pmd; } -static pte_t * -huge_pte_offset(struct mm_struct *mm, unsigned long addr) +static pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr) { pgd_t *pgd; pmd_t *pmd = NULL; @@ -115,9 +85,7 @@ #define mk_pte_huge(entry) {entry.pte_low |= (_PAGE_PRESENT | _PAGE_PSE);} -static void -set_huge_pte(struct mm_struct *mm, struct vm_area_struct *vma, - struct page *page, pte_t * page_table, int write_access) +static void set_huge_pte(struct mm_struct *mm, struct vm_area_struct *vma, struct page *page, pte_t * page_table, int write_access) { pte_t entry; @@ -130,24 +98,17 @@ entry = pte_mkyoung(entry); mk_pte_huge(entry); set_pte(page_table, entry); - return; } -static int -anon_get_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma, - int write_access, pte_t * page_table) +static int anon_get_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma, int write_access, pte_t *page_table) { - struct page *page; - - page = alloc_hugetlb_page(); - if (page == NULL) - return -1; - set_huge_pte(mm, vma, page, page_table, write_access); - return 1; + struct page *page = alloc_hugetlb_page(); + if (page) + set_huge_pte(mm, vma, page, page_table, write_access); + return page ? 1 : -1; } -int -make_hugetlb_pages_present(unsigned long addr, unsigned long end, int flags) +int make_hugetlb_pages_present(unsigned long addr, unsigned long end, int flags) { int write; struct mm_struct *mm = current->mm; @@ -253,31 +214,61 @@ return i; } -void -zap_hugetlb_resources(struct vm_area_struct *mpnt) +void free_huge_page(struct page *page) { - struct mm_struct *mm = mpnt->vm_mm; - unsigned long len, addr, end; - pte_t *ptep; + BUG_ON(page_count(page)); + BUG_ON(page->mapping); + + INIT_LIST_HEAD(&page->list); + + spin_lock(&htlbpage_lock); + list_add(&page->list, &htlbpage_freelist); + htlbpagemem++; + spin_unlock(&htlbpage_lock); +} + +void huge_page_release(struct page *page) +{ + if (!put_page_testzero(page)) + return; + + free_huge_page(page); +} + +void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, unsigned long end) +{ + struct mm_struct *mm = vma->vm_mm; + unsigned long address; + pte_t *pte; struct page *page; - addr = mpnt->vm_start; - end = mpnt->vm_end; - len = end - addr; - do { - ptep = huge_pte_offset(mm, addr); - page = pte_page(*ptep); - pte_clear(ptep); - free_hugetlb_page(page); - addr += HPAGE_SIZE; - } while (addr < end); - mm->rss -= (len >> PAGE_SHIFT); - mpnt->vm_ops = NULL; - flush_tlb_range(mpnt, end - len, end); + BUG_ON(start & (HPAGE_SIZE - 1)); + BUG_ON(end & (HPAGE_SIZE - 1)); + + for (address = start; address < end; address += HPAGE_SIZE) { + pte = huge_pte_offset(mm, address); + page = pte_page(*pte); + huge_page_release(page); + pte_clear(pte); + } + mm->rss -= (end - start) >> PAGE_SHIFT; + flush_tlb_range(vma, start, end); } -static void -unlink_vma(struct vm_area_struct *mpnt) +void zap_hugepage_range(struct vm_area_struct *vma, unsigned long start, unsigned long length) +{ + struct mm_struct *mm = vma->vm_mm; + spin_lock(&mm->page_table_lock); + unmap_hugepage_range(vma, start, start + length); + spin_unlock(&mm->page_table_lock); +} + +void zap_hugetlb_resources(struct vm_area_struct *vma) +{ + zap_hugepage_range(vma, vma->vm_start, vma->vm_end); +} + +static void unlink_vma(struct vm_area_struct *mpnt) { struct mm_struct *mm = current->mm; struct vm_area_struct *vma; @@ -296,17 +287,7 @@ mm->map_count--; } -int -free_hugepages(struct vm_area_struct *mpnt) -{ - unlink_vma(mpnt); - zap_hugetlb_resources(mpnt); - kmem_cache_free(vm_area_cachep, mpnt); - return 1; -} - -static struct inode * -set_new_inode(unsigned long len, int prot, int flag, int key) +static struct inode *set_new_inode(unsigned long len, int prot, int flag, int key) { struct inode *inode; int i; @@ -336,8 +317,7 @@ return inode; } -static int -check_size_prot(struct inode *inode, unsigned long len, int prot, int flag) +static int check_size_prot(struct inode *inode, unsigned long len, int prot, int flag) { if (inode->i_uid != current->fsuid) return -1; @@ -348,15 +328,12 @@ return 0; } -static int -alloc_shared_hugetlb_pages(int key, unsigned long addr, unsigned long len, - int prot, int flag) +static int alloc_shared_hugetlb_pages(int key, unsigned long addr, unsigned long len, int prot, int flag) { struct mm_struct *mm = current->mm; struct vm_area_struct *vma; struct inode *inode; struct address_space *mapping; - struct page *page; int idx; int retval = -ENOMEM; int newalloc = 0; @@ -404,25 +381,10 @@ goto freeinode; } - spin_lock(&mm->page_table_lock); - do { - pte_t *pte = huge_pte_alloc(mm, addr); - if ((pte) && (pte_none(*pte))) { - idx = (addr - vma->vm_start) >> HPAGE_SHIFT; - page = find_get_page(mapping, idx); - if (page == NULL) { - page = alloc_hugetlb_page(); - if (page == NULL) - goto out; - add_to_page_cache(page, mapping, idx); - } - set_huge_pte(mm, vma, page, pte, - (vma->vm_flags & VM_WRITE)); - } else - goto out; - addr += HPAGE_SIZE; - } while (addr < vma->vm_end); - retval = 0; + retval = hugetlb_prefault(mapping, vma); + if (retval) + goto out; + vma->vm_flags |= (VM_HUGETLB | VM_RESERVED); vma->vm_ops = &hugetlb_vm_ops; spin_unlock(&mm->page_table_lock); @@ -457,9 +419,48 @@ return retval; } -static int -alloc_private_hugetlb_pages(int key, unsigned long addr, unsigned long len, - int prot, int flag) +int hugetlb_prefault(struct address_space *mapping, struct vm_area_struct *vma) +{ + struct mm_struct *mm = current->mm; + unsigned long addr; + int ret = 0; + + BUG_ON(vma->vm_start & ~HPAGE_MASK); + BUG_ON(vma->vm_end & ~HPAGE_MASK); + + spin_lock(&mm->page_table_lock); + for (addr = vma->vm_start; addr < vma->vm_end; addr += HPAGE_SIZE) { + unsigned long idx; + pte_t *pte = huge_pte_alloc(mm, addr); + struct page *page; + + if (!pte) { + ret = -ENOMEM; + goto out; + } + if (!pte_none(*pte)) + continue; + + idx = ((addr - vma->vm_start) >> HPAGE_SHIFT) + + (vma->vm_pgoff >> (HPAGE_SHIFT - PAGE_SHIFT)); + page = find_get_page(mapping, idx); + if (!page) { + page = alloc_hugetlb_page(); + if (!page) { + ret = -ENOMEM; + goto out; + } + add_to_page_cache(page, mapping, idx); + unlock_page(page); + } + set_huge_pte(mm, vma, page, pte, vma->vm_flags & VM_WRITE); + } +out: + spin_unlock(&mm->page_table_lock); + return ret; +} + +static int alloc_private_hugetlb_pages(int key, unsigned long addr, unsigned long len, int prot, int flag) { if (!capable(CAP_SYS_ADMIN)) { if (!in_group_p(0)) @@ -476,17 +477,14 @@ return 0; } -int -alloc_hugetlb_pages(int key, unsigned long addr, unsigned long len, int prot, - int flag) +int alloc_hugetlb_pages(int key, unsigned long addr, unsigned long len, int prot, int flag) { if (key > 0) return alloc_shared_hugetlb_pages(key, addr, len, prot, flag); return alloc_private_hugetlb_pages(key, addr, len, prot, flag); } -int -set_hugetlb_mem_size(int count) +int set_hugetlb_mem_size(int count) { int j, lcount; struct page *page, *map; @@ -529,7 +527,7 @@ map->flags &= ~(1 << PG_locked | 1 << PG_error | 1 << PG_referenced | 1 << PG_dirty | 1 << PG_active | 1 << PG_reserved | 1 << PG_private | 1<< PG_writeback); - set_page_count(page, 0); + set_page_count(map, 0); map++; } set_page_count(page, 1); @@ -538,6 +536,12 @@ return (int) htlbzone_pages; } -static struct vm_operations_struct hugetlb_vm_ops = { - .close = zap_hugetlb_resources, +static struct page * hugetlb_nopage(struct vm_area_struct * area, unsigned long address, int unused) +{ + BUG(); + return NULL; +} + +struct vm_operations_struct hugetlb_vm_ops = { + .nopage = hugetlb_nopage, }; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/i386/mm/init.c linux-2.5-bk/arch/i386/mm/init.c --- linux-2.5.45/arch/i386/mm/init.c Wed Oct 30 16:43:34 2002 +++ linux-2.5-bk/arch/i386/mm/init.c Thu Oct 31 22:12:38 2002 @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/i386/oprofile/Makefile linux-2.5-bk/arch/i386/oprofile/Makefile --- linux-2.5.45/arch/i386/oprofile/Makefile Wed Oct 30 16:43:39 2002 +++ linux-2.5-bk/arch/i386/oprofile/Makefile Thu Oct 31 22:12:38 2002 @@ -1,5 +1,3 @@ -vpath %.c = . $(TOPDIR)/drivers/oprofile - obj-$(CONFIG_OPROFILE) += oprofile.o DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ @@ -9,8 +7,6 @@ oprofile-objs := $(DRIVER_OBJS) init.o timer_int.o -ifdef CONFIG_X86_LOCAL_APIC -oprofile-objs += nmi_int.o op_model_athlon.o op_model_ppro.o -endif +oprofile-$(CONFIG_X86_LOCAL_APIC) += nmi_int.o op_model_athlon.o op_model_ppro.o include $(TOPDIR)/Rules.make diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/i386/oprofile/nmi_int.c linux-2.5-bk/arch/i386/oprofile/nmi_int.c --- linux-2.5.45/arch/i386/oprofile/nmi_int.c Wed Oct 30 16:43:43 2002 +++ linux-2.5-bk/arch/i386/oprofile/nmi_int.c Thu Oct 31 22:12:38 2002 @@ -135,9 +135,19 @@ static void nmi_cpu_shutdown(void * dummy) { + unsigned int v; int cpu = smp_processor_id(); struct op_msrs * msrs = &cpu_msrs[cpu]; + + /* restoring APIC_LVTPC can trigger an apic error because the delivery + * mode and vector nr combination can be illegal. That's by design: on + * power on apic lvt contain a zero vector nr which are legal only for + * NMI delivery mode. So inhibit apic err before restoring lvtpc + */ + v = apic_read(APIC_LVTERR); + apic_write(APIC_LVTERR, v | APIC_LVT_MASKED); apic_write(APIC_LVTPC, saved_lvtpc[cpu]); + apic_write(APIC_LVTERR, v); nmi_restore_registers(msrs); } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/ia64/kernel/irq.c linux-2.5-bk/arch/ia64/kernel/irq.c --- linux-2.5.45/arch/ia64/kernel/irq.c Wed Oct 30 16:43:39 2002 +++ linux-2.5-bk/arch/ia64/kernel/irq.c Thu Oct 31 22:12:38 2002 @@ -172,7 +172,7 @@ #else for (j = 0; j < NR_CPUS; j++) if (cpu_online(j)) - seq_printf(p, "%10u ", kstat.irqs[j][i]); + seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]); #endif seq_printf(p, " %14s", idesc->handler->typename); seq_printf(p, " %s", action->name); @@ -346,7 +346,7 @@ unsigned int status; irq_enter(); - kstat.irqs[cpu][irq]++; + kstat_cpu(cpu).irqs[irq]++; if (desc->status & IRQ_PER_CPU) { /* no locking required for CPU-local interrupts: */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/ia64/kernel/sys_ia64.c linux-2.5-bk/arch/ia64/kernel/sys_ia64.c --- linux-2.5.45/arch/ia64/kernel/sys_ia64.c Wed Oct 30 16:42:59 2002 +++ linux-2.5-bk/arch/ia64/kernel/sys_ia64.c Thu Oct 31 22:12:38 2002 @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include /* doh, must come after sched.h... */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/ia64/mm/hugetlbpage.c linux-2.5-bk/arch/ia64/mm/hugetlbpage.c --- linux-2.5.45/arch/ia64/mm/hugetlbpage.c Wed Oct 30 16:42:56 2002 +++ linux-2.5-bk/arch/ia64/mm/hugetlbpage.c Thu Oct 31 22:12:38 2002 @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/m68k/amiga/amiints.c linux-2.5-bk/arch/m68k/amiga/amiints.c --- linux-2.5.45/arch/m68k/amiga/amiints.c Wed Oct 30 16:43:48 2002 +++ linux-2.5-bk/arch/m68k/amiga/amiints.c Thu Oct 31 22:12:38 2002 @@ -350,7 +350,7 @@ inline void amiga_do_irq(int irq, struct pt_regs *fp) { - kstat.irqs[0][SYS_IRQS + irq]++; + kstat_cpu(0).irqs[SYS_IRQS + irq]++; ami_irq_list[irq]->handler(irq, ami_irq_list[irq]->dev_id, fp); } @@ -358,7 +358,7 @@ { irq_node_t *node; - kstat.irqs[0][SYS_IRQS + irq]++; + kstat_cpu(0).irqs[SYS_IRQS + irq]++; custom.intreq = amiga_intena_vals[irq]; @@ -479,7 +479,7 @@ if (!(node = ami_irq_list[i])) continue; seq_printf(p, "ami %2d: %10u ", i, - kstat.irqs[0][SYS_IRQS + i]); + kstat_cpu(0).irqs[SYS_IRQS + i]); do { if (node->flags & SA_INTERRUPT) seq_puts(p, "F "); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/m68k/amiga/cia.c linux-2.5-bk/arch/m68k/amiga/cia.c --- linux-2.5.45/arch/m68k/amiga/cia.c Wed Oct 30 16:43:06 2002 +++ linux-2.5-bk/arch/m68k/amiga/cia.c Thu Oct 31 22:12:38 2002 @@ -131,7 +131,7 @@ custom.intreq = base->int_mask; for (i = 0; i < CIA_IRQS; i++, irq++, mach_irq++) { if (ints & 1) { - kstat.irqs[0][irq]++; + kstat_cpu(0).irqs[irq]++; base->irq_list[i].handler(mach_irq, base->irq_list[i].dev_id, fp); } ints >>= 1; @@ -166,7 +166,7 @@ j = base->cia_irq; for (i = 0; i < CIA_IRQS; i++) { seq_printf(p, "cia %2d: %10d ", j + i, - kstat.irqs[0][SYS_IRQS + j + i]); + kstat_cpu(0).irqs[SYS_IRQS + j + i]); seq_puts(p, " "); seq_printf(p, "%s\n", base->irq_list[i].devname); } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/m68k/atari/ataints.c linux-2.5-bk/arch/m68k/atari/ataints.c --- linux-2.5.45/arch/m68k/atari/ataints.c Wed Oct 30 16:42:16 2002 +++ linux-2.5-bk/arch/m68k/atari/ataints.c Thu Oct 31 22:12:38 2002 @@ -191,7 +191,7 @@ " andw #0xfeff,%%sr\n" /* set IPL = 6 again */ \ " orb #(1<<(%c3&7)),%a4:w\n" /* now unmask the int again */ \ " jbra ret_from_interrupt\n" \ - : : "i" (&kstat.irqs[0][n+8]), "i" (&irq_handler[n+8]), \ + : : "i" (&kstat_cpu(0).irqs[n+8]), "i" (&irq_handler[n+8]), \ "n" (PT_OFF_SR), "n" (n), \ "i" (n & 8 ? (n & 16 ? &tt_mfp.int_mk_a : &mfp.int_mk_a) \ : (n & 16 ? &tt_mfp.int_mk_b : &mfp.int_mk_b)), \ @@ -297,7 +297,7 @@ addql #8,%%sp addql #4,%%sp jbra ret_from_interrupt" - : : "i" (&kstat.irqs[0]), "n" (PT_OFF_FORMATVEC), + : : "i" (&kstat_cpu(0).irqs), "n" (PT_OFF_FORMATVEC), "m" (local_irq_count(0)) ); for (;;); @@ -623,11 +623,11 @@ continue; if (i < STMFP_SOURCE_BASE) seq_printf(p, "auto %2d: %10u ", - i, kstat.irqs[0][i]); + i, kstat_cpu(0).irqs[i]); else seq_printf(p, "vec $%02x: %10u ", IRQ_SOURCE_TO_VECTOR(i), - kstat.irqs[0][i]); + kstat_cpu(0).irqs[i]); if (irq_handler[i].handler != atari_call_irq_list) { seq_printf(p, "%s\n", irq_param[i].devname); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/m68k/kernel/ints.c linux-2.5-bk/arch/m68k/kernel/ints.c --- linux-2.5.45/arch/m68k/kernel/ints.c Wed Oct 30 16:42:55 2002 +++ linux-2.5-bk/arch/m68k/kernel/ints.c Thu Oct 31 22:12:39 2002 @@ -231,7 +231,7 @@ { if (vec >= VEC_INT1 && vec <= VEC_INT7 && !MACH_IS_BVME6000) { vec -= VEC_SPUR; - kstat.irqs[0][vec]++; + kstat_cpu(0).irqs[vec]++; irq_list[vec].handler(vec, irq_list[vec].dev_id, fp); } else { if (mach_process_int) @@ -250,7 +250,7 @@ if (mach_default_handler) { for (i = 0; i < SYS_IRQS; i++) { seq_printf(p, "auto %2d: %10u ", i, - i ? kstat.irqs[0][i] : num_spurious); + i ? kstat_cpu(0).irqs[i] : num_spurious); seq_puts(p, " "); seq_printf(p, "%s\n", irq_list[i].devname); } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/m68k/mac/macints.c linux-2.5-bk/arch/m68k/mac/macints.c --- linux-2.5.45/arch/m68k/mac/macints.c Wed Oct 30 16:43:39 2002 +++ linux-2.5-bk/arch/m68k/mac/macints.c Thu Oct 31 22:12:39 2002 @@ -345,7 +345,7 @@ irq_node_t *node, *slow_nodes; unsigned long cpu_flags; - kstat.irqs[0][irq]++; + kstat_cpu(0).irqs[irq]++; #ifdef DEBUG_SPURIOUS if (!mac_irq_list[irq] && (console_loglevel > 7)) { @@ -620,7 +620,7 @@ case 8: base = "bbn"; break; } - seq_printf(p, "%4s %2d: %10u ", base, i, kstat.irqs[0][i]); + seq_printf(p, "%4s %2d: %10u ", base, i, kstat_cpu(0).irqs[i]); do { if (node->flags & IRQ_FLG_FAST) { diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/m68k/sun3/sun3ints.c linux-2.5-bk/arch/m68k/sun3/sun3ints.c --- linux-2.5.45/arch/m68k/sun3/sun3ints.c Wed Oct 30 16:43:47 2002 +++ linux-2.5-bk/arch/m68k/sun3/sun3ints.c Thu Oct 31 22:12:39 2002 @@ -58,7 +58,7 @@ inline void sun3_do_irq(int irq, struct pt_regs *fp) { - kstat.irqs[0][SYS_IRQS + irq]++; + kstat_cpu(0).irqs[SYS_IRQS + irq]++; *sun3_intreg &= ~(1<flags & SA_INTERRUPT) ? '+' : ' ', action->name); for (action=action->next; action; action = action->next) { @@ -180,7 +180,7 @@ cpu = smp_processor_id(); irq_enter(cpu, irq); - kstat.irqs[cpu][irq]++; + kstat_cpu(cpu).irqs[irq]++; mask_irq(irq); action = *(irq + irq_action); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/mips/dec/irq.c linux-2.5-bk/arch/mips/dec/irq.c --- linux-2.5.45/arch/mips/dec/irq.c Wed Oct 30 16:42:59 2002 +++ linux-2.5-bk/arch/mips/dec/irq.c Thu Oct 31 22:12:39 2002 @@ -103,7 +103,7 @@ if (!action) continue; seq_printf(p, "%2d: %8d %c %s", - i, kstat.irqs[0][i], + i, kstat_cpu(0).irqs[i], (action->flags & SA_INTERRUPT) ? '+' : ' ', action->name); for (action = action->next; action; action = action->next) { @@ -130,7 +130,7 @@ cpu = smp_processor_id(); irq_enter(cpu, irq); - kstat.irqs[cpu][irq]++; + kstat_cpu(cpu).irqs[irq]++; mask_irq(irq); action = *(irq + irq_action); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/mips/ite-boards/generic/irq.c linux-2.5-bk/arch/mips/ite-boards/generic/irq.c --- linux-2.5.45/arch/mips/ite-boards/generic/irq.c Wed Oct 30 16:42:58 2002 +++ linux-2.5-bk/arch/mips/ite-boards/generic/irq.c Thu Oct 31 22:12:39 2002 @@ -256,7 +256,7 @@ cpu = smp_processor_id(); irq_enter(cpu, irq); - kstat.irqs[cpu][irq]++; + kstat_cpu(cpu).irqs[irq]++; #if 0 if (irq_desc[irq].handler && irq_desc[irq].handler->ack) { // printk("invoking ack handler\n"); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/mips/kernel/irq.c linux-2.5-bk/arch/mips/kernel/irq.c --- linux-2.5.45/arch/mips/kernel/irq.c Wed Oct 30 16:41:56 2002 +++ linux-2.5-bk/arch/mips/kernel/irq.c Thu Oct 31 22:12:39 2002 @@ -244,7 +244,7 @@ struct irqaction * action; unsigned int status; - kstat.irqs[cpu][irq]++; + kstat_cpu(cpu).irqs[irq]++; spin_lock(&desc->lock); desc->handler->ack(irq); /* diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/mips/kernel/old-irq.c linux-2.5-bk/arch/mips/kernel/old-irq.c --- linux-2.5.45/arch/mips/kernel/old-irq.c Wed Oct 30 16:42:57 2002 +++ linux-2.5-bk/arch/mips/kernel/old-irq.c Thu Oct 31 22:12:39 2002 @@ -134,7 +134,7 @@ if (!action) continue; seq_printf(p, "%2d: %8d %c %s", - i, kstat.irqs[0][i], + i, kstat_cpu(0).irqs[i], (action->flags & SA_INTERRUPT) ? '+' : ' ', action->name); for (action=action->next; action; action = action->next) { @@ -176,7 +176,7 @@ i8259_mask_and_ack_irq(irq); - kstat.irqs[cpu][irq]++; + kstat_cpu(cpu).irqs[irq]++; action = *(irq + irq_action); if (!action) @@ -214,7 +214,7 @@ cpu = smp_processor_id(); irq_enter(cpu, irq); - kstat.irqs[cpu][irq]++; + kstat_cpu(cpu).irqs[irq]++; action = *(irq + irq_action); if (action) { diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/mips/kernel/old-time.c linux-2.5-bk/arch/mips/kernel/old-time.c --- linux-2.5.45/arch/mips/kernel/old-time.c Wed Oct 30 16:43:08 2002 +++ linux-2.5-bk/arch/mips/kernel/old-time.c Thu Oct 31 22:12:39 2002 @@ -424,7 +424,7 @@ */ write_32bit_cp0_register (CP0_COMPARE, (unsigned long) (count + r4k_interval)); - kstat.irqs[0][irq]++; + kstat_cpu(0).irqs[irq]++; #endif timer_interrupt(irq, dev_id, regs); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/mips/kernel/time.c linux-2.5-bk/arch/mips/kernel/time.c --- linux-2.5.45/arch/mips/kernel/time.c Wed Oct 30 16:42:53 2002 +++ linux-2.5-bk/arch/mips/kernel/time.c Thu Oct 31 22:12:39 2002 @@ -369,7 +369,7 @@ int cpu = smp_processor_id(); irq_enter(cpu, irq); - kstat.irqs[cpu][irq]++; + kstat_cpu(cpu).irqs[irq]++; /* we keep interrupt disabled all the time */ timer_interrupt(irq, NULL, regs); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/mips/mips-boards/atlas/atlas_int.c linux-2.5-bk/arch/mips/mips-boards/atlas/atlas_int.c --- linux-2.5.45/arch/mips/mips-boards/atlas/atlas_int.c Wed Oct 30 16:43:07 2002 +++ linux-2.5-bk/arch/mips/mips-boards/atlas/atlas_int.c Thu Oct 31 22:12:40 2002 @@ -105,7 +105,7 @@ if (!action) continue; seq_printf(p, "%2d: %8d %c %s", - num, kstat.irqs[0][num], + num, kstat_cpu(0).irqs[num], (action->flags & SA_INTERRUPT) ? '+' : ' ', action->name); for (action=action->next; action; action = action->next) { @@ -202,7 +202,7 @@ } irq_enter(cpu, irq); - kstat.irqs[0][irq]++; + kstat_cpu(0).irqs[irq]++; action->handler(irq, action->dev_id, regs); irq_exit(cpu, irq); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/mips/mips-boards/generic/time.c linux-2.5-bk/arch/mips/mips-boards/generic/time.c --- linux-2.5.45/arch/mips/mips-boards/generic/time.c Wed Oct 30 16:43:06 2002 +++ linux-2.5-bk/arch/mips/mips-boards/generic/time.c Thu Oct 31 22:12:40 2002 @@ -139,7 +139,7 @@ goto null; do { - kstat.irqs[0][irq]++; + kstat_cpu(0).irqs[irq]++; do_timer(regs); /* Historical comment/code: diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/mips/philips/nino/irq.c linux-2.5-bk/arch/mips/philips/nino/irq.c --- linux-2.5.45/arch/mips/philips/nino/irq.c Wed Oct 30 16:41:50 2002 +++ linux-2.5-bk/arch/mips/philips/nino/irq.c Thu Oct 31 22:12:40 2002 @@ -125,7 +125,7 @@ if (!action) continue; seq_printf(p, "%2d: %8d %c %s", - i, kstat.irqs[0][i], + i, kstat_cpu(0).irqs[i], (action->flags & SA_INTERRUPT) ? '+' : ' ', action->name); for (action = action->next; action; action = action->next) { @@ -161,7 +161,7 @@ cpu = smp_processor_id(); irq_enter(cpu, irq); - kstat.irqs[cpu][irq]++; + kstat_cpu(0).irqs[irq]++; if (irq == 20) { printk("20 %08lx %08lx\n %08lx %08lx\n %08lx\n", diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/mips/sgi/kernel/indy_int.c linux-2.5-bk/arch/mips/sgi/kernel/indy_int.c --- linux-2.5.45/arch/mips/sgi/kernel/indy_int.c Wed Oct 30 16:41:43 2002 +++ linux-2.5-bk/arch/mips/sgi/kernel/indy_int.c Thu Oct 31 22:12:40 2002 @@ -418,7 +418,7 @@ int irq = 6; irq_enter(cpu, irq); - kstat.irqs[0][irq]++; + kstat_cpu(0).irqs[irq]++; printk("Got a bus error IRQ, shouldn't happen yet\n"); show_regs(regs); printk("Spinning...\n"); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/mips/sgi/kernel/time.c linux-2.5-bk/arch/mips/sgi/kernel/time.c --- linux-2.5.45/arch/mips/sgi/kernel/time.c Wed Oct 30 16:41:58 2002 +++ linux-2.5-bk/arch/mips/sgi/kernel/time.c Thu Oct 31 22:12:40 2002 @@ -13,7 +13,7 @@ int irq = 4; irq_enter(cpu, irq); - kstat.irqs[0][irq]++; + kstat_cpu(0).irqs[irq]++; printk("indy_8254timer_irq: Whoops, should not have gotten this IRQ\n"); prom_getchar(); ArcEnterInteractiveMode(); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/mips64/mips-boards/atlas/atlas_int.c linux-2.5-bk/arch/mips64/mips-boards/atlas/atlas_int.c --- linux-2.5.45/arch/mips64/mips-boards/atlas/atlas_int.c Wed Oct 30 16:42:54 2002 +++ linux-2.5-bk/arch/mips64/mips-boards/atlas/atlas_int.c Thu Oct 31 22:12:40 2002 @@ -101,7 +101,7 @@ if (!action) continue; seq_printf(p, "%2d: %8d %c %s", - num, kstat.irqs[0][num], + num, kstat_cpu(0).irqs[num], (action->flags & SA_INTERRUPT) ? '+' : ' ', action->name); for (action=action->next; action; action = action->next) { @@ -198,7 +198,7 @@ } irq_enter(cpu, irq); - kstat.irqs[0][irq]++; + kstat_cpu(0).irqs[irq]++; action->handler(irq, action->dev_id, regs); irq_exit(cpu, irq); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/mips64/mips-boards/generic/time.c linux-2.5-bk/arch/mips64/mips-boards/generic/time.c --- linux-2.5.45/arch/mips64/mips-boards/generic/time.c Wed Oct 30 16:42:19 2002 +++ linux-2.5-bk/arch/mips64/mips-boards/generic/time.c Thu Oct 31 22:12:40 2002 @@ -138,7 +138,7 @@ goto null; do { - kstat.irqs[0][irq]++; + kstat_cpu(0).irqs[irq]++; do_timer(regs); /* Historical comment/code: diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/mips64/mips-boards/malta/malta_int.c linux-2.5-bk/arch/mips64/mips-boards/malta/malta_int.c --- linux-2.5.45/arch/mips64/mips-boards/malta/malta_int.c Wed Oct 30 16:43:08 2002 +++ linux-2.5-bk/arch/mips64/mips-boards/malta/malta_int.c Thu Oct 31 22:12:40 2002 @@ -131,7 +131,7 @@ if (!action) continue; seq_printf(p, "%2d: %8d %c %s", - num, kstat.irqs[0][num], + num, kstat_cpu(0).irqs[num], (action->flags & SA_INTERRUPT) ? '+' : ' ', action->name); for (action=action->next; action; action = action->next) { @@ -146,7 +146,7 @@ if (!action) continue; seq_printf(p, "%2d: %8d %c %s", - num, kstat.irqs[0][num], + num, kstat_cpu(0).irqs[num], (action->flags & SA_INTERRUPT) ? '+' : ' ', action->name); for (action=action->next; action; action = action->next) { @@ -322,7 +322,7 @@ return; irq_enter(cpu, irq); - kstat.irqs[0][irq + 8]++; + kstat_cpu(0).irqs[irq + 8]++; do { action->handler(irq, action->dev_id, regs); action = action->next; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/mips64/sgi-ip22/ip22-int.c linux-2.5-bk/arch/mips64/sgi-ip22/ip22-int.c --- linux-2.5.45/arch/mips64/sgi-ip22/ip22-int.c Wed Oct 30 16:41:55 2002 +++ linux-2.5-bk/arch/mips64/sgi-ip22/ip22-int.c Thu Oct 31 22:12:40 2002 @@ -243,7 +243,7 @@ if (!action) continue; seq_printf(p, "%2d: %8d %c %s", - num, kstat.irqs[0][num], + num, kstat_cpu(0).irqs[num], (action->flags & SA_INTERRUPT) ? '+' : ' ', action->name); for (action=action->next; action; action = action->next) { @@ -258,7 +258,7 @@ if (!action) continue; seq_printf(p, "%2d: %8d %c %s", - num, kstat.irqs[0][num], + num, kstat_cpu(0).irqs[num], (action->flags & SA_INTERRUPT) ? '+' : ' ', action->name); for (action=action->next; action; action = action->next) { @@ -285,7 +285,7 @@ cpu = smp_processor_id(); irq_enter(cpu, irq); - kstat.irqs[0][irq]++; + kstat_cpu(0).irqs[irq]++; panic(KERN_DEBUG "Got irq %d, press a key.", irq); @@ -444,7 +444,7 @@ } irq_enter(cpu, irq); - kstat.irqs[0][irq + 16]++; + kstat_cpu(0).irqs[irq + 16]++; action->handler(irq, action->dev_id, regs); irq_exit(cpu, irq); } @@ -468,7 +468,7 @@ action = local_irq_action[irq]; } irq_enter(cpu, irq); - kstat.irqs[0][irq + 24]++; + kstat_cpu(0).irqs[irq + 24]++; action->handler(irq, action->dev_id, regs); irq_exit(cpu, irq); } @@ -479,7 +479,7 @@ int irq = 6; irq_enter(cpu, irq); - kstat.irqs[0][irq]++; + kstat_cpu(0).irqs[irq]++; printk("Got a bus error IRQ, shouldn't happen yet\n"); show_regs(regs); printk("Spinning...\n"); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/mips64/sgi-ip22/ip22-timer.c linux-2.5-bk/arch/mips64/sgi-ip22/ip22-timer.c --- linux-2.5.45/arch/mips64/sgi-ip22/ip22-timer.c Wed Oct 30 16:42:56 2002 +++ linux-2.5-bk/arch/mips64/sgi-ip22/ip22-timer.c Thu Oct 31 22:12:40 2002 @@ -98,7 +98,7 @@ else r4k_cur += r4k_offset; ack_r4ktimer(r4k_cur); - kstat.irqs[0][irq]++; + kstat_cpu(0).irqs[irq]++; do_timer(regs); /* We update the Dallas time of day approx. every 11 minutes, @@ -236,7 +236,7 @@ int irq = 4; irq_enter(cpu, irq); - kstat.irqs[0][irq]++; + kstat_cpu(0).irqs[irq]++; panic("indy_8254timer_irq: Whoops, should not have gotten this IRQ\n"); irq_exit(cpu, irq); } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/mips64/sgi-ip27/ip27-irq.c linux-2.5-bk/arch/mips64/sgi-ip27/ip27-irq.c --- linux-2.5.45/arch/mips64/sgi-ip27/ip27-irq.c Wed Oct 30 16:42:56 2002 +++ linux-2.5-bk/arch/mips64/sgi-ip27/ip27-irq.c Thu Oct 31 22:12:40 2002 @@ -146,7 +146,7 @@ action = irq_action[i]; if (!action) continue; - seq_printf(p, "%2d: %8d %c %s", i, kstat.irqs[0][i], + seq_printf(p, "%2d: %8d %c %s", i, kstat_cpu(0).irqs[i], (action->flags & SA_INTERRUPT) ? '+' : ' ', action->name); for (action=action->next; action; action = action->next) { @@ -170,7 +170,7 @@ int do_random; irq_enter(thiscpu, irq); - kstat.irqs[thiscpu][irq]++; + kstat_cpu(thiscpu).irqs[irq]++; action = *(irq + irq_action); if (action) { diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/mips64/sgi-ip27/ip27-timer.c linux-2.5-bk/arch/mips64/sgi-ip27/ip27-timer.c --- linux-2.5.45/arch/mips64/sgi-ip27/ip27-timer.c Wed Oct 30 16:42:26 2002 +++ linux-2.5-bk/arch/mips64/sgi-ip27/ip27-timer.c Thu Oct 31 22:12:40 2002 @@ -103,7 +103,7 @@ if (LOCAL_HUB_L(PI_RT_COUNT) >= ct_cur[cpu]) goto again; - kstat.irqs[cpu][irq]++; /* kstat only for bootcpu? */ + kstat_cpu(cpu).irqs[irq]++; /* kstat only for bootcpu? */ if (cpu == 0) do_timer(regs); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/parisc/kernel/irq.c linux-2.5-bk/arch/parisc/kernel/irq.c --- linux-2.5.45/arch/parisc/kernel/irq.c Wed Oct 30 16:42:28 2002 +++ linux-2.5-bk/arch/parisc/kernel/irq.c Thu Oct 31 22:12:40 2002 @@ -232,7 +232,7 @@ #else for (j = 0; j < smp_num_cpus; j++) seq_printf(p, "%10u ", - kstat.irqs[cpu_logical_map(j)][irq_no]); + kstat_cpu(cpu_logical_map(j)).irqs[irq_no]); #endif seq_printf(p, " %14s", region->data.name ? region->data.name : "N/A"); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/ppc/amiga/amiints.c linux-2.5-bk/arch/ppc/amiga/amiints.c --- linux-2.5.45/arch/ppc/amiga/amiints.c Wed Oct 30 16:41:54 2002 +++ linux-2.5-bk/arch/ppc/amiga/amiints.c Thu Oct 31 22:12:40 2002 @@ -189,7 +189,7 @@ irq_desc_t *desc = irq_desc + irq; struct irqaction *action = desc->action; - kstat.irqs[0][irq]++; + kstat_cpu(0).irqs[irq]++; action->handler(irq, action->dev_id, fp); } @@ -198,7 +198,7 @@ irq_desc_t *desc = irq_desc + irq; struct irqaction *action; - kstat.irqs[0][irq]++; + kstat_cpu(0).irqs[irq]++; custom.intreq = ami_intena_vals[irq]; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/ppc/amiga/cia.c linux-2.5-bk/arch/ppc/amiga/cia.c --- linux-2.5.45/arch/ppc/amiga/cia.c Wed Oct 30 16:42:20 2002 +++ linux-2.5-bk/arch/ppc/amiga/cia.c Thu Oct 31 22:12:40 2002 @@ -148,7 +148,7 @@ custom.intreq = base->int_mask; for (i = 0; i < CIA_IRQS; i++, irq++) { if (ints & 1) { - kstat.irqs[0][irq]++; + kstat_cpu(0).irqs[irq]++; action = desc->action; action->handler(irq, action->dev_id, fp); } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/ppc/amiga/ints.c linux-2.5-bk/arch/ppc/amiga/ints.c --- linux-2.5.45/arch/ppc/amiga/ints.c Wed Oct 30 16:41:43 2002 +++ linux-2.5-bk/arch/ppc/amiga/ints.c Thu Oct 31 22:12:40 2002 @@ -130,7 +130,7 @@ { if (vec >= VEC_INT1 && vec <= VEC_INT7 && !MACH_IS_BVME6000) { vec -= VEC_SPUR; - kstat.irqs[0][vec]++; + kstat_cpu(0).irqs[vec]++; irq_list[vec].handler(vec, irq_list[vec].dev_id, fp); } else { if (mach_process_int) @@ -149,7 +149,7 @@ if (mach_default_handler) { for (i = 0; i < SYS_IRQS; i++) { seq_printf(p, "auto %2d: %10u ", i, - i ? kstat.irqs[0][i] : num_spurious); + i ? kstat_cpu(0).irqs[i] : num_spurious); seq_puts(p, " "); seq_printf(p, "%s\n", irq_list[i].devname); } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/ppc/kernel/irq.c linux-2.5-bk/arch/ppc/kernel/irq.c --- linux-2.5.45/arch/ppc/kernel/irq.c Wed Oct 30 16:43:38 2002 +++ linux-2.5-bk/arch/ppc/kernel/irq.c Thu Oct 31 22:12:41 2002 @@ -362,7 +362,7 @@ for (j = 0; j < NR_CPUS; j++) if (cpu_online(j)) seq_printf(p, "%10u ", - kstat.irqs[j][i]); + kstat_cpu(j).irqs[i]); #else seq_printf(p, "%10u ", kstat_irqs(i)); #endif /* CONFIG_SMP */ @@ -423,7 +423,7 @@ int cpu = smp_processor_id(); irq_desc_t *desc = irq_desc + irq; - kstat.irqs[cpu][irq]++; + kstat_cpu(cpu).irqs[irq]++; spin_lock(&desc->lock); ack_irq(irq); /* diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/ppc64/kernel/irq.c linux-2.5-bk/arch/ppc64/kernel/irq.c --- linux-2.5.45/arch/ppc64/kernel/irq.c Wed Oct 30 16:42:53 2002 +++ linux-2.5-bk/arch/ppc64/kernel/irq.c Thu Oct 31 22:12:42 2002 @@ -357,7 +357,7 @@ #ifdef CONFIG_SMP for (j = 0; j < NR_CPUS; j++) { if (cpu_online(j)) - seq_printf(p, "%10u ", kstat.irqs[j][i]); + seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]); } #else seq_printf(p, "%10u ", kstat_irqs(i)); @@ -484,7 +484,7 @@ if (naca->interrupt_controller == IC_OPEN_PIC) balance_irq(irq); - kstat.irqs[cpu][irq]++; + kstat_cpu(cpu).irqs[irq]++; spin_lock(&desc->lock); ack_irq(irq); /* diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/sh/kernel/irq.c linux-2.5-bk/arch/sh/kernel/irq.c --- linux-2.5.45/arch/sh/kernel/irq.c Wed Oct 30 16:42:26 2002 +++ linux-2.5-bk/arch/sh/kernel/irq.c Thu Oct 31 22:12:42 2002 @@ -239,7 +239,7 @@ :"=z" (irq)); irq = irq_demux(irq); - kstat.irqs[cpu][irq]++; + kstat_cpu(cpu).irqs[irq]++; desc = irq_desc + irq; spin_lock(&desc->lock); desc->handler->ack(irq); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/sparc/kernel/irq.c linux-2.5-bk/arch/sparc/kernel/irq.c --- linux-2.5.45/arch/sparc/kernel/irq.c Wed Oct 30 16:41:39 2002 +++ linux-2.5-bk/arch/sparc/kernel/irq.c Thu Oct 31 22:12:42 2002 @@ -124,7 +124,7 @@ for (j = 0; j < NR_CPUS; j++) { if (cpu_online(j)) seq_printf(p, "%10u ", - kstat.irqs[cpu_logical_map(j)][i]); + kstat_cpu(cpu_logical_map(j)).irqs[i]); } #endif seq_printf(p, " %c %s", @@ -424,7 +424,7 @@ smp4m_irq_rotate(cpu); #endif action = *(irq + irq_action); - kstat.irqs[cpu][irq]++; + kstat_cpu(cpu).irqs[irq]++; do { if (!action || !action->handler) unexpected_irq(irq, 0, regs); @@ -444,7 +444,7 @@ disable_pil_irq(irq); irq_enter(); - kstat.irqs[cpu][irq]++; + kstat_cpu(cpu).irqs[irq]++; floppy_interrupt(irq, dev_id, regs); irq_exit(); enable_pil_irq(irq); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/sparc/kernel/sun4d_irq.c linux-2.5-bk/arch/sparc/kernel/sun4d_irq.c --- linux-2.5.45/arch/sparc/kernel/sun4d_irq.c Wed Oct 30 16:41:31 2002 +++ linux-2.5-bk/arch/sparc/kernel/sun4d_irq.c Thu Oct 31 22:12:42 2002 @@ -102,7 +102,7 @@ for (x = 0; x < NR_CPUS; x++) { if (cpu_online) seq_printf(p, "%10u ", - kstat.irqs[cpu_logical_map(x)][i]); + kstat_cpu(cpu_logical_map(x)).irqs[i]); } #endif seq_printf(p, "%c %s", @@ -199,7 +199,7 @@ cc_set_iclr(1 << irq); irq_enter(); - kstat.irqs[cpu][irq]++; + kstat_cpu(cpu).irqs[irq]++; if (!sbusl) { action = *(irq + irq_action); if (!action) diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/sparc64/kernel/irq.c linux-2.5-bk/arch/sparc64/kernel/irq.c --- linux-2.5.45/arch/sparc64/kernel/irq.c Wed Oct 30 16:43:43 2002 +++ linux-2.5-bk/arch/sparc64/kernel/irq.c Thu Oct 31 22:12:43 2002 @@ -135,7 +135,7 @@ if (!cpu_online(j)) continue; seq_printf(p, "%10u ", - kstat.irqs[j][i]); + kstat_cpu(j).irqs[i]); } #endif seq_printf(p, " %s:%lx", action->name, @@ -738,7 +738,7 @@ #endif irq_enter(); - kstat.irqs[cpu][irq]++; + kstat_cpu(cpu).irqs[irq]++; if (irq == 9) kbd_pt_regs = regs; @@ -813,7 +813,7 @@ int cpu = smp_processor_id(); irq_enter(); - kstat.irqs[cpu][irq]++; + kstat_cpu(cpu).irqs[irq]++; *(irq_work(cpu, irq)) = 0; bucket = get_ino_in_irqaction(action) + ivector_table; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/sparc64/kernel/smp.c linux-2.5-bk/arch/sparc64/kernel/smp.c --- linux-2.5.45/arch/sparc64/kernel/smp.c Wed Oct 30 16:41:31 2002 +++ linux-2.5-bk/arch/sparc64/kernel/smp.c Thu Oct 31 22:12:43 2002 @@ -967,7 +967,7 @@ if (cpu == boot_cpu_id) { irq_enter(); - kstat.irqs[cpu][0]++; + kstat_cpu(cpu).irqs[0]++; timer_tick_interrupt(regs); irq_exit(); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/sparc64/kernel/sys_sparc.c linux-2.5-bk/arch/sparc64/kernel/sys_sparc.c --- linux-2.5.45/arch/sparc64/kernel/sys_sparc.c Wed Oct 30 16:42:58 2002 +++ linux-2.5-bk/arch/sparc64/kernel/sys_sparc.c Thu Oct 31 22:12:43 2002 @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/sparc64/mm/hugetlbpage.c linux-2.5-bk/arch/sparc64/mm/hugetlbpage.c --- linux-2.5.45/arch/sparc64/mm/hugetlbpage.c Wed Oct 30 16:43:07 2002 +++ linux-2.5-bk/arch/sparc64/mm/hugetlbpage.c Thu Oct 31 22:12:43 2002 @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/sparc64/mm/init.c linux-2.5-bk/arch/sparc64/mm/init.c --- linux-2.5.45/arch/sparc64/mm/init.c Wed Oct 30 16:42:26 2002 +++ linux-2.5-bk/arch/sparc64/mm/init.c Thu Oct 31 22:12:43 2002 @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/um/kernel/irq.c linux-2.5-bk/arch/um/kernel/irq.c --- linux-2.5.45/arch/um/kernel/irq.c Wed Oct 30 16:43:38 2002 +++ linux-2.5-bk/arch/um/kernel/irq.c Thu Oct 31 22:12:43 2002 @@ -108,7 +108,7 @@ #else for (j = 0; j < num_online_cpus(); j++) p += sprintf(p, "%10u ", - kstat.irqs[cpu_logical_map(j)][i]); + kstat_cpu(cpu_logical_map(j)).irqs[i]); #endif p += sprintf(p, " %14s", irq_desc[i].handler->typename); p += sprintf(p, " %s", action->name); @@ -283,7 +283,7 @@ unsigned int status; irq_enter(); - kstat.irqs[cpu][irq]++; + kstat_cpu(cpu).irqs[irq]++; spin_lock(&desc->lock); desc->handler->ack(irq); /* diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/arch/x86_64/kernel/irq.c linux-2.5-bk/arch/x86_64/kernel/irq.c --- linux-2.5.45/arch/x86_64/kernel/irq.c Wed Oct 30 16:42:53 2002 +++ linux-2.5-bk/arch/x86_64/kernel/irq.c Thu Oct 31 22:12:43 2002 @@ -151,7 +151,7 @@ #else for_each_cpu(j) seq_printf(p, "%10u ", - kstat.irqs[j][i]); + kstat_cpu(j).irqs[i]); #endif seq_printf(p, " %14s", irq_desc[i].handler->typename); seq_printf(p, " %s", action->name); @@ -328,7 +328,7 @@ if (irq > 256) BUG(); irq_enter(); - kstat.irqs[cpu][irq]++; + kstat_cpu(cpu).irqs[irq]++; spin_lock(&desc->lock); desc->handler->ack(irq); /* diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/acpi/Makefile linux-2.5-bk/drivers/acpi/Makefile --- linux-2.5.45/drivers/acpi/Makefile Wed Oct 30 16:43:43 2002 +++ linux-2.5-bk/drivers/acpi/Makefile Thu Oct 31 22:12:44 2002 @@ -32,7 +32,7 @@ # # ACPI Bus and Device Drivers # -obj-$(CONFIG_ACPI_BUS) += bus.o driverfs.o +obj-$(CONFIG_ACPI_BUS) += bus.o obj-$(CONFIG_ACPI_AC) += ac.o obj-$(CONFIG_ACPI_BATTERY) += battery.o obj-$(CONFIG_ACPI_BUTTON) += button.o diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/acpi/acpi_bus.h linux-2.5-bk/drivers/acpi/acpi_bus.h --- linux-2.5.45/drivers/acpi/acpi_bus.h Wed Oct 30 16:42:28 2002 +++ linux-2.5-bk/drivers/acpi/acpi_bus.h Thu Oct 31 22:12:44 2002 @@ -27,7 +27,7 @@ #define __ACPI_BUS_H__ #include -#include +#include #include "include/acpi.h" @@ -255,7 +255,7 @@ struct acpi_device_ops ops; struct acpi_driver *driver; void *driver_data; - struct driver_dir_entry driverfs_dir; + struct kobject kobj; }; #define acpi_driver_data(d) ((d)->driver_data) @@ -274,6 +274,7 @@ u32 data; }; +extern struct subsystem acpi_subsys; /* * External Functions diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/acpi/bus.c linux-2.5-bk/drivers/acpi/bus.c --- linux-2.5.45/drivers/acpi/bus.c Wed Oct 30 16:43:02 2002 +++ linux-2.5-bk/drivers/acpi/bus.c Thu Oct 31 22:12:44 2002 @@ -675,6 +675,9 @@ return_VALUE(-ENODEV); } +struct subsystem acpi_subsys = { + .kobj = { .name = "acpi" }, +}; static int __init acpi_init (void) { @@ -692,6 +695,8 @@ printk(KERN_INFO PREFIX "Disabled via command line (acpi=off)\n"); return -ENODEV; } + + firmware_register(&acpi_subsys); result = acpi_bus_init(); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/acpi/driverfs.c linux-2.5-bk/drivers/acpi/driverfs.c --- linux-2.5.45/drivers/acpi/driverfs.c Wed Oct 30 16:42:28 2002 +++ linux-2.5-bk/drivers/acpi/driverfs.c Wed Dec 31 16:00:00 1969 @@ -1,46 +0,0 @@ -/* - * driverfs.c - ACPI bindings for driverfs. - * - * Copyright (c) 2002 Patrick Mochel - * Copyright (c) 2002 The Open Source Development Lab - * - */ - -#include -#include -#include - -#include "acpi_bus.h" - -static struct driver_dir_entry acpi_dir = { - .name = "acpi", - .mode = (S_IRWXU | S_IRUGO | S_IXUGO), -}; - -/* driverfs ops for ACPI attribute files go here, when/if - * there are ACPI attribute files. - * For now, we just have directory creation and removal. - */ - -void acpi_remove_dir(struct acpi_device * dev) -{ - if (dev) - driverfs_remove_dir(&dev->driverfs_dir); -} - -int acpi_create_dir(struct acpi_device * dev) -{ - struct driver_dir_entry * parent; - - parent = dev->parent ? &dev->parent->driverfs_dir : &acpi_dir; - dev->driverfs_dir.name = dev->pnp.bus_id; - dev->driverfs_dir.mode = (S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO); - return driverfs_create_dir(&dev->driverfs_dir,parent); -} - -static int __init acpi_driverfs_init(void) -{ - return driverfs_create_dir(&acpi_dir,NULL); -} - -subsys_initcall(acpi_driverfs_init); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/acpi/scan.c linux-2.5-bk/drivers/acpi/scan.c --- linux-2.5.45/drivers/acpi/scan.c Wed Oct 30 16:42:29 2002 +++ linux-2.5-bk/drivers/acpi/scan.c Thu Oct 31 22:12:44 2002 @@ -25,20 +25,52 @@ static LIST_HEAD(acpi_device_list); static spinlock_t acpi_device_lock = SPIN_LOCK_UNLOCKED; -static int -acpi_device_register ( - struct acpi_device *device, - struct acpi_device *parent) +static void acpi_device_release(struct kobject * kobj) { - return acpi_create_dir(device); + struct acpi_device * dev = container_of(kobj,struct acpi_device,kobj); + kfree(dev); } +static struct subsystem acpi_namespace_subsys = { + .kobj = { .name = "namespace" }, + .parent = &acpi_subsys, + .release = acpi_device_release, +}; + + +static void acpi_device_register(struct acpi_device * device, struct acpi_device * parent) +{ + /* + * Linkage + * ------- + * Link this device to its parent and siblings. + */ + INIT_LIST_HEAD(&device->children); + INIT_LIST_HEAD(&device->node); + INIT_LIST_HEAD(&device->g_list); + + spin_lock(&acpi_device_lock); + if (device->parent) { + list_add_tail(&device->node, &device->parent->children); + list_add_tail(&device->g_list,&device->parent->g_list); + } else + list_add_tail(&device->g_list,&acpi_device_list); + spin_unlock(&acpi_device_lock); + + kobject_init(&device->kobj); + strncpy(device->kobj.name,device->pnp.bus_id,KOBJ_NAME_LEN); + if (parent) + device->kobj.parent = &parent->kobj; + device->kobj.subsys = &acpi_namespace_subsys; + kobject_register(&device->kobj); +} static int acpi_device_unregister ( - struct acpi_device *device) + struct acpi_device *device, + int type) { - acpi_remove_dir(device); + kobject_unregister(&device->kobj); return 0; } @@ -443,16 +475,6 @@ return_VALUE(0); } -static int -acpi_bus_remove ( - struct acpi_device *device, - int type) -{ - acpi_device_unregister(device); - kfree(device); - return 0; -} - static void acpi_device_get_busid(struct acpi_device * device, acpi_handle handle, int type) { char bus_id[5] = {'?',0}; @@ -621,28 +643,6 @@ #endif /*CONFIG_ACPI_DEBUG*/ } -static void acpi_device_attach(struct acpi_device * device, struct acpi_device * parent) -{ - /* - * Linkage - * ------- - * Link this device to its parent and siblings. - */ - INIT_LIST_HEAD(&device->children); - INIT_LIST_HEAD(&device->node); - INIT_LIST_HEAD(&device->g_list); - - spin_lock(&acpi_device_lock); - if (device->parent) { - list_add_tail(&device->node, &device->parent->children); - list_add_tail(&device->g_list,&device->parent->g_list); - } else - list_add_tail(&device->g_list,&acpi_device_list); - spin_unlock(&acpi_device_lock); - - acpi_device_register(device, parent); -} - static int acpi_bus_add ( struct acpi_device **child, @@ -741,7 +741,7 @@ acpi_device_get_debug_info(device,handle,type); - acpi_device_attach(device,parent); + acpi_device_register(device,parent); /* * Bind _ADR-Based Devices @@ -919,6 +919,8 @@ if (acpi_disabled) return_VALUE(0); + subsystem_register(&acpi_namespace_subsys); + /* * Create the root device in the bus's device tree */ @@ -935,7 +937,7 @@ result = acpi_bus_scan(acpi_root); if (result) - acpi_bus_remove(acpi_root, ACPI_BUS_REMOVAL_NORMAL); + acpi_device_unregister(acpi_root, ACPI_BUS_REMOVAL_NORMAL); Done: return_VALUE(result); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/base/Makefile linux-2.5-bk/drivers/base/Makefile --- linux-2.5.45/drivers/base/Makefile Wed Oct 30 16:43:38 2002 +++ linux-2.5-bk/drivers/base/Makefile Thu Oct 31 22:12:44 2002 @@ -2,13 +2,14 @@ obj-y := core.o sys.o interface.o power.o bus.o \ driver.o class.o intf.o platform.o \ - cpu.o + cpu.o firmware.o obj-y += fs/ obj-$(CONFIG_HOTPLUG) += hotplug.o export-objs := core.o power.o sys.o bus.o driver.o \ - class.o intf.o platform.o cpu.o + class.o intf.o platform.o cpu.o \ + firmware.o include $(TOPDIR)/Rules.make diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/base/base.h linux-2.5-bk/drivers/base/base.h --- linux-2.5.45/drivers/base/base.h Wed Oct 30 16:42:26 2002 +++ linux-2.5-bk/drivers/base/base.h Thu Oct 31 22:12:44 2002 @@ -10,43 +10,14 @@ extern spinlock_t device_lock; extern struct semaphore device_sem; -extern struct device * get_device_locked(struct device *); - extern int bus_add_device(struct device * dev); extern void bus_remove_device(struct device * dev); -extern int device_make_dir(struct device * dev); -extern void device_remove_dir(struct device * dev); - -extern int bus_make_dir(struct bus_type * bus); -extern void bus_remove_dir(struct bus_type * bus); - extern int bus_add_driver(struct device_driver *); extern void bus_remove_driver(struct device_driver *); -extern int driver_make_dir(struct device_driver * drv); -extern void driver_remove_dir(struct device_driver * drv); - -extern int device_bus_link(struct device * dev); -extern void device_remove_symlink(struct driver_dir_entry * dir, const char * name); - -extern int devclass_make_dir(struct device_class *); -extern void devclass_remove_dir(struct device_class *); - -extern int devclass_drv_link(struct device_driver *); -extern void devclass_drv_unlink(struct device_driver *); - -extern int devclass_dev_link(struct device_class *, struct device *); -extern void devclass_dev_unlink(struct device_class *, struct device *); - extern int devclass_add_device(struct device *); extern void devclass_remove_device(struct device *); - -extern int intf_make_dir(struct device_interface *); -extern void intf_remove_dir(struct device_interface *); - -extern int intf_dev_link(struct intf_data *); -extern void intf_dev_unlink(struct intf_data *); extern int interface_add(struct device_class *, struct device *); extern void interface_remove(struct device_class *, struct device *); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/base/bus.c linux-2.5-bk/drivers/base/bus.c --- linux-2.5.45/drivers/base/bus.c Wed Oct 30 16:42:20 2002 +++ linux-2.5-bk/drivers/base/bus.c Thu Oct 31 22:12:44 2002 @@ -12,6 +12,7 @@ #include #include #include +#include #include "base.h" static LIST_HEAD(bus_driver_list); @@ -19,6 +20,109 @@ #define to_dev(node) container_of(node,struct device,bus_list) #define to_drv(node) container_of(node,struct device_driver,bus_list) +#define to_bus_attr(_attr) container_of(_attr,struct bus_attribute,attr) +#define to_bus(obj) container_of(obj,struct bus_type,subsys.kobj) + +/* + * sysfs bindings for drivers + */ + +#define to_drv_attr(_attr) container_of(_attr,struct driver_attribute,attr) +#define to_driver(obj) container_of(obj, struct device_driver, kobj) + + +static ssize_t +drv_attr_show(struct kobject * kobj, struct attribute * attr, + char * buf, size_t count, loff_t off) +{ + struct driver_attribute * drv_attr = to_drv_attr(attr); + struct device_driver * drv = to_driver(kobj); + ssize_t ret = 0; + + if (drv_attr->show) + ret = drv_attr->show(drv,buf,count,off); + return ret; +} + +static ssize_t +drv_attr_store(struct kobject * kobj, struct attribute * attr, + const char * buf, size_t count, loff_t off) +{ + struct driver_attribute * drv_attr = to_drv_attr(attr); + struct device_driver * drv = to_driver(kobj); + ssize_t ret = 0; + + if (drv_attr->store) + ret = drv_attr->store(drv,buf,count,off); + return ret; +} + +static struct sysfs_ops driver_sysfs_ops = { + .show = drv_attr_show, + .store = drv_attr_store, +}; + + +/* + * sysfs bindings for drivers + */ + + +static ssize_t +bus_attr_show(struct kobject * kobj, struct attribute * attr, + char * buf, size_t count, loff_t off) +{ + struct bus_attribute * bus_attr = to_bus_attr(attr); + struct bus_type * bus = to_bus(kobj); + ssize_t ret = 0; + + if (bus_attr->show) + ret = bus_attr->show(bus,buf,count,off); + return ret; +} + +static ssize_t +bus_attr_store(struct kobject * kobj, struct attribute * attr, + const char * buf, size_t count, loff_t off) +{ + struct bus_attribute * bus_attr = to_bus_attr(attr); + struct bus_type * bus = to_bus(kobj); + ssize_t ret = 0; + + if (bus_attr->store) + ret = bus_attr->store(bus,buf,count,off); + return ret; +} + +static struct sysfs_ops bus_sysfs_ops = { + .show = bus_attr_show, + .store = bus_attr_store, +}; + +int bus_create_file(struct bus_type * bus, struct bus_attribute * attr) +{ + int error; + if (get_bus(bus)) { + error = sysfs_create_file(&bus->subsys.kobj,&attr->attr); + put_bus(bus); + } else + error = -EINVAL; + return error; +} + +void bus_remove_file(struct bus_type * bus, struct bus_attribute * attr) +{ + if (get_bus(bus)) { + sysfs_remove_file(&bus->subsys.kobj,&attr->attr); + put_bus(bus); + } +} + +struct subsystem bus_subsys = { + .kobj = { .name = "bus" }, + .sysfs_ops = &bus_sysfs_ops, +}; + /** * bus_for_each_dev - walk list of devices and do something to each * @bus: bus in question @@ -92,6 +196,7 @@ pr_debug("bound device '%s' to driver '%s'\n", dev->bus_id,dev->driver->name); list_add_tail(&dev->driver_list,&dev->driver->devices); + sysfs_create_link(&dev->driver->kobj,&dev->kobj,dev->kobj.name); } static int bus_match(struct device * dev, struct device_driver * drv) @@ -162,6 +267,7 @@ static void detach(struct device * dev, struct device_driver * drv) { if (drv) { + sysfs_remove_link(&drv->kobj,dev->kobj.name); list_del_init(&dev->driver_list); devclass_remove_device(dev); if (drv->remove) @@ -206,7 +312,7 @@ list_add_tail(&dev->bus_list,&dev->bus->devices); device_attach(dev); up_write(&dev->bus->rwsem); - device_bus_link(dev); + sysfs_create_link(&bus->devsubsys.kobj,&dev->kobj,dev->bus_id); } return 0; } @@ -221,9 +327,9 @@ void bus_remove_device(struct device * dev) { if (dev->bus) { + sysfs_remove_link(&dev->bus->devsubsys.kobj,dev->bus_id); down_write(&dev->bus->rwsem); pr_debug("bus %s: remove device %s\n",dev->bus->name,dev->bus_id); - device_remove_symlink(&dev->bus->device_dir,dev->bus_id); device_detach(dev); list_del_init(&dev->bus_list); up_write(&dev->bus->rwsem); @@ -240,7 +346,6 @@ list_add_tail(&drv->bus_list,&bus->drivers); driver_attach(drv); up_write(&bus->rwsem); - driver_make_dir(drv); } return 0; } @@ -275,7 +380,6 @@ list_del_init(&bus->node); spin_unlock(&device_lock); WARN_ON(bus->present); - bus_remove_dir(bus); } int bus_register(struct bus_type * bus) @@ -286,16 +390,25 @@ atomic_set(&bus->refcount,2); bus->present = 1; + strncpy(bus->subsys.kobj.name,bus->name,KOBJ_NAME_LEN); + bus->subsys.parent = &bus_subsys; + subsystem_register(&bus->subsys); + + snprintf(bus->devsubsys.kobj.name,KOBJ_NAME_LEN,"devices"); + bus->devsubsys.parent = &bus->subsys; + subsystem_register(&bus->devsubsys); + + snprintf(bus->drvsubsys.kobj.name,KOBJ_NAME_LEN,"drivers"); + bus->drvsubsys.parent = &bus->subsys; + bus->drvsubsys.sysfs_ops = &driver_sysfs_ops; + subsystem_register(&bus->drvsubsys); + spin_lock(&device_lock); list_add_tail(&bus->node,&bus_driver_list); spin_unlock(&device_lock); pr_debug("bus type '%s' registered\n",bus->name); - - /* give it some driverfs entities */ - bus_make_dir(bus); put_bus(bus); - return 0; } @@ -306,9 +419,19 @@ spin_unlock(&device_lock); pr_debug("bus %s: unregistering\n",bus->name); + subsystem_unregister(&bus->drvsubsys); + subsystem_unregister(&bus->devsubsys); + subsystem_unregister(&bus->subsys); put_bus(bus); } +static int __init bus_subsys_init(void) +{ + return subsystem_register(&bus_subsys); +} + +core_initcall(bus_subsys_init); + EXPORT_SYMBOL(bus_for_each_dev); EXPORT_SYMBOL(bus_for_each_drv); EXPORT_SYMBOL(bus_add_device); @@ -317,3 +440,6 @@ EXPORT_SYMBOL(bus_unregister); EXPORT_SYMBOL(get_bus); EXPORT_SYMBOL(put_bus); + +EXPORT_SYMBOL(bus_create_file); +EXPORT_SYMBOL(bus_remove_file); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/base/class.c linux-2.5-bk/drivers/base/class.c --- linux-2.5.45/drivers/base/class.c Wed Oct 30 16:43:45 2002 +++ linux-2.5-bk/drivers/base/class.c Thu Oct 31 22:12:44 2002 @@ -4,10 +4,98 @@ #include #include +#include #include "base.h" static LIST_HEAD(class_list); +#define to_class_attr(_attr) container_of(_attr,struct devclass_attribute,attr) +#define to_class(obj) container_of(obj,struct device_class,subsys.kobj) + +static ssize_t +devclass_attr_show(struct kobject * kobj, struct attribute * attr, + char * buf, size_t count, loff_t off) +{ + struct devclass_attribute * class_attr = to_class_attr(attr); + struct device_class * dc = to_class(kobj); + ssize_t ret = 0; + + if (class_attr->show) + ret = class_attr->show(dc,buf,count,off); + return ret; +} + +static ssize_t +devclass_attr_store(struct kobject * kobj, struct attribute * attr, + const char * buf, size_t count, loff_t off) +{ + struct devclass_attribute * class_attr = to_class_attr(attr); + struct device_class * dc = to_class(kobj); + ssize_t ret = 0; + + if (class_attr->store) + ret = class_attr->store(dc,buf,count,off); + return ret; +} + +static struct sysfs_ops class_sysfs_ops = { + show: devclass_attr_show, + store: devclass_attr_store, +}; + +static struct subsystem class_subsys = { + .kobj = { .name = "class", }, + .sysfs_ops = &class_sysfs_ops, +}; + + +static int devclass_dev_link(struct device_class * cls, struct device * dev) +{ + char linkname[16]; + snprintf(linkname,16,"%u",dev->class_num); + return sysfs_create_link(&cls->devsubsys.kobj,&dev->kobj,linkname); +} + +static void devclass_dev_unlink(struct device_class * cls, struct device * dev) +{ + char linkname[16]; + snprintf(linkname,16,"%u",dev->class_num); + sysfs_remove_link(&cls->devsubsys.kobj,linkname); +} + +static int devclass_drv_link(struct device_driver * drv) +{ + char name[KOBJ_NAME_LEN * 3]; + snprintf(name,KOBJ_NAME_LEN * 3,"%s:%s",drv->bus->name,drv->name); + return sysfs_create_link(&drv->devclass->drvsubsys.kobj,&drv->kobj,name); +} + +static void devclass_drv_unlink(struct device_driver * drv) +{ + char name[KOBJ_NAME_LEN * 3]; + snprintf(name,KOBJ_NAME_LEN * 3,"%s:%s",drv->bus->name,drv->name); + return sysfs_remove_link(&drv->devclass->drvsubsys.kobj,name); +} + + +int devclass_create_file(struct device_class * cls, struct devclass_attribute * attr) +{ + int error; + if (cls) { + error = sysfs_create_file(&cls->subsys.kobj,&attr->attr); + } else + error = -EINVAL; + return error; +} + +void devclass_remove_file(struct device_class * cls, struct devclass_attribute * attr) +{ + if (cls) + sysfs_remove_file(&cls->subsys.kobj,&attr->attr); +} + + + int devclass_add_driver(struct device_driver * drv) { struct device_class * cls = get_devclass(drv->devclass); @@ -136,7 +224,6 @@ if (atomic_dec_and_lock(&cls->refcount,&device_lock)) { list_del_init(&cls->node); spin_unlock(&device_lock); - devclass_remove_dir(cls); } } @@ -150,10 +237,21 @@ cls->present = 1; pr_debug("device class '%s': registering\n",cls->name); + strncpy(cls->subsys.kobj.name,cls->name,KOBJ_NAME_LEN); + cls->subsys.parent = &class_subsys; + subsystem_register(&cls->subsys); + + snprintf(cls->devsubsys.kobj.name,KOBJ_NAME_LEN,"devices"); + cls->devsubsys.parent = &cls->subsys; + subsystem_register(&cls->devsubsys); + + snprintf(cls->drvsubsys.kobj.name,KOBJ_NAME_LEN,"drivers"); + cls->drvsubsys.parent = &cls->subsys; + subsystem_register(&cls->drvsubsys); + spin_lock(&device_lock); list_add_tail(&cls->node,&class_list); spin_unlock(&device_lock); - devclass_make_dir(cls); put_devclass(cls); return 0; } @@ -164,8 +262,18 @@ cls->present = 0; spin_unlock(&device_lock); pr_debug("device class '%s': unregistering\n",cls->name); + subsystem_unregister(&cls->drvsubsys); + subsystem_unregister(&cls->devsubsys); + subsystem_unregister(&cls->subsys); put_devclass(cls); } + +static int __init class_subsys_init(void) +{ + return subsystem_register(&class_subsys); +} + +core_initcall(class_subsys_init); EXPORT_SYMBOL(devclass_register); EXPORT_SYMBOL(devclass_unregister); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/base/core.c linux-2.5-bk/drivers/base/core.c --- linux-2.5.45/drivers/base/core.c Wed Oct 30 16:41:43 2002 +++ linux-2.5-bk/drivers/base/core.c Thu Oct 31 22:12:44 2002 @@ -23,7 +23,74 @@ spinlock_t device_lock = SPIN_LOCK_UNLOCKED; -#define to_dev(node) container_of(node,struct device,driver_list) +#define to_dev(obj) container_of(obj,struct device,kobj) + + +/* + * sysfs bindings for devices. + */ + +#define to_dev_attr(_attr) container_of(_attr,struct device_attribute,attr) + +extern struct attribute * dev_default_attrs[]; + +static ssize_t +dev_attr_show(struct kobject * kobj, struct attribute * attr, + char * buf, size_t count, loff_t off) +{ + struct device_attribute * dev_attr = to_dev_attr(attr); + struct device * dev = to_dev(kobj); + ssize_t ret = 0; + + if (dev_attr->show) + ret = dev_attr->show(dev,buf,count,off); + return ret; +} + +static ssize_t +dev_attr_store(struct kobject * kobj, struct attribute * attr, + const char * buf, size_t count, loff_t off) +{ + struct device_attribute * dev_attr = to_dev_attr(attr); + struct device * dev = to_dev(kobj); + ssize_t ret = 0; + + if (dev_attr->store) + ret = dev_attr->store(dev,buf,count,off); + return ret; +} + +static struct sysfs_ops dev_sysfs_ops = { + .show = dev_attr_show, + .store = dev_attr_store, +}; + +struct subsystem device_subsys = { + .kobj = { + .name = "devices", + }, + .sysfs_ops = &dev_sysfs_ops, + .default_attrs = dev_default_attrs, +}; + + +int device_create_file(struct device * dev, struct device_attribute * attr) +{ + int error = 0; + if (get_device(dev)) { + error = sysfs_create_file(&dev->kobj,&attr->attr); + put_device(dev); + } + return error; +} + +void device_remove_file(struct device * dev, struct device_attribute * attr) +{ + if (get_device(dev)) { + sysfs_remove_file(&dev->kobj,&attr->attr); + put_device(dev); + } +} int device_add(struct device *dev) { @@ -44,7 +111,11 @@ pr_debug("DEV: registering device: ID = '%s', name = %s\n", dev->bus_id, dev->name); - if ((error = device_make_dir(dev))) + strncpy(dev->kobj.name,dev->bus_id,KOBJ_NAME_LEN); + if (dev->parent) + dev->kobj.parent = &dev->parent->kobj; + dev->kobj.subsys = &device_subsys; + if ((error = kobject_register(&dev->kobj))) goto register_done; bus_add_device(dev); @@ -69,6 +140,7 @@ void device_initialize(struct device *dev) { + kobject_init(&dev->kobj); INIT_LIST_HEAD(&dev->node); INIT_LIST_HEAD(&dev->children); INIT_LIST_HEAD(&dev->g_list); @@ -157,9 +229,6 @@ bus_remove_device(dev); - /* remove the driverfs directory */ - device_remove_dir(dev); - if (dev->release) dev->release(dev); @@ -184,10 +253,21 @@ pr_debug("DEV: Unregistering device. ID = '%s', name = '%s'\n", dev->bus_id,dev->name); + kobject_unregister(&dev->kobj); put_device(dev); } +static int __init device_subsys_init(void) +{ + return subsystem_register(&device_subsys); +} + +core_initcall(device_subsys_init); + EXPORT_SYMBOL(device_register); EXPORT_SYMBOL(device_unregister); EXPORT_SYMBOL(get_device); EXPORT_SYMBOL(put_device); + +EXPORT_SYMBOL(device_create_file); +EXPORT_SYMBOL(device_remove_file); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/base/driver.c linux-2.5-bk/drivers/base/driver.c --- linux-2.5.45/drivers/base/driver.c Wed Oct 30 16:43:39 2002 +++ linux-2.5-bk/drivers/base/driver.c Thu Oct 31 22:12:44 2002 @@ -12,6 +12,29 @@ #define to_dev(node) container_of(node,struct device,driver_list) +/* + * helpers for creating driver attributes in sysfs + */ + +int driver_create_file(struct device_driver * drv, struct driver_attribute * attr) +{ + int error; + if (get_driver(drv)) { + error = sysfs_create_file(&drv->kobj,&attr->attr); + put_driver(drv); + } else + error = -EINVAL; + return error; +} + +void driver_remove_file(struct device_driver * drv, struct driver_attribute * attr) +{ + if (get_driver(drv)) { + sysfs_remove_file(&drv->kobj,&attr->attr); + put_driver(drv); + } +} + int driver_for_each_dev(struct device_driver * drv, void * data, int (*callback)(struct device *, void * )) { @@ -65,7 +88,6 @@ return; spin_unlock(&device_lock); BUG_ON(drv->present); - bus_remove_driver(drv); if (drv->release) drv->release(drv); put_bus(bus); @@ -84,6 +106,11 @@ pr_debug("driver %s:%s: registering\n",drv->bus->name,drv->name); + kobject_init(&drv->kobj); + strncpy(drv->kobj.name,drv->name,KOBJ_NAME_LEN); + drv->kobj.subsys = &drv->bus->drvsubsys; + kobject_register(&drv->kobj); + get_bus(drv->bus); atomic_set(&drv->refcount,2); rwlock_init(&drv->lock); @@ -108,3 +135,6 @@ EXPORT_SYMBOL(driver_unregister); EXPORT_SYMBOL(get_driver); EXPORT_SYMBOL(put_driver); + +EXPORT_SYMBOL(driver_create_file); +EXPORT_SYMBOL(driver_remove_file); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/base/firmware.c linux-2.5-bk/drivers/base/firmware.c --- linux-2.5.45/drivers/base/firmware.c Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/drivers/base/firmware.c Thu Oct 31 22:12:44 2002 @@ -0,0 +1,32 @@ +/* + * firmware.c - firmware subsystem hoohaw. + */ + +#include +#include +#include + +static struct subsystem firmware_subsys = { + .kobj = { .name = "firmware" }, +}; + +int firmware_register(struct subsystem * s) +{ + s->parent = &firmware_subsys; + return subsystem_register(s); +} + +void firmware_unregister(struct subsystem * s) +{ + subsystem_unregister(s); +} + +static int __init firmware_init(void) +{ + return subsystem_register(&firmware_subsys); +} + +core_initcall(firmware_init); + +EXPORT_SYMBOL(firmware_register); +EXPORT_SYMBOL(firmware_unregister); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/base/fs/Makefile linux-2.5-bk/drivers/base/fs/Makefile --- linux-2.5.45/drivers/base/fs/Makefile Wed Oct 30 16:42:56 2002 +++ linux-2.5-bk/drivers/base/fs/Makefile Thu Oct 31 22:12:44 2002 @@ -1,5 +1,5 @@ -obj-y := device.o bus.o driver.o class.o intf.o +obj-y := device.o -export-objs := device.o bus.o driver.o class.o +export-objs := device.o include $(TOPDIR)/Rules.make diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/base/fs/bus.c linux-2.5-bk/drivers/base/fs/bus.c --- linux-2.5.45/drivers/base/fs/bus.c Wed Oct 30 16:43:03 2002 +++ linux-2.5-bk/drivers/base/fs/bus.c Wed Dec 31 16:00:00 1969 @@ -1,123 +0,0 @@ -#include -#include -#include -#include -#include "fs.h" - -static struct driver_dir_entry bus_dir; - -#define to_bus_attr(_attr) container_of(_attr,struct bus_attribute,attr) - -#define to_bus(dir) container_of(dir,struct bus_type,dir) - - -/* driverfs ops for device attribute files */ - -static int -bus_attr_open(struct driver_dir_entry * dir) -{ - struct bus_type * bus = to_bus(dir); - get_bus(bus); - return 0; -} - -static int -bus_attr_close(struct driver_dir_entry * dir) -{ - struct bus_type * bus = to_bus(dir); - put_bus(bus); - return 0; -} - -static ssize_t -bus_attr_show(struct driver_dir_entry * dir, struct attribute * attr, - char * buf, size_t count, loff_t off) -{ - struct bus_attribute * bus_attr = to_bus_attr(attr); - struct bus_type * bus = to_bus(dir); - ssize_t ret = 0; - - if (bus_attr->show) - ret = bus_attr->show(bus,buf,count,off); - return ret; -} - -static ssize_t -bus_attr_store(struct driver_dir_entry * dir, struct attribute * attr, - const char * buf, size_t count, loff_t off) -{ - struct bus_attribute * bus_attr = to_bus_attr(attr); - struct bus_type * bus = to_bus(dir); - ssize_t ret = 0; - - if (bus_attr->store) - ret = bus_attr->store(bus,buf,count,off); - return ret; -} - -static struct driverfs_ops bus_attr_ops = { - .open = bus_attr_open, - .close = bus_attr_close, - .show = bus_attr_show, - .store = bus_attr_store, -}; - -int bus_create_file(struct bus_type * bus, struct bus_attribute * attr) -{ - int error; - if (get_bus(bus)) { - error = driverfs_create_file(&attr->attr,&bus->dir); - put_bus(bus); - } else - error = -EINVAL; - return error; -} - -void bus_remove_file(struct bus_type * bus, struct bus_attribute * attr) -{ - if (get_bus(bus)) { - driverfs_remove_file(&bus->dir,attr->attr.name); - put_bus(bus); - } -} - -int bus_make_dir(struct bus_type * bus) -{ - int error; - bus->dir.name = bus->name; - bus->dir.ops = &bus_attr_ops; - - error = device_create_dir(&bus->dir,&bus_dir); - if (!error) { - bus->device_dir.name = "devices"; - device_create_dir(&bus->device_dir,&bus->dir); - - bus->driver_dir.name = "drivers"; - device_create_dir(&bus->driver_dir,&bus->dir); - } - return error; -} - -void bus_remove_dir(struct bus_type * bus) -{ - /* remove driverfs entries */ - driverfs_remove_dir(&bus->driver_dir); - driverfs_remove_dir(&bus->device_dir); - driverfs_remove_dir(&bus->dir); -} - -static struct driver_dir_entry bus_dir = { - .name = "bus", - .mode = (S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO), -}; - -static int __init bus_init(void) -{ - /* make 'bus' driverfs directory */ - return driverfs_create_dir(&bus_dir,NULL); -} - -core_initcall(bus_init); - -EXPORT_SYMBOL(bus_create_file); -EXPORT_SYMBOL(bus_remove_file); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/base/fs/class.c linux-2.5-bk/drivers/base/fs/class.c --- linux-2.5.45/drivers/base/fs/class.c Wed Oct 30 16:43:08 2002 +++ linux-2.5-bk/drivers/base/fs/class.c Wed Dec 31 16:00:00 1969 @@ -1,215 +0,0 @@ -/* - * class.c - driverfs bindings for device classes. - */ - -#include -#include -#include -#include -#include -#include "fs.h" - -static struct driver_dir_entry class_dir; - - -#define to_class_attr(_attr) container_of(_attr,struct devclass_attribute,attr) - -#define to_class(d) container_of(d,struct device_class,dir) - - -static ssize_t -devclass_attr_show(struct driver_dir_entry * dir, struct attribute * attr, - char * buf, size_t count, loff_t off) -{ - struct devclass_attribute * class_attr = to_class_attr(attr); - struct device_class * dc = to_class(dir); - ssize_t ret = 0; - - if (class_attr->show) - ret = class_attr->show(dc,buf,count,off); - return ret; -} - -static ssize_t -devclass_attr_store(struct driver_dir_entry * dir, struct attribute * attr, - const char * buf, size_t count, loff_t off) -{ - struct devclass_attribute * class_attr = to_class_attr(attr); - struct device_class * dc = to_class(dir); - ssize_t ret = 0; - - if (class_attr->store) - ret = class_attr->store(dc,buf,count,off); - return ret; -} - -static struct driverfs_ops devclass_attr_ops = { - show: devclass_attr_show, - store: devclass_attr_store, -}; - -int devclass_create_file(struct device_class * dc, struct devclass_attribute * attr) -{ - int error; - if (dc) { - error = driverfs_create_file(&attr->attr,&dc->dir); - } else - error = -EINVAL; - return error; -} - -void devclass_remove_file(struct device_class * dc, struct devclass_attribute * attr) -{ - if (dc) - driverfs_remove_file(&dc->dir,attr->attr.name); -} - -/** - * devclass_dev_link - create symlink to device's directory - * @cls - device class we're a part of - * @dev - device we're linking to - * - * Create a symlink in the class's devices/ directory to @dev's - * directory in the physical hierarchy. The name is the device's - * class-enumerated value (struct device::class_num). We're - * creating: - * class//devices/ -> - * root// - * So, the link looks like: - * ../../../root// - */ -int devclass_dev_link(struct device_class * cls, struct device * dev) -{ - char linkname[16]; - char * path; - int length; - int error; - - length = get_devpath_length(dev); - length += strlen("../../../root"); - - if (length > PATH_MAX) - return -ENAMETOOLONG; - - if (!(path = kmalloc(length,GFP_KERNEL))) - return -ENOMEM; - memset(path,0,length); - strcpy(path,"../../../root"); - fill_devpath(dev,path,length); - - snprintf(linkname,16,"%u",dev->class_num); - error = driverfs_create_symlink(&cls->device_dir,linkname,path); - kfree(path); - return error; -} - -void devclass_dev_unlink(struct device_class * cls, struct device * dev) -{ - char linkname[16]; - - snprintf(linkname,16,"%u",dev->class_num); - driverfs_remove_file(&cls->device_dir,linkname); -} - -/** - * devclass_drv_link - create symlink to driver's directory - * @drv: driver we're linking up - * - * Create a symlink in the class's drivers/ directory to @drv's - * directory (in the bus's directory). It's name is : - * to prevent naming conflicts. - * - * We're creating - * class//drivers/ -> - * bus//drivers// - * So, the link looks like: - * ../../../bus//drivers/ - */ -int devclass_drv_link(struct device_driver * drv) -{ - char * name; - char * path; - int namelen; - int pathlen; - int error = 0; - - namelen = strlen(drv->name) + strlen(drv->bus->name) + 2; - name = kmalloc(namelen,GFP_KERNEL); - if (!name) - return -ENOMEM; - snprintf(name,namelen,"%s:%s",drv->bus->name,drv->name); - - pathlen = strlen("../../../bus/") + - strlen(drv->bus->name) + - strlen("/drivers/") + - strlen(drv->name) + 1; - if (!(path = kmalloc(pathlen,GFP_KERNEL))) { - error = -ENOMEM; - goto Done; - } - snprintf(path,pathlen,"%s%s%s%s", - "../../../bus/", - drv->bus->name, - "/drivers/", - drv->name); - - error = driverfs_create_symlink(&drv->devclass->driver_dir,name,path); - Done: - kfree(name); - kfree(path); - return error; -} - -void devclass_drv_unlink(struct device_driver * drv) -{ - char * name; - int length; - - length = strlen(drv->name) + strlen(drv->bus->name) + 2; - if ((name = kmalloc(length,GFP_KERNEL))) { - driverfs_remove_file(&drv->devclass->driver_dir,name); - kfree(name); - } -} - -void devclass_remove_dir(struct device_class * dc) -{ - driverfs_remove_dir(&dc->device_dir); - driverfs_remove_dir(&dc->driver_dir); - driverfs_remove_dir(&dc->dir); -} - -int devclass_make_dir(struct device_class * dc) -{ - int error; - - dc->dir.name = dc->name; - dc->dir.ops = &devclass_attr_ops; - error = device_create_dir(&dc->dir,&class_dir); - if (!error) { - dc->driver_dir.name = "drivers"; - error = device_create_dir(&dc->driver_dir,&dc->dir); - if (!error) { - dc->device_dir.name = "devices"; - error = device_create_dir(&dc->device_dir,&dc->dir); - } - if (error) - driverfs_remove_dir(&dc->dir); - } - return error; -} - -static struct driver_dir_entry class_dir = { - name: "class", - mode: (S_IRWXU | S_IRUGO | S_IXUGO), -}; - -static int __init devclass_driverfs_init(void) -{ - return driverfs_create_dir(&class_dir,NULL); -} - -core_initcall(devclass_driverfs_init); - -EXPORT_SYMBOL(devclass_create_file); -EXPORT_SYMBOL(devclass_remove_file); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/base/fs/device.c linux-2.5-bk/drivers/base/fs/device.c --- linux-2.5.45/drivers/base/fs/device.c Wed Oct 30 16:42:56 2002 +++ linux-2.5-bk/drivers/base/fs/device.c Thu Oct 31 22:12:44 2002 @@ -16,112 +16,6 @@ #include #include -static struct driver_dir_entry device_root_dir = { - .name = "root", - .mode = (S_IRWXU | S_IRUGO | S_IXUGO), -}; - -extern struct device_attribute * device_default_files[]; - -#define to_dev_attr(_attr) container_of(_attr,struct device_attribute,attr) - -#define to_device(d) container_of(d, struct device, dir) - - -/* driverfs ops for device attribute files */ - -static int -dev_attr_open(struct driver_dir_entry * dir) -{ - struct device * dev = to_device(dir); - get_device(dev); - return 0; -} - -static int -dev_attr_close(struct driver_dir_entry * dir) -{ - struct device * dev = to_device(dir); - put_device(dev); - return 0; -} - -static ssize_t -dev_attr_show(struct driver_dir_entry * dir, struct attribute * attr, - char * buf, size_t count, loff_t off) -{ - struct device_attribute * dev_attr = to_dev_attr(attr); - struct device * dev = to_device(dir); - ssize_t ret = 0; - - if (dev_attr->show) - ret = dev_attr->show(dev,buf,count,off); - return ret; -} - -static ssize_t -dev_attr_store(struct driver_dir_entry * dir, struct attribute * attr, - const char * buf, size_t count, loff_t off) -{ - struct device_attribute * dev_attr = to_dev_attr(attr); - struct device * dev = to_device(dir); - ssize_t ret = 0; - - if (dev_attr->store) - ret = dev_attr->store(dev,buf,count,off); - return ret; -} - -static struct driverfs_ops dev_attr_ops = { - .open = dev_attr_open, - .close = dev_attr_close, - .show = dev_attr_show, - .store = dev_attr_store, -}; - -/** - * device_create_file - create a driverfs file for a device - * @dev: device requesting file - * @entry: entry describing file - * - * Allocate space for file entry, copy descriptor, and create. - */ -int device_create_file(struct device * dev, struct device_attribute * entry) -{ - int error = -EINVAL; - - if (get_device(dev)) { - error = driverfs_create_file(&entry->attr,&dev->dir); - put_device(dev); - } - return error; -} - -/** - * device_remove_file - remove a device's file by name - * @dev: device requesting removal - * @name: name of the file - * - */ -void device_remove_file(struct device * dev, struct device_attribute * attr) -{ - if (dev) { - get_device(dev); - driverfs_remove_file(&dev->dir,attr->attr.name); - put_device(dev); - } -} - -/** - * device_remove_dir - remove a device's directory - * @dev: device in question - */ -void device_remove_dir(struct device * dev) -{ - if (dev) - driverfs_remove_dir(&dev->dir); -} - int get_devpath_length(struct device * dev) { int length = 1; @@ -153,92 +47,3 @@ pr_debug("%s: path = '%s'\n",__FUNCTION__,path); } -int device_bus_link(struct device * dev) -{ - char * path; - int length; - int error = 0; - - if (!dev->bus) - return 0; - - length = get_devpath_length(dev); - - /* now add the path from the bus directory - * It should be '../../..' (one to get to the bus's directory, - * one to get to the 'bus' directory, and one to get to the root - * of the fs.) - */ - length += strlen("../../../root"); - - if (length > PATH_MAX) - return -ENAMETOOLONG; - - if (!(path = kmalloc(length,GFP_KERNEL))) - return -ENOMEM; - memset(path,0,length); - - /* our relative position */ - strcpy(path,"../../../root"); - - fill_devpath(dev,path,length); - error = driverfs_create_symlink(&dev->bus->device_dir,dev->bus_id,path); - kfree(path); - return error; -} - -void device_remove_symlink(struct driver_dir_entry * dir, const char * name) -{ - driverfs_remove_file(dir,name); -} - -int device_create_dir(struct driver_dir_entry * dir, struct driver_dir_entry * parent) -{ - dir->mode = (S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO); - return driverfs_create_dir(dir,parent); -} - -/** - * device_make_dir - create a driverfs directory - * @name: name of directory - * @parent: dentry for the parent directory - * - * Do the initial creation of the device's driverfs directory - * and populate it with the one default file. - * - * This is just a helper for device_register(), as we - * don't export this function. (Yes, that means we don't allow - * devices to create subdirectories). - */ -int device_make_dir(struct device * dev) -{ - struct driver_dir_entry * parent; - struct device_attribute * entry; - int error; - int i; - - parent = dev->parent ? &dev->parent->dir : &device_root_dir; - dev->dir.name = dev->bus_id; - dev->dir.ops = &dev_attr_ops; - - if ((error = device_create_dir(&dev->dir,parent))) - return error; - - for (i = 0; (entry = *(device_default_files + i)); i++) { - if ((error = device_create_file(dev,entry))) { - device_remove_dir(dev); - break; - } - } - return error; -} - -static int device_driverfs_init(void) -{ - return driverfs_create_dir(&device_root_dir,NULL); -} - -core_initcall(device_driverfs_init); - -EXPORT_SYMBOL(device_create_file); -EXPORT_SYMBOL(device_remove_file); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/base/fs/driver.c linux-2.5-bk/drivers/base/fs/driver.c --- linux-2.5.45/drivers/base/fs/driver.c Wed Oct 30 16:41:39 2002 +++ linux-2.5-bk/drivers/base/fs/driver.c Wed Dec 31 16:00:00 1969 @@ -1,100 +0,0 @@ -#include -#include -#include -#include -#include "fs.h" - -#define to_drv_attr(_attr) container_of(_attr,struct driver_attribute,attr) - -#define to_drv(d) container_of(d, struct device_driver, dir) - - -/* driverfs ops for device attribute files */ - -static int -drv_attr_open(struct driver_dir_entry * dir) -{ - struct device_driver * drv = to_drv(dir); - get_driver(drv); - return 0; -} - -static int -drv_attr_close(struct driver_dir_entry * dir) -{ - struct device_driver * drv = to_drv(dir); - put_driver(drv); - return 0; -} - -static ssize_t -drv_attr_show(struct driver_dir_entry * dir, struct attribute * attr, - char * buf, size_t count, loff_t off) -{ - struct driver_attribute * drv_attr = to_drv_attr(attr); - struct device_driver * drv = to_drv(dir); - ssize_t ret = 0; - - if (drv_attr->show) - ret = drv_attr->show(drv,buf,count,off); - return ret; -} - -static ssize_t -drv_attr_store(struct driver_dir_entry * dir, struct attribute * attr, - const char * buf, size_t count, loff_t off) -{ - struct driver_attribute * drv_attr = to_drv_attr(attr); - struct device_driver * drv = to_drv(dir); - ssize_t ret = 0; - - if (drv_attr->store) - ret = drv_attr->store(drv,buf,count,off); - return ret; -} - -static struct driverfs_ops drv_attr_ops = { - .open = drv_attr_open, - .close = drv_attr_close, - .show = drv_attr_show, - .store = drv_attr_store, -}; - -int driver_create_file(struct device_driver * drv, struct driver_attribute * attr) -{ - int error; - if (get_driver(drv)) { - error = driverfs_create_file(&attr->attr,&drv->dir); - put_driver(drv); - } else - error = -EINVAL; - return error; -} - -void driver_remove_file(struct device_driver * drv, struct driver_attribute * attr) -{ - if (get_driver(drv)) { - driverfs_remove_file(&drv->dir,attr->attr.name); - put_driver(drv); - } -} - -/** - * driver_make_dir - create a driverfs directory for a driver - * @drv: driver in question - */ -int driver_make_dir(struct device_driver * drv) -{ - drv->dir.name = drv->name; - drv->dir.ops = &drv_attr_ops; - - return device_create_dir(&drv->dir,&drv->bus->driver_dir); -} - -void driver_remove_dir(struct device_driver * drv) -{ - driverfs_remove_dir(&drv->dir); -} - -EXPORT_SYMBOL(driver_create_file); -EXPORT_SYMBOL(driver_remove_file); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/base/fs/fs.h linux-2.5-bk/drivers/base/fs/fs.h --- linux-2.5.45/drivers/base/fs/fs.h Wed Oct 30 16:42:21 2002 +++ linux-2.5-bk/drivers/base/fs/fs.h Thu Oct 31 22:12:44 2002 @@ -1,6 +1,4 @@ -extern int device_create_dir(struct driver_dir_entry * dir, struct driver_dir_entry * parent); - int get_devpath_length(struct device * dev); void fill_devpath(struct device * dev, char * path, int length); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/base/fs/intf.c linux-2.5-bk/drivers/base/fs/intf.c --- linux-2.5.45/drivers/base/fs/intf.c Wed Oct 30 16:42:55 2002 +++ linux-2.5-bk/drivers/base/fs/intf.c Wed Dec 31 16:00:00 1969 @@ -1,54 +0,0 @@ -/* - * intf.c - driverfs glue for device interfaces - */ - -#include -#include -#include "fs.h" - -/** - * intf_dev_link - symlink from interface's directory to device's directory - * - */ -int intf_dev_link(struct intf_data * data) -{ - char linkname[16]; - char * path; - int length; - int error; - - length = get_devpath_length(data->dev); - length += strlen("../../../root"); - - if (length > PATH_MAX) - return -ENAMETOOLONG; - - if (!(path = kmalloc(length,GFP_KERNEL))) - return -ENOMEM; - memset(path,0,length); - strcpy(path,"../../../root"); - fill_devpath(data->dev,path,length); - - snprintf(linkname,16,"%u",data->intf_num); - error = driverfs_create_symlink(&data->intf->dir,linkname,path); - kfree(path); - return error; -} - -void intf_dev_unlink(struct intf_data * data) -{ - char linkname[16]; - snprintf(linkname,16,"%u",data->intf_num); - driverfs_remove_file(&data->intf->dir,linkname); -} - -void intf_remove_dir(struct device_interface * intf) -{ - driverfs_remove_dir(&intf->dir); -} - -int intf_make_dir(struct device_interface * intf) -{ - intf->dir.name = intf->name; - return device_create_dir(&intf->dir,&intf->devclass->dir); -} diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/base/interface.c linux-2.5-bk/drivers/base/interface.c --- linux-2.5.45/drivers/base/interface.c Wed Oct 30 16:41:54 2002 +++ linux-2.5-bk/drivers/base/interface.c Thu Oct 31 22:12:44 2002 @@ -88,8 +88,8 @@ static DEVICE_ATTR(power,S_IWUSR | S_IRUGO, device_read_power,device_write_power); -struct device_attribute * device_default_files[] = { - &dev_attr_name, - &dev_attr_power, +struct attribute * dev_default_attrs[] = { + &dev_attr_name.attr, + &dev_attr_power.attr, NULL, }; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/base/intf.c linux-2.5-bk/drivers/base/intf.c --- linux-2.5.45/drivers/base/intf.c Wed Oct 30 16:43:39 2002 +++ linux-2.5-bk/drivers/base/intf.c Thu Oct 31 22:12:44 2002 @@ -11,6 +11,26 @@ #define to_intf(node) container_of(node,struct device_interface,node) +/** + * intf_dev_link - symlink from interface's directory to device's directory + * + */ +static int intf_dev_link(struct intf_data * data) +{ + char linkname[16]; + + snprintf(linkname,16,"%u",data->intf_num); + return sysfs_create_link(&data->intf->kobj,&data->dev->kobj,linkname); +} + +static void intf_dev_unlink(struct intf_data * data) +{ + char linkname[16]; + snprintf(linkname,16,"%u",data->intf_num); + sysfs_remove_link(&data->intf->kobj,linkname); +} + + int interface_register(struct device_interface * intf) { struct device_class * cls = intf->devclass; @@ -18,7 +38,11 @@ if (cls) { pr_debug("register interface '%s' with class '%s\n", intf->name,cls->name); - intf_make_dir(intf); + kobject_init(&intf->kobj); + strncpy(intf->kobj.name,intf->name,KOBJ_NAME_LEN); + intf->kobj.subsys = &cls->subsys; + kobject_register(&intf->kobj); + spin_lock(&device_lock); list_add_tail(&intf->node,&cls->intf_list); spin_unlock(&device_lock); @@ -31,11 +55,10 @@ { pr_debug("unregistering interface '%s' from class '%s'\n", intf->name,intf->devclass->name); + kobject_unregister(&intf->kobj); spin_lock(&device_lock); list_del_init(&intf->node); spin_unlock(&device_lock); - - intf_remove_dir(intf); } int interface_add(struct device_class * cls, struct device * dev) diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/block/genhd.c linux-2.5-bk/drivers/block/genhd.c --- linux-2.5.45/drivers/block/genhd.c Wed Oct 30 16:42:55 2002 +++ linux-2.5-bk/drivers/block/genhd.c Thu Oct 31 22:12:45 2002 @@ -252,14 +252,6 @@ extern int blk_dev_init(void); -struct device_class disk_devclass = { - .name = "disk", -}; - -static struct bus_type disk_bus = { - name: "block", -}; - static struct gendisk *base_probe(dev_t dev, int *part, void *data) { char name[20]; @@ -268,6 +260,8 @@ return NULL; } +struct subsystem block_subsys; + int __init device_init(void) { struct blk_probe *base = kmalloc(sizeof(struct blk_probe), GFP_KERNEL); @@ -280,23 +274,116 @@ for (i = 1; i < MAX_BLKDEV; i++) probes[i] = base; blk_dev_init(); - devclass_register(&disk_devclass); - bus_register(&disk_bus); + subsystem_register(&block_subsys); return 0; } -__initcall(device_init); +subsys_initcall(device_init); + + + +/* + * kobject & sysfs bindings for block devices + */ + +#define to_disk(obj) container_of(obj,struct gendisk,kobj) + +struct disk_attribute { + struct attribute attr; + ssize_t (*show)(struct gendisk *, char *, size_t, loff_t); +}; + +static ssize_t disk_attr_show(struct kobject * kobj, struct attribute * attr, + char * page, size_t count, loff_t off) +{ + struct gendisk * disk = to_disk(kobj); + struct disk_attribute * disk_attr = container_of(attr,struct disk_attribute,attr); + ssize_t ret = 0; + if (disk_attr->show) + ret = disk_attr->show(disk,page,count,off); + return ret; +} + +static struct sysfs_ops disk_sysfs_ops = { + .show = &disk_attr_show, +}; + +static ssize_t disk_dev_read(struct gendisk * disk, + char *page, size_t count, loff_t off) +{ + dev_t base = MKDEV(disk->major, disk->first_minor); + return off ? 0 : sprintf(page, "%04x\n",base); +} +static ssize_t disk_range_read(struct gendisk * disk, + char *page, size_t count, loff_t off) +{ + return off ? 0 : sprintf(page, "%d\n",disk->minors); +} +static ssize_t disk_size_read(struct gendisk * disk, + char *page, size_t count, loff_t off) +{ + return off ? 0 : sprintf(page, "%llu\n",(unsigned long long)get_capacity(disk)); +} +static inline unsigned MSEC(unsigned x) +{ + return x * 1000 / HZ; +} +static ssize_t disk_stat_read(struct gendisk * disk, + char *page, size_t count, loff_t off) +{ + disk_round_stats(disk); + return off ? 0 : sprintf(page, + "%8u %8u %8llu %8u " + "%8u %8u %8llu %8u " + "%8u %8u %8u" + "\n", + disk->reads, disk->read_merges, (u64)disk->read_sectors, + MSEC(disk->read_ticks), + disk->writes, disk->write_merges, (u64)disk->write_sectors, + MSEC(disk->write_ticks), + disk->in_flight, MSEC(disk->io_ticks), + MSEC(disk->time_in_queue)); +} +static struct disk_attribute disk_attr_dev = { + .attr = {.name = "dev", .mode = S_IRUGO }, + .show = disk_dev_read +}; +static struct disk_attribute disk_attr_range = { + .attr = {.name = "range", .mode = S_IRUGO }, + .show = disk_range_read +}; +static struct disk_attribute disk_attr_size = { + .attr = {.name = "size", .mode = S_IRUGO }, + .show = disk_size_read +}; +static struct disk_attribute disk_attr_stat = { + .attr = {.name = "stat", .mode = S_IRUGO }, + .show = disk_stat_read +}; -EXPORT_SYMBOL(disk_devclass); +static struct attribute * default_attrs[] = { + &disk_attr_dev.attr, + &disk_attr_range.attr, + &disk_attr_size.attr, + &disk_attr_stat.attr, + NULL, +}; -static void disk_release(struct device *dev) +static void disk_release(struct kobject * kobj) { - struct gendisk *disk = dev->driver_data; + struct gendisk *disk = to_disk(kobj); kfree(disk->random); kfree(disk->part); kfree(disk); } +struct subsystem block_subsys = { + .kobj = { .name = "block" }, + .release = disk_release, + .sysfs_ops = &disk_sysfs_ops, + .default_attrs = default_attrs, +}; + struct gendisk *alloc_disk(int minors) { struct gendisk *disk = kmalloc(sizeof(struct gendisk), GFP_KERNEL); @@ -314,11 +401,9 @@ disk->minors = minors; while (minors >>= 1) disk->minor_shift++; + kobject_init(&disk->kobj); + disk->kobj.subsys = &block_subsys; INIT_LIST_HEAD(&disk->full_list); - disk->disk_dev.bus = &disk_bus; - disk->disk_dev.release = disk_release; - disk->disk_dev.driver_data = disk; - device_initialize(&disk->disk_dev); } rand_initialize_disk(disk); return disk; @@ -332,14 +417,13 @@ owner = disk->fops->owner; if (owner && !try_inc_mod_count(owner)) return NULL; - atomic_inc(&disk->disk_dev.refcount); - return disk; + return to_disk(kobject_get(&disk->kobj)); } void put_disk(struct gendisk *disk) { if (disk) - put_device(&disk->disk_dev); + kobject_put(&disk->kobj); } EXPORT_SYMBOL(alloc_disk); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/block/ll_rw_blk.c linux-2.5-bk/drivers/block/ll_rw_blk.c --- linux-2.5.45/drivers/block/ll_rw_blk.c Wed Oct 30 16:41:55 2002 +++ linux-2.5-bk/drivers/block/ll_rw_blk.c Thu Oct 31 22:12:45 2002 @@ -27,6 +27,10 @@ #include #include +/* + * Disk stats + */ +struct disk_stat dkstat; /* * For the allocated request tables @@ -1432,13 +1436,13 @@ if ((index >= DK_MAX_DISK) || (major >= DK_MAX_MAJOR)) return; - kstat.dk_drive[major][index] += new_io; + dkstat.drive[major][index] += new_io; if (rw == READ) { - kstat.dk_drive_rio[major][index] += new_io; - kstat.dk_drive_rblk[major][index] += nr_sectors; + dkstat.drive_rio[major][index] += new_io; + dkstat.drive_rblk[major][index] += nr_sectors; } else if (rw == WRITE) { - kstat.dk_drive_wio[major][index] += new_io; - kstat.dk_drive_wblk[major][index] += nr_sectors; + dkstat.drive_wio[major][index] += new_io; + dkstat.drive_wblk[major][index] += nr_sectors; } else printk(KERN_ERR "drive_stat_acct: cmd not R/W?\n"); } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/media/Kconfig linux-2.5-bk/drivers/media/Kconfig --- linux-2.5.45/drivers/media/Kconfig Wed Oct 30 16:42:20 2002 +++ linux-2.5-bk/drivers/media/Kconfig Thu Oct 31 22:12:48 2002 @@ -12,9 +12,14 @@ this are available from . - If you are interested in writing a driver for such an audio/video - device or user software interacting with such a driver, please read - the file . + This kernel includes support for the new Video for Linux Two API, + (V4L2) as well as the original system. Drivers and applications + need to be rewritten to use V4L2, but drivers for popular cards + and applications for most video capture functions already exist. + + Documentation for the original API is included in the file + Documentation/video4linux/API.html. Documentation for V4L2 is + available on the web at http://bytesex.org/v4l/ This driver is also available as a module called videodev.o ( = code which can be inserted in and removed from the running kernel diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/media/video/Kconfig linux-2.5-bk/drivers/media/video/Kconfig --- linux-2.5.45/drivers/media/video/Kconfig Wed Oct 30 16:43:42 2002 +++ linux-2.5-bk/drivers/media/video/Kconfig Thu Oct 31 22:12:49 2002 @@ -212,5 +212,17 @@ whenever you want). If you want to compile it as a module, say M here and read . +config VIDEO_SAA7134 + tristate "Philips SAA7134 support" + depends on VIDEO_DEV && PCI && I2C + ---help--- + This is a video4linux driver for Philips SAA7130/7134 based + TV cards. + + This driver is available as a module called saa7134.o ( = code + which can be inserted in and removed from the running kernel + whenever you want). If you want to compile it as a module, say M + here and read . + endmenu diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/media/video/Makefile linux-2.5-bk/drivers/media/video/Makefile --- linux-2.5.45/drivers/media/video/Makefile Wed Oct 30 16:42:26 2002 +++ linux-2.5-bk/drivers/media/video/Makefile Thu Oct 31 22:12:49 2002 @@ -5,13 +5,14 @@ # All of the (potential) objects that export symbols. # This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'. -export-objs := videodev.o bttv-if.o cpia.o video-buf.o +export-objs := videodev.o v4l2-common.o \ + bttv-if.o cpia.o video-buf.o bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o \ bttv-risc.o bttv-vbi.o zoran-objs := zr36120.o zr36120_i2c.o zr36120_mem.o -obj-$(CONFIG_VIDEO_DEV) += videodev.o +obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-common.o obj-$(CONFIG_VIDEO_BT848) += bttv.o msp3400.o tvaudio.o \ tda7432.o tda9875.o tuner.o video-buf.o @@ -34,6 +35,7 @@ obj-$(CONFIG_VIDEO_CPIA_PP) += cpia_pp.o obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o obj-$(CONFIG_VIDEO_MEYE) += meye.o +obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ tuner.o tda9887.o obj-$(CONFIG_TUNER_3036) += tuner-3036.o include $(TOPDIR)/Rules.make diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/media/video/bt848.h linux-2.5-bk/drivers/media/video/bt848.h --- linux-2.5.45/drivers/media/video/bt848.h Wed Oct 30 16:42:21 2002 +++ linux-2.5-bk/drivers/media/video/bt848.h Thu Oct 31 22:12:49 2002 @@ -347,6 +347,8 @@ #define BT848_PLL_X (1<<7) #define BT848_PLL_C (1<<6) +#define BT848_DVSIF 0x0FC + /* Bt878 register */ #define BT878_DEVCTRL 0x40 diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/media/video/bttv-cards.c linux-2.5-bk/drivers/media/video/bttv-cards.c --- linux-2.5.45/drivers/media/video/bttv-cards.c Wed Oct 30 16:42:55 2002 +++ linux-2.5-bk/drivers/media/video/bttv-cards.c Thu Oct 31 22:12:49 2002 @@ -37,15 +37,16 @@ #include "bttvp.h" #include "tuner.h" +#if 0 +# include "bt832.h" +#endif /* fwd decl */ static void boot_msp34xx(struct bttv *btv, int pin); static void hauppauge_eeprom(struct bttv *btv); static void avermedia_eeprom(struct bttv *btv); +static void osprey_eeprom(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 lt9415_audio(struct bttv *btv, struct video_audio *v, int set); @@ -58,6 +59,12 @@ static void fv2000s_audio(struct bttv *btv, struct video_audio *v, int set); static void windvr_audio(struct bttv *btv, struct video_audio *v, int set); static void rv605_muxsel(struct bttv *btv, unsigned int input); +static void eagle_muxsel(struct bttv *btv, unsigned int input); + +static int terratec_active_radio_upgrade(struct bttv *btv); +static int tea5757_read(struct bttv *btv); +static int tea5757_write(struct bttv *btv, int value); + /* config variables */ static int triton1=0; @@ -83,11 +90,11 @@ MODULE_PARM_DESC(vsfx,"set VSFX pci config bit " "[yet another chipset flaw workaround]"); MODULE_PARM(no_overlay,"i"); -MODULE_PARM(card,"1-" __MODULE_STRING(BTTV_MAX) "i"); +MODULE_PARM(card,"1-" __stringify(BTTV_MAX) "i"); MODULE_PARM_DESC(card,"specify TV/grabber card model, see CARDLIST file for a list"); -MODULE_PARM(pll,"1-" __MODULE_STRING(BTTV_MAX) "i"); +MODULE_PARM(pll,"1-" __stringify(BTTV_MAX) "i"); MODULE_PARM_DESC(pll,"specify installed crystal (0=none, 28=28 MHz, 35=35 MHz)"); -MODULE_PARM(tuner,"1-" __MODULE_STRING(BTTV_MAX) "i"); +MODULE_PARM(tuner,"1-" __stringify(BTTV_MAX) "i"); MODULE_PARM_DESC(tuner,"specify installed tuner type"); MODULE_PARM(autoload,"i"); MODULE_PARM_DESC(autoload,"automatically load i2c modules like tuner.o, default is 1 (yes)"); @@ -127,24 +134,29 @@ } cards[] __devinitdata = { { 0x13eb0070, BTTV_HAUPPAUGE878, "Hauppauge WinTV" }, { 0x39000070, BTTV_HAUPPAUGE878, "Hauppauge WinTV-D" }, - { 0x45000070, BTTV_HAUPPAUGE878, "Hauppauge WinTV/PVR" }, - { 0xff000070, BTTV_HAUPPAUGE878, "Osprey-100" }, - { 0xff010070, BTTV_HAUPPAUGE878, "Osprey-200" }, + { 0x45000070, BTTV_HAUPPAUGEPVR, "Hauppauge WinTV/PVR" }, + { 0xff000070, BTTV_OSPREY1x0, "Osprey-100" }, + { 0xff010070, BTTV_OSPREY2x0_SVID,"Osprey-200" }, + { 0xff020070, BTTV_OSPREY500, "Osprey-500" }, + { 0xff030070, BTTV_OSPREY2000, "Osprey-2000" }, + { 0xff040070, BTTV_OSPREY540, "Osprey-540" }, { 0x00011002, BTTV_ATI_TVWONDER, "ATI TV Wonder" }, { 0x00031002, BTTV_ATI_TVWONDERVE,"ATI TV Wonder/VE" }, { 0x6606107d, BTTV_WINFAST2000, "Leadtek WinFast TV 2000" }, { 0x6607107d, BTTV_WINFAST2000, "Leadtek WinFast VC 100" }, - { 0x263610b4, BTTV_STB2, "STB TV PCI FM, P/N 6000704" }, + { 0x263610b4, BTTV_STB2, "STB TV PCI FM, Gateway P/N 6000704" }, { 0x402010fc, BTTV_GVBCTV3PCI, "I-O Data Co. GV-BCTV3/PCI" }, { 0x405010fc, BTTV_GVBCTV4PCI, "I-O Data Co. GV-BCTV4/PCI" }, + { 0x407010fc, BTTV_GVBCTV5PCI, "I-O Data Co. GV-BCTV5/PCI" }, { 0x1200bd11, BTTV_PINNACLE, "Pinnacle PCTV" }, { 0x001211bd, BTTV_PINNACLE, "Pinnacle PCTV" }, { 0x001c11bd, BTTV_PINNACLE, "Pinnacle PCTV Sat" }, { 0x3000121a, BTTV_VOODOOTV_FM, "3Dfx VoodooTV FM/ VoodooTV 200" }, + { 0x3060121a, BTTV_STB2, "3Dfx VoodooTV 100/ STB OEM" }, { 0x3000144f, BTTV_MAGICTVIEW063, "(Askey Magic/others) TView99 CPH06x" }, { 0x3002144f, BTTV_MAGICTVIEW061, "(Askey Magic/others) TView99 CPH05x" }, @@ -170,6 +182,7 @@ { 0x1135153b, BTTV_TERRATVALUER, "Terratec TValue Radio" }, { 0x5018153b, BTTV_TERRATVALUE, "Terratec TValue" }, + { 0x400015b0, BTTV_ZOLTRIX_GENIE, "Zoltrix Genie TV" }, { 0x400a15b0, BTTV_ZOLTRIX_GENIE, "Zoltrix Genie TV" }, { 0x400d15b0, BTTV_ZOLTRIX_GENIE, "Zoltrix Genie TV / Radio" }, { 0x401015b0, BTTV_ZOLTRIX_GENIE, "Zoltrix Genie TV / Radio" }, @@ -225,7 +238,7 @@ needs_tvaudio: 1, tuner_type: -1, },{ - name: "STB", + name: "STB, Gateway P/N 6000699 (bt848)", video_inputs: 3, audio_inputs: 1, tuner: 0, @@ -235,7 +248,9 @@ audiomux: { 4, 0, 2, 3, 1}, no_msp34xx: 1, needs_tvaudio: 1, - tuner_type: -1, + tuner_type: TUNER_PHILIPS_NTSC, + pll: PLL_28, + has_radio: 1, },{ /* ---- card 0x04 ---------------------------------- */ @@ -357,7 +372,7 @@ audiomux: { 13, 14, 11, 7, 0, 0}, needs_tvaudio: 1, pll: PLL_28, - tuner_type: -1, + tuner_type: TUNER_PHILIPS_PAL, },{ name: "Aimslab Video Highway Xtreme (VHX)", video_inputs: 3, @@ -490,16 +505,17 @@ pll: PLL_28, tuner_type: -1, },{ - name: "Terratec Terra TV+ Version 1.0 (Bt848)/Vobis TV-Boostar", + name: "Terratec TerraTV+ Version 1.0 (Bt848)/ Terra TValue Version 1.0/ Vobis TV-Boostar", video_inputs: 3, audio_inputs: 1, tuner: 0, svhs: 2, - gpiomask: 16777215, + gpiomask: 0x1f0fff, muxsel: { 2, 3, 1, 1}, - audiomux: { 131072, 1, 1638400, 3,4}, - needs_tvaudio: 1, - tuner_type: -1, + audiomux: { 0x20000, 0x30000, 0x10000, 0, 0x40000}, + needs_tvaudio: 0, + tuner_type: TUNER_PHILIPS_PAL, + audio_hook: terratv_audio, },{ name: "Hauppauge WinCam newer (bt878)", video_inputs: 4, @@ -525,17 +541,48 @@ },{ /* ---- card 0x1c ---------------------------------- */ - name: "Terratec TerraTV+", + name: "Terratec TerraTV+ Version 1.1 (bt878)", video_inputs: 3, audio_inputs: 1, tuner: 0, svhs: 2, - gpiomask: 0x70000, + gpiomask: 0x1f0fff, muxsel: { 2, 3, 1, 1}, audiomux: { 0x20000, 0x30000, 0x10000, 0x00000, 0x40000}, - needs_tvaudio: 1, + needs_tvaudio: 0, tuner_type: TUNER_PHILIPS_PAL, audio_hook: terratv_audio, + /* GPIO wiring: + External 20 pin connector (for Active Radio Upgrade board) + gpio00: i2c-sda + gpio01: i2c-scl + gpio02: om5610-data + gpio03: om5610-clk + gpio04: om5610-wre + gpio05: om5610-stereo + gpio06: rds6588-davn + gpio07: Pin 7 n.c. + gpio08: nIOW + gpio09+10: nIOR, nSEL ?? (bt878) + gpio09: nIOR (bt848) + gpio10: nSEL (bt848) + Sound Routing: + gpio16: u2-A0 (1st 4052bt) + gpio17: u2-A1 + gpio18: u2-nEN + gpio19: u4-A0 (2nd 4052) + gpio20: u4-A1 + u4-nEN - GND + Btspy: + 00000 : Cdrom (internal audio input) + 10000 : ext. Video audio input + 20000 : TV Mono + a0000 : TV Mono/2 + 1a0000 : TV Stereo + 30000 : Radio + 40000 : Mute + */ + },{ /* Jannik Fritsch */ name: "Imagenation PXC200", @@ -583,7 +630,7 @@ needs_tvaudio: 0, tuner_type: 4, },{ - name: "Terratec TerraTValue", + name: "Terratec TerraTValue Version Bt878", video_inputs: 3, audio_inputs: 1, tuner: 0, @@ -595,17 +642,29 @@ pll: PLL_28, tuner_type: TUNER_PHILIPS_PAL, },{ - name: "Leadtek WinFast 2000", - video_inputs: 3, + name: "Leadtek WinFast 2000/ WinFast 2000 XP", + video_inputs: 4, audio_inputs: 1, tuner: 0, svhs: 2, gpiomask: 0xc33000, - muxsel: { 2, 3, 1, 1,0}, - audiomux: { 0x422000,0x001000,0x621100,0x620000,0x800000,0x620000}, + muxsel: { 2, 3, 1, 1, 0}, // TV, CVid, SVid, CVid over SVid connector + audiomux: { 0x422000,0x1000,0x0000,0x620000,0x800000}, + /* Audio Routing for "WinFast 2000 XP" (no tv stereo !) + gpio23 -- hef4052:nEnable (0x800000) + gpio12 -- hef4052:A1 + gpio13 -- hef4052:A0 + 0x0000: external audio + 0x1000: FM + 0x2000: TV + 0x3000: n.c. + Note: There exists another variant "Winfast 2000" with tv stereo !? + Note: eeprom only contains FF and pci subsystem id 107d:6606 + */ needs_tvaudio: 0, pll: PLL_28, - tuner_type: -1, + has_radio: 1, + tuner_type: 5, // default for now, gpio reads BFFF06 for Pal bg+dk audio_hook: winfast2000_audio, },{ name: "Lifeview FlyVideo 98 LR50 / Chronos Video Shuttle II", @@ -671,7 +730,7 @@ },{ /* ---- card 0x28 ---------------------------------- */ - name: "STB2", + name: "STB TV PCI FM, Gateway P/N 6000704 (bt878), 3Dfx VoodooTV 100", video_inputs: 3, audio_inputs: 1, tuner: 0, @@ -681,7 +740,9 @@ audiomux: { 4, 0, 2, 3, 1}, no_msp34xx: 1, needs_tvaudio: 1, - tuner_type: -1, + tuner_type: TUNER_PHILIPS_NTSC, + pll: PLL_28, + has_radio: 1, },{ name: "AVerMedia TVPhone 98", video_inputs: 3, @@ -810,6 +871,15 @@ no_msp34xx: 1, pll: PLL_28, tuner_type: TUNER_PHILIPS_PAL_I, + /* GPIO wiring: (different from Rev.4C !) + GPIO17: U4.A0 (first hef4052bt) + GPIO19: U4.A1 + GPIO20: U5.A1 (second hef4052bt) + GPIO21: U4.nEN + GPIO22: BT832 Reset Line + GPIO23: A5,A0, U5,nEN + Note: At i2c=0x8a is a Bt832 chip, which changes to 0x88 after being reset via GPIO22 + */ },{ name: "Eagle Wireless Capricorn2 (bt878A)", video_inputs: 4, @@ -1105,10 +1175,17 @@ svhs: 2, gpiomask: 15, muxsel: { 2, 3, 1, 1}, - audiomux: { 0, 0, 11, 7, 13, 0}, + audiomux: { 0, 0, 11, 7, 13, 0}, // TV and Radio with same GPIO ! needs_tvaudio: 1, pll: PLL_28, tuner_type: 25, + /* GPIO wiring: + GPIO0: U4.A0 (hef4052bt) + GPIO1: U4.A1 + GPIO2: U4.A1 (second hef4052bt) + GPIO3: U4.nEN, U5.A0, A5.nEN + GPIO8-15: vrd866b ? + */ },{ name: "Lifeview FlyVideo 98EZ (capture only) LR51", video_inputs: 4, @@ -1123,20 +1200,27 @@ /* ---- card 0x48 ---------------------------------- */ /* Dariusz Kowalewski */ name: "Prolink Pixelview PV-BT878P+9B (PlayTV Pro rev.9B FM+NICAM)", - video_inputs: 3, + video_inputs: 4, audio_inputs: 1, tuner: 0, svhs: 2, gpiomask: 0x3f, - muxsel: { 2, 3, 0, 1 }, + muxsel: { 2, 3, 1, 1 }, audiomux: { 0x01, 0x00, 0x03, 0x03, 0x09, 0x02 }, needs_tvaudio: 1, no_msp34xx: 1, no_tda9875: 1, pll: PLL_28, - tuner_type: -1, - audio_hook: pvbt878p9b_audio, - has_radio: 1, + tuner_type: 5, + audio_hook: pvbt878p9b_audio, // Note: not all cards have stereo + has_radio: 1, // Note: not all cards have radio + /* GPIO wiring: + GPIO0: A0 hef4052 + GPIO1: A1 hef4052 + GPIO3: nEN hef4052 + GPIO8-15: vrd866b + GPIO20,22,23: R30,R29,R28 + */ },{ /* Clay Kunz */ /* you must jumper JP5 for the card to work */ @@ -1242,6 +1326,193 @@ muxsel: { 2, 3, 1, 0}, pll: PLL_28, tuner_type: -1, +},{ + + /* ---- card 0x50 ---------------------------------- */ + name: "Hauppauge WinTV PVR", + video_inputs: 4, + audio_inputs: 1, + tuner: 0, + svhs: 2, + muxsel: { 2, 0, 1, 1}, + needs_tvaudio: 1, + pll: PLL_28, + tuner_type: -1, + + gpiomask: 7, + audiomux: {7}, +},{ + name: "GV-BCTV5/PCI", + video_inputs: 3, + audio_inputs: 1, + tuner: 0, + svhs: 2, + gpiomask: 0x010f00, + muxsel: {2, 3, 1, 0}, + audiomux: {0x10000, 0, 0x10000, 0, 0, 0}, + no_msp34xx: 1, + pll: PLL_28, + tuner_type: TUNER_PHILIPS_NTSC_M, + audio_hook: gvbctv3pci_audio, +},{ + name: "Osprey 100/150 (878)", /* 0x1(2|3)-45C6-C1 */ + video_inputs: 4, /* id-inputs-clock */ + audio_inputs: 0, + tuner: -1, + svhs: 3, + muxsel: { 3, 2, 0, 1 }, + pll: PLL_28, + tuner_type: -1, + no_msp34xx: 1, + no_tda9875: 1, + no_tda7432: 1, +},{ + name: "Osprey 100/150 (848)", /* 0x04-54C0-C1 & older boards */ + video_inputs: 3, + audio_inputs: 0, + tuner: -1, + svhs: 2, + muxsel: { 2, 3, 1 }, + pll: PLL_28, + tuner_type: -1, + no_msp34xx: 1, + no_tda9875: 1, + no_tda7432: 1, +},{ + + /* ---- card 0x54 ---------------------------------- */ + name: "Osprey 101 (848)", /* 0x05-40C0-C1 */ + video_inputs: 2, + audio_inputs: 0, + tuner: -1, + svhs: 1, + muxsel: { 3, 1 }, + pll: PLL_28, + tuner_type: -1, + no_msp34xx: 1, + no_tda9875: 1, + no_tda7432: 1, +},{ + name: "Osprey 101/151", /* 0x1(4|5)-0004-C4 */ + video_inputs: 1, + audio_inputs: 0, + tuner: -1, + svhs: -1, + muxsel: { 0 }, + pll: PLL_28, + tuner_type: -1, + no_msp34xx: 1, + no_tda9875: 1, + no_tda7432: 1, +},{ + name: "Osprey 101/151 w/ svid", /* 0x(16|17|20)-00C4-C1 */ + video_inputs: 2, + audio_inputs: 0, + tuner: -1, + svhs: 1, + muxsel: { 0, 1 }, + pll: PLL_28, + tuner_type: -1, + no_msp34xx: 1, + no_tda9875: 1, + no_tda7432: 1, +},{ + name: "Osprey 200/201/250/251", /* 0x1(8|9|E|F)-0004-C4 */ + video_inputs: 1, + audio_inputs: 1, + tuner: -1, + svhs: -1, + muxsel: { 0 }, + pll: PLL_28, + tuner_type: -1, + no_msp34xx: 1, + no_tda9875: 1, + no_tda7432: 1, +},{ + + /* ---- card 0x58 ---------------------------------- */ + name: "Osprey 200/250", /* 0x1(A|B)-00C4-C1 */ + video_inputs: 2, + audio_inputs: 1, + tuner: -1, + svhs: 1, + muxsel: { 0, 1 }, + pll: PLL_28, + tuner_type: -1, + no_msp34xx: 1, + no_tda9875: 1, + no_tda7432: 1, +},{ + name: "Osprey 210/220", /* 0x1(A|B)-04C0-C1 */ + video_inputs: 2, + audio_inputs: 1, + tuner: -1, + svhs: 1, + muxsel: { 2, 3 }, + pll: PLL_28, + tuner_type: -1, + no_msp34xx: 1, + no_tda9875: 1, + no_tda7432: 1, +},{ + name: "Osprey 500", /* 500 */ + video_inputs: 2, + audio_inputs: 1, + tuner: -1, + svhs: 1, + muxsel: { 2, 3 }, + pll: PLL_28, + tuner_type: -1, + no_msp34xx: 1, + no_tda9875: 1, + no_tda7432: 1, +},{ + name: "Osprey 540", /* 540 */ + video_inputs: 4, + audio_inputs: 1, + tuner: -1, +#if 0 /* TODO ... */ + svhs: OSPREY540_SVID_ANALOG, + muxsel: { [OSPREY540_COMP_ANALOG] = 2, + [OSPREY540_SVID_ANALOG] = 3, }, +#endif + pll: PLL_28, + tuner_type: -1, + no_msp34xx: 1, + no_tda9875: 1, + no_tda7432: 1, +#if 0 /* TODO ... */ + muxsel_hook: osprey_540_muxsel, + picture_hook: osprey_540_set_picture, +#endif +},{ + + /* ---- card 0x5C ---------------------------------- */ + name: "Osprey 2000", /* 2000 */ + video_inputs: 2, + audio_inputs: 1, + tuner: -1, + svhs: 1, + muxsel: { 2, 3 }, + pll: PLL_28, + tuner_type: -1, + no_msp34xx: 1, + no_tda9875: 1, + no_tda7432: 1, /* must avoid, conflicts with the bt860 */ +},{ + /* M G Berberich */ + name: "IDS Eagle", + video_inputs: 4, + audio_inputs: 0, + tuner: -1, + tuner_type: -1, + svhs: -1, + gpiomask: 0, + muxsel: { 0, 1, 2, 3 }, + muxsel_hook: eagle_muxsel, + no_msp34xx: 1, + no_tda9875: 1, + pll: PLL_28, }}; const int bttv_num_tvcards = (sizeof(bttv_tvcards)/sizeof(struct tvcard)); @@ -1355,11 +1626,13 @@ switch(ttype) { case 0x0: tuner=4; // None break; - case 0x4: tuner=5; // Philips PAL + case 0x2: tuner=39;// LG NTSC (newer TAPC series) TAPC-H701P + break; + case 0x4: tuner=5; // Philips PAL TPI8PSB02P, TPI8PSB12P, TPI8PSB12D or FI1216, FM1216 break; - case 0x6: tuner=37; // LG PAL (newer TAPC series) + case 0x6: tuner=37; // LG PAL (newer TAPC series) TAPC-G702P break; - case 0xC: tuner=3; // Philips SECAM(+PAL) + case 0xC: tuner=3; // Philips SECAM(+PAL) FQ1216ME or FI1216MF break; default: printk(KERN_INFO "bttv%d: FlyVideo_gpio: unknown tuner type.\n", btv->nr); @@ -1394,48 +1667,148 @@ int miro_tunermap[] = { 0,6,2,3, 4,5,6,0, 3,0,4,5, 5,2,16,1, 14,2,17,1, 4,1,4,3, 1,2,16,1, 4,4,4,4 }; int miro_fmtuner[] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, - 1,1,1,1, 1,1,1,0, 0,0,0,0, 0,0,0,0 }; + 1,1,1,1, 1,1,1,0, 0,0,0,0, 0,1,0,0 }; static void miro_pinnacle_gpio(struct bttv *btv) { - int id,msp; - - id = ((btread(BT848_GPIO_DATA)>>10) & 31) -1; - msp = bttv_I2CRead(btv, I2C_MSP3400, "MSP34xx"); - btv->tuner_type = miro_tunermap[id]; - if (0 == (btread(BT848_GPIO_DATA) & 0x20)) { - btv->has_radio = 1; - if (!miro_fmtuner[id]) { - btv->has_matchbox = 1; - btv->mbox_we = (1<<6); - btv->mbox_most = (1<<7); - btv->mbox_clk = (1<<8); - btv->mbox_data = (1<<9); - btv->mbox_mask = (1<<6)|(1<<7)|(1<<8)|(1<<9); + int id,msp,gpio; + char *info; + + btwrite(0,BT848_GPIO_OUT_EN); + gpio = btread(BT848_GPIO_DATA); + id = ((gpio>>10) & 63) -1; + msp = bttv_I2CRead(btv, I2C_MSP3400, "MSP34xx"); + if (id < 32) { + btv->tuner_type = miro_tunermap[id]; + if (0 == (gpio & 0x20)) { + btv->has_radio = 1; + if (!miro_fmtuner[id]) { + btv->has_matchbox = 1; + btv->mbox_we = (1<<6); + btv->mbox_most = (1<<7); + btv->mbox_clk = (1<<8); + btv->mbox_data = (1<<9); + btv->mbox_mask = (1<<6)|(1<<7)|(1<<8)|(1<<9); + } + } else { + btv->has_radio = 0; + } + if (-1 != msp) { + if (btv->type == BTTV_MIRO) + btv->type = BTTV_MIROPRO; + if (btv->type == BTTV_PINNACLE) + btv->type = BTTV_PINNACLEPRO; } + printk(KERN_INFO "bttv%d: miro: id=%d tuner=%d radio=%s stereo=%s\n", + btv->nr, id+1, btv->tuner_type, + !btv->has_radio ? "no" : + (btv->has_matchbox ? "matchbox" : "fmtuner"), + (-1 == msp) ? "no" : "yes"); } else { + /* new cards with microtune tuner */ + id = 63 - id; btv->has_radio = 0; + switch (id) { + case 1: + info = "PAL / mono"; + break; + case 2: + info = "PAL+SECAM / stereo"; + btv->has_radio = 1; + break; + case 3: + info = "NTSC / stereo"; + btv->has_radio = 1; + break; + case 4: + info = "PAL+SECAM / mono"; + break; + case 5: + info = "NTSC / mono"; + break; + case 6: + info = "NTSC / stereo"; + break; + default: + info = "oops: unknown card"; + break; + } + printk(KERN_INFO + "bttv%d: pinnacle/mt: id=%d info=\"%s\" radio=%s\n", + btv->nr, id, info, btv->has_radio ? "yes" : "no"); + btv->tuner_type = 33; + if (autoload) + request_module("tda9887"); + bttv_call_i2c_clients(btv,AUDC_CONFIG_PINNACLE,&id); } - if (-1 != msp) { - if (btv->type == BTTV_MIRO) - btv->type = BTTV_MIROPRO; - if (btv->type == BTTV_PINNACLE) - btv->type = BTTV_PINNACLEPRO; - } - printk(KERN_INFO "bttv%d: miro: id=%d tuner=%d radio=%s stereo=%s\n", - btv->nr, id+1, btv->tuner_type, - !btv->has_radio ? "no" : - (btv->has_matchbox ? "matchbox" : "fmtuner"), - (-1 == msp) ? "no" : "yes"); } +/* GPIO21 L: Buffer aktiv, H: Buffer inaktiv */ +#define LM1882_SYNC_DRIVE 0x200000L + +static void init_ids_eagle(struct bttv *btv) +{ + btwrite(0xFFFF37, BT848_GPIO_OUT_EN); + btwrite(0x000000, BT848_GPIO_REG_INP); + + btwrite(0x200020, BT848_GPIO_DATA); + + /* flash strobe inverter ?! */ + btwrite(0x200024, BT848_GPIO_DATA); + + /* switch sync drive off */ + btor(LM1882_SYNC_DRIVE, BT848_GPIO_DATA); + + /* set BT848 muxel to 2 */ + btaor((2)<<5, ~(2<<5), BT848_IFORM); +} + +/* Muxsel helper for the IDS Eagle. + * the eagles does not use the standard muxsel-bits but + * has its own multiplexer */ +static void eagle_muxsel(struct bttv *btv, unsigned int input) +{ + btaor((2)<<5, ~(3<<5), BT848_IFORM); + btaor((bttv_tvcards[btv->type].muxsel[input&7]&3), + ~3, BT848_GPIO_DATA); + +#if 0 + /* svhs */ + /* wake chroma ADC */ + btand(~BT848_ADC_C_SLEEP, BT848_ADC); + /* set to YC video */ + btor(BT848_CONTROL_COMP, BT848_E_CONTROL); + btor(BT848_CONTROL_COMP, BT848_O_CONTROL); +#else + /* composite */ + /* set chroma ADC to sleep */ + btor(BT848_ADC_C_SLEEP, BT848_ADC); + /* set to composite video */ + btand(~BT848_CONTROL_COMP, BT848_E_CONTROL); + btand(~BT848_CONTROL_COMP, BT848_O_CONTROL); +#endif + + /* switch sync drive off */ + btor(LM1882_SYNC_DRIVE, BT848_GPIO_DATA); +} + +/* ----------------------------------------------------------------------- */ + /* initialization part one -- before registering i2c bus */ void __devinit bttv_init_card1(struct bttv *btv) { - if (btv->type == BTTV_HAUPPAUGE || btv->type == BTTV_HAUPPAUGE878) + switch (btv->type) { + case BTTV_HAUPPAUGE: + case BTTV_HAUPPAUGE878: boot_msp34xx(btv,5); - if (btv->type == BTTV_VOODOOTV_FM) + break; + case BTTV_VOODOOTV_FM: boot_msp34xx(btv,20); + break; + case BTTV_HAUPPAUGEPVR: + pvr_boot(btv); + break; + } } /* initialization part two -- after registering i2c bus */ @@ -1443,40 +1816,43 @@ { btv->tuner_type = -1; - /* miro/pinnacle */ - if (btv->type == BTTV_MIRO || - btv->type == BTTV_MIROPRO || - btv->type == BTTV_PINNACLE || - btv->type == BTTV_PINNACLEPRO) + switch (btv->type) { + case BTTV_MIRO: + case BTTV_MIROPRO: + case BTTV_PINNACLE: + case BTTV_PINNACLEPRO: + /* miro/pinnacle */ miro_pinnacle_gpio(btv); - - if (btv->type == BTTV_FLYVIDEO_98 || - btv->type == BTTV_LIFE_FLYKIT || - btv->type == BTTV_FLYVIDEO || - btv->type == BTTV_TYPHOON_TVIEW || - btv->type == BTTV_CHRONOS_VS2 || - btv->type == BTTV_FLYVIDEO_98FM || - btv->type == BTTV_FLYVIDEO2000 || - btv->type == BTTV_FLYVIDEO98EZ || - btv->type == BTTV_CONFERENCETV || - btv->type == BTTV_LIFETEC_9415) + break; + case BTTV_FLYVIDEO_98: + case BTTV_MAXI: + case BTTV_LIFE_FLYKIT: + case BTTV_FLYVIDEO: + case BTTV_TYPHOON_TVIEW: + case BTTV_CHRONOS_VS2: + case BTTV_FLYVIDEO_98FM: + case BTTV_FLYVIDEO2000: + case BTTV_FLYVIDEO98EZ: + case BTTV_CONFERENCETV: + case BTTV_LIFETEC_9415: flyvideo_gpio(btv); - - if (btv->type == BTTV_HAUPPAUGE || btv->type == BTTV_HAUPPAUGE878) { + break; + case BTTV_HAUPPAUGE: + case BTTV_HAUPPAUGE878: + case BTTV_HAUPPAUGEPVR: /* pick up some config infos from the eeprom */ bttv_readee(btv,eeprom_data,0xa0); hauppauge_eeprom(btv); - } - - if (btv->type == BTTV_AVERMEDIA98 || btv->type == BTTV_AVPHONE98) { + break; + case BTTV_AVERMEDIA98: + case BTTV_AVPHONE98: bttv_readee(btv,eeprom_data,0xa0); avermedia_eeprom(btv); - } - - if (btv->type == BTTV_PXC200) + break; + case BTTV_PXC200: init_PXC200(btv); - - if (btv->type == BTTV_VHX) { + break; + case BTTV_VHX: btv->has_radio = 1; btv->has_matchbox = 1; btv->mbox_we = 0x20; @@ -1484,13 +1860,42 @@ btv->mbox_clk = 0x08; btv->mbox_data = 0x10; btv->mbox_mask = 0x38; - } - - if (btv->type == BTTV_MAGICTVIEW061) { - if(btv->cardid == 0x4002144f) { + break; + case BTTV_VOBIS_BOOSTAR: + case BTTV_TERRATV: + terratec_active_radio_upgrade(btv); + break; + case BTTV_MAGICTVIEW061: + if (btv->cardid == 0x3002144f) { btv->has_radio=1; printk("bttv%d: radio detected by subsystem id (CPH05x)\n",btv->nr); } + break; + case BTTV_STB2: + if (btv->cardid == 0x3060121a) { + /* Fix up entry for 3DFX VoodooTV 100, + which is an OEM STB card variant. */ + btv->has_radio=0; + btv->tuner_type=TUNER_TEMIC_NTSC; + } + break; + case BTTV_OSPREY1x0: + case BTTV_OSPREY1x0_848: + case BTTV_OSPREY101_848: + case BTTV_OSPREY1x1: + case BTTV_OSPREY1x1_SVID: + case BTTV_OSPREY2xx: + case BTTV_OSPREY2x0_SVID: + case BTTV_OSPREY2x0: + case BTTV_OSPREY500: + case BTTV_OSPREY540: + case BTTV_OSPREY2000: + bttv_readee(btv,eeprom_data,0xa0); + osprey_eeprom(btv); + break; + case BTTV_IDS_EAGLE: + init_ids_eagle(btv); + break; } /* pll configuration */ @@ -1542,6 +1947,34 @@ if (bttv_tvcards[btv->type].audio_hook) btv->audio_hook=bttv_tvcards[btv->type].audio_hook; +#if 0 + /* detect Bt832 chip for quartzsight digital camera */ + if((bttv_I2CRead(btv, I2C_BT832_ALT1, "Bt832") >=0) || + (bttv_I2CRead(btv, I2C_BT832_ALT2, "Bt832") >=0)) { + int outbits,databits; + request_module("bt832"); + + bttv_call_i2c_clients(btv, BT832_HEXDUMP, NULL); + + printk("Reset Bt832 (0x400000 for Pixelview 4E)\n"); + btwrite(0, BT848_GPIO_DATA); + outbits = btread(BT848_GPIO_OUT_EN); + databits= btread(BT848_GPIO_DATA); + btwrite(0x400000, BT848_GPIO_OUT_EN); + udelay(5); + btwrite(0x400000, BT848_GPIO_DATA); + udelay(5); + btwrite(0, BT848_GPIO_DATA); + udelay(5); + btwrite(outbits, BT848_GPIO_OUT_EN); + btwrite(databits, BT848_GPIO_DATA); + + // bt832 on pixelview changes from i2c 0x8a to 0x88 after + // being reset as above. So we must follow by this: + bttv_call_i2c_clients(btv, BT832_REATTACH, NULL); + } +#endif + /* try to detect audio/fader chips */ if (!bttv_tvcards[btv->type].no_msp34xx && bttv_I2CRead(btv, I2C_MSP3400, "MSP34xx") >=0) { @@ -1555,7 +1988,8 @@ request_module("tda9875"); } - if (bttv_I2CRead(btv, I2C_TDA7432, "TDA7432") >=0) { + if (!bttv_tvcards[btv->type].no_tda7432 && + bttv_I2CRead(btv, I2C_TDA7432, "TDA7432") >=0) { if (autoload) request_module("tda7432"); } @@ -1663,6 +2097,220 @@ btv->tuner_type, radio ? "yes" : "no"); } +static int terratec_active_radio_upgrade(struct bttv *btv) +{ + int freq; + + btv->has_radio = 1; + btv->has_matchbox = 1; + btv->mbox_we = 0x10; + btv->mbox_most = 0x20; + btv->mbox_clk = 0x08; + btv->mbox_data = 0x04; + btv->mbox_mask = 0x3c; + + btv->mbox_iow = 1 << 8; + btv->mbox_ior = 1 << 9; + btv->mbox_csel = 1 << 10; + + freq=88000/62.5; + tea5757_write(btv, 5 * freq + 0x358); // write 0x1ed8 + if (0x1ed8 == tea5757_read(btv)) { + printk("bttv%d: Terratec Active Radio Upgrade found.\n", + btv->nr); + btv->has_radio = 1; + btv->has_matchbox = 1; + } else { + btv->has_radio = 0; + btv->has_matchbox = 0; + } + return 0; +} + + +/* ----------------------------------------------------------------------- */ + +/* + * minimal bootstrap for the WinTV/PVR -- upload altera firmware. + * + * The hcwamc.rbf firmware file is on the Hauppauge driver CD. Have + * a look at Pvr/pvr45xxx.EXE (self-extracting zip archive, can be + * unpacked with unzip). + */ +static char *firm_altera = "/usr/lib/video4linux/hcwamc.rbf"; +MODULE_PARM(firm_altera,"s"); +MODULE_PARM_DESC(firm_altera,"WinTV/PVR firmware " + "(driver CD => unzip pvr45xxx.exe => hcwamc.rbf)"); + +/* drivers/sound/sound_firmware.c => soundcore.o */ +extern int mod_firmware_load(const char *fn, char **fp); + +#define PVR_GPIO_DELAY 10 + +#define BTTV_ALT_DATA 0x000001 +#define BTTV_ALT_DCLK 0x100000 +#define BTTV_ALT_NCONFIG 0x800000 + +static int __devinit pvr_altera_load(struct bttv *btv, u8 *micro, u32 microlen) +{ + u32 n; + u8 bits; + int i; + + btwrite(BTTV_ALT_DATA|BTTV_ALT_DCLK|BTTV_ALT_NCONFIG, + BT848_GPIO_OUT_EN); + btwrite(0,BT848_GPIO_DATA); + udelay(PVR_GPIO_DELAY); + + btwrite(BTTV_ALT_NCONFIG,BT848_GPIO_DATA); + udelay(PVR_GPIO_DELAY); + + for (n = 0; n < microlen; n++) { + bits = micro[n]; + for ( i = 0 ; i < 8 ; i++ ) { + btand(~BTTV_ALT_DCLK,BT848_GPIO_DATA); + if (bits & 0x01) + btor(BTTV_ALT_DATA,BT848_GPIO_DATA); + else + btand(~BTTV_ALT_DATA,BT848_GPIO_DATA); + btor(BTTV_ALT_DCLK,BT848_GPIO_DATA); + bits >>= 1; + } + } + btand(~BTTV_ALT_DCLK,BT848_GPIO_DATA); + udelay(PVR_GPIO_DELAY); + + /* begin Altera init loop (Not necessary,but doesn't hurt) */ + for (i = 0 ; i < 30 ; i++) { + btand(~BTTV_ALT_DCLK,BT848_GPIO_DATA); + btor(BTTV_ALT_DCLK,BT848_GPIO_DATA); + } + btand(~BTTV_ALT_DCLK,BT848_GPIO_DATA); + return 0; +} + +int __devinit pvr_boot(struct bttv *btv) +{ + u32 microlen; + u8 *micro; + int result; + + microlen = mod_firmware_load(firm_altera, (char**) µ); + if (!microlen) + return -1; + + printk(KERN_INFO "bttv%d: uploading altera firmware [%s] ...\n", + btv->nr, firm_altera); + result = pvr_altera_load(btv, micro, microlen); + printk(KERN_INFO "bttv%d: ... upload %s\n", + btv->nr, (result < 0) ? "failed" : "ok"); + vfree(micro); + return result; +} + +/* ----------------------------------------------------------------------- */ +/* some osprey specific stuff */ + +static void __devinit osprey_eeprom(struct bttv *btv) +{ + int i = 0; + unsigned char *ee = eeprom_data; + unsigned long serial = 0; + + if (btv->type == 0) { + /* this might be an antique... check for MMAC label in eeprom */ + if ((ee[0]=='M') && (ee[1]=='M') && (ee[2]=='A') && (ee[3]=='C')) { + unsigned char checksum = 0; + for (i =0; i<21; i++) + checksum += ee[i]; + if (checksum != ee[21]) + return; + btv->type = BTTV_OSPREY1x0_848; + for (i = 12; i < 21; i++) + serial *= 10, serial += ee[i] - '0'; + } + } else { + unsigned short type; + int offset = 4*16; + + for(; offset < 8*16; offset += 16) { + unsigned short checksum = 0; + /* verify the checksum */ + for(i = 0; i<14; i++) checksum += ee[i+offset]; + checksum = ~checksum; /* no idea why */ + if ((((checksum>>8)&0x0FF) == ee[offset+14]) && + ((checksum & 0x0FF) == ee[offset+15])) { + break; + } + } + + if (offset >= 8*16) + return; + + /* found a valid descriptor */ + type = (ee[offset+4]<<8) | (ee[offset+5]); + + switch(type) { + + /* 848 based */ + case 0x0004: + btv->type = BTTV_OSPREY1x0_848; + break; + case 0x0005: + btv->type = BTTV_OSPREY101_848; + break; + + /* 878 based */ + case 0x0012: + case 0x0013: + btv->type = BTTV_OSPREY1x0; + break; + case 0x0014: + case 0x0015: + btv->type = BTTV_OSPREY1x1; + break; + case 0x0016: + case 0x0017: + case 0x0020: + btv->type = BTTV_OSPREY1x1_SVID; + break; + case 0x0018: + case 0x0019: + case 0x001E: + case 0x001F: + btv->type = BTTV_OSPREY2xx; + break; + case 0x001A: + case 0x001B: + btv->type = BTTV_OSPREY2x0_SVID; + break; + case 0x0040: + btv->type = BTTV_OSPREY500; + break; + case 0x0050: + case 0x0056: + btv->type = BTTV_OSPREY540; + /* bttv_osprey_540_init(btv); */ + break; + case 0x0060: + case 0x0070: + btv->type = BTTV_OSPREY2x0; + //enable output on select control lines + btwrite(0x000303, BT848_GPIO_OUT_EN); + break; + default: + /* unknown...leave generic, but get serial # */ + break; + } + serial = (ee[offset+6] << 24) + | (ee[offset+7] << 16) + | (ee[offset+8] << 8) + | (ee[offset+9]); + } + + printk(KERN_INFO "bttv%d: osprey eeprom: card=%d name=%s serial=%ld\n", + btv->nr, btv->type, bttv_tvcards[btv->type].name,serial); +} /* ----------------------------------------------------------------------- */ /* AVermedia specific stuff, from bktr_card.c */ @@ -1822,17 +2470,55 @@ * Brutally hacked by Dan Sheridan djs52 8/3/00 */ -#if 0 -/* bus bits on the GPIO port */ -#define TEA_WE 6 -#define TEA_DATA 9 -#define TEA_CLK 8 -#define TEA_MOST 7 -#endif +void bus_low(struct bttv *btv, int bit) +{ + if (btv->mbox_ior) { + btor(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel, + BT848_GPIO_DATA); + udelay(5); + } + + btand(~(bit), BT848_GPIO_DATA); + udelay(5); + + if (btv->mbox_ior) { + btand(~(btv->mbox_iow | btv->mbox_csel), + BT848_GPIO_DATA); + udelay(5); + } +} + +void bus_high(struct bttv *btv, int bit) +{ + if (btv->mbox_ior) { + btor(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel, + BT848_GPIO_DATA); + udelay(5); + } + + btor((bit), BT848_GPIO_DATA); + udelay(5); -#define BUS_LOW(bit) btand(~(bit), BT848_GPIO_DATA) -#define BUS_HIGH(bit) btor((bit), BT848_GPIO_DATA) -#define BUS_IN(bit) (btread(BT848_GPIO_DATA) & (bit)) + if (btv->mbox_ior) { + btand(~(btv->mbox_iow | btv->mbox_csel), + BT848_GPIO_DATA); + udelay(5); + } +} + +int bus_in(struct bttv *btv, int bit) +{ + if (btv->mbox_ior) { + btor(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel, + BT848_GPIO_DATA); + udelay(5); + + btand(~(btv->mbox_ior | btv->mbox_csel), + BT848_GPIO_DATA); + udelay(5); + } + return btread(BT848_GPIO_DATA) & (bit); +} /* TEA5757 register bits */ #define TEA_FREQ 0:14 @@ -1871,34 +2557,41 @@ /* better safe than sorry */ btaor((btv->mbox_clk | btv->mbox_we), ~btv->mbox_mask, BT848_GPIO_OUT_EN); + + if (btv->mbox_ior) { + btor(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel, + BT848_GPIO_DATA); + udelay(5); + } + if (bttv_gpio) bttv_gpio_tracking(btv,"tea5757 read"); - BUS_LOW(btv->mbox_we); - BUS_LOW(btv->mbox_clk); + bus_low(btv,btv->mbox_we); + bus_low(btv,btv->mbox_clk); udelay(10); - for(timeout = jiffies + 10 * HZ; - BUS_IN(btv->mbox_data) && time_before(jiffies, timeout); - schedule()); /* 10 s */ - if (BUS_IN(btv->mbox_data)) { + timeout= jiffies + HZ; + + // wait for DATA line to go low; error if it doesn't + while (bus_in(btv,btv->mbox_data) && time_before(jiffies, timeout)) + schedule(); + if (bus_in(btv,btv->mbox_data)) { printk(KERN_WARNING "bttv%d: tea5757: read timeout\n",btv->nr); return -1; } - for(timeout = jiffies + HZ/5; - BUS_IN(btv->mbox_data) == 1 && time_before(jiffies, timeout); - schedule()); /* 0.2 s */ + dprintk("bttv%d: tea5757:",btv->nr); for(i = 0; i < 24; i++) { udelay(5); - BUS_HIGH(btv->mbox_clk); + bus_high(btv,btv->mbox_clk); udelay(5); - dprintk("%c",(BUS_IN(btv->mbox_most) == 0)?'T':'-'); - BUS_LOW(btv->mbox_clk); + dprintk("%c",(bus_in(btv,btv->mbox_most) == 0)?'T':'-'); + bus_low(btv,btv->mbox_clk); value <<= 1; - value |= (BUS_IN(btv->mbox_data) == 0)?0:1; /* MSB first */ - dprintk("%c", (BUS_IN(btv->mbox_most) == 0)?'S':'M'); + value |= (bus_in(btv,btv->mbox_data) == 0)?0:1; /* MSB first */ + dprintk("%c", (bus_in(btv,btv->mbox_most) == 0)?'S':'M'); } dprintk("\nbttv%d: tea5757: read 0x%X\n", btv->nr, value); return value; @@ -1911,32 +2604,42 @@ btaor(btv->mbox_clk | btv->mbox_we | btv->mbox_data, ~btv->mbox_mask, BT848_GPIO_OUT_EN); + + if (btv->mbox_ior) { + btor(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel, + BT848_GPIO_DATA); + udelay(5); + } if (bttv_gpio) bttv_gpio_tracking(btv,"tea5757 write"); + dprintk("bttv%d: tea5757: write 0x%X\n", btv->nr, value); - BUS_LOW(btv->mbox_clk); - BUS_HIGH(btv->mbox_we); + bus_low(btv,btv->mbox_clk); + bus_high(btv,btv->mbox_we); for(i = 0; i < 25; i++) { if (reg & 0x1000000) - BUS_HIGH(btv->mbox_data); + bus_high(btv,btv->mbox_data); else - BUS_LOW(btv->mbox_data); + bus_low(btv,btv->mbox_data); reg <<= 1; - BUS_HIGH(btv->mbox_clk); + bus_high(btv,btv->mbox_clk); udelay(10); - BUS_LOW(btv->mbox_clk); + bus_low(btv,btv->mbox_clk); udelay(10); } - BUS_LOW(btv->mbox_we); /* unmute !!! */ + bus_low(btv,btv->mbox_we); /* unmute !!! */ return 0; } void tea5757_set_freq(struct bttv *btv, unsigned short freq) { + int value; + + dprintk("tea5757_set_freq %d\n",freq); tea5757_write(btv, 5 * freq + 0x358); /* add 10.7MHz (see docs) */ - if (bttv_debug) - tea5757_read(btv); + value = tea5757_read(btv); + dprintk("bttv%d: tea5757 readback =0x%x\n",btv->nr,value); } @@ -2076,7 +2779,7 @@ } } - +// TDA9821 on TerraTV+ Bt848, Bt878 static void terratv_audio(struct bttv *btv, struct video_audio *v, int set) { @@ -2127,6 +2830,9 @@ * Dariusz Kowalewski * sound control for Prolink PV-BT878P+9B (PixelView PlayTV Pro FM+NICAM * revision 9B has on-board TDA9874A sound decoder). + * + * Note: There are card variants without tda9874a. Forcing the "stereo sound route" + * will mute this cards. */ static void pvbt878p9b_audio(struct bttv *btv, struct video_audio *v, int set) diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/media/video/bttv-driver.c linux-2.5-bk/drivers/media/video/bttv-driver.c --- linux-2.5.45/drivers/media/video/bttv-driver.c Wed Oct 30 16:43:34 2002 +++ linux-2.5-bk/drivers/media/video/bttv-driver.c Thu Oct 31 22:12:49 2002 @@ -23,6 +23,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include #include #include #include @@ -31,7 +32,6 @@ #include #include #include -#include #include #include @@ -74,14 +74,11 @@ /* API features (turn on/off stuff for testing) */ static unsigned int sloppy = 0; -static unsigned int mmap = 1; -#ifdef HAVE_V4L2 static unsigned int v4l2 = 1; -#endif /* insmod args */ -MODULE_PARM(radio,"1-" __MODULE_STRING(BTTV_MAX) "i"); +MODULE_PARM(radio,"1-" __stringify(BTTV_MAX) "i"); 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"); @@ -115,10 +112,7 @@ MODULE_PARM_DESC(adc_crush,"enables the luminance ADC crush, default is 1 (yes)"); MODULE_PARM(sloppy,"i"); -MODULE_PARM(mmap,"i"); -#ifdef HAVE_V4L2 MODULE_PARM(v4l2,"i"); -#endif MODULE_DESCRIPTION("bttv - v4l/v4l2 driver module for bt848/878 based cards"); MODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr"); @@ -130,74 +124,148 @@ __setup("bttv.radio=", p_radio); #endif -#ifndef HAVE_V4L2 -/* some dummy defines to avoid cluttering up the source code with - a huge number of ifdef's for V4L2 */ -# define V4L2_STD_PAL -1 -# define V4L2_STD_NTSC -1 -# define V4L2_STD_SECAM -1 -# define V4L2_STD_PAL_60 -1 -# define V4L2_STD_PAL_M -1 -# define V4L2_STD_PAL_N -1 -# define V4L2_STD_NTSC_N -1 -# define V4L2_PIX_FMT_GREY -1 -# define V4L2_PIX_FMT_HI240 -1 -# define V4L2_PIX_FMT_RGB555 -1 -# define V4L2_PIX_FMT_RGB555X -1 -# define V4L2_PIX_FMT_RGB565 -1 -# define V4L2_PIX_FMT_RGB565X -1 -# define V4L2_PIX_FMT_BGR24 -1 -# define V4L2_PIX_FMT_BGR32 -1 -# define V4L2_PIX_FMT_RGB32 -1 -# define V4L2_PIX_FMT_YUYV -1 -# define V4L2_PIX_FMT_UYVY -1 -# define V4L2_PIX_FMT_YUV422P -1 -# define V4L2_PIX_FMT_YUV420 -1 -# define V4L2_PIX_FMT_YVU420 -1 -# define V4L2_PIX_FMT_YUV411P -1 -# define V4L2_PIX_FMT_YUV410 -1 -# define V4L2_PIX_FMT_YVU410 -1 -# define V4L2_BUF_TYPE_CAPTURE -1 -# define V4L2_BUF_TYPE_VBI -1 -# define BTTV_APIS "[v4l]" -#else -# define BTTV_APIS "[v4l/v4l2]" -#endif - /* ----------------------------------------------------------------------- */ /* static data */ +/* special timing tables from conexant... */ +static u8 SRAM_Table[][60] = +{ + /* PAL digital input over GPIO[7:0] */ + { + 0x36,0x11,0x01,0x00,0x90,0x02,0x05,0x10,0x04,0x16, + 0x12,0x05,0x11,0x00,0x04,0x12,0xC0,0x00,0x31,0x00, + 0x06,0x51,0x08,0x03,0x89,0x08,0x07,0xC0,0x44,0x00, + 0x81,0x01,0x01,0xA9,0x0D,0x02,0x02,0x50,0x03,0x37, + 0x37,0x00,0xAF,0x21,0x00 + }, + /* NTSC digital input over GPIO[7:0] */ + { + 0x0C,0xC0,0x00,0x00,0x90,0x02,0x03,0x10,0x03,0x06, + 0x10,0x04,0x12,0x12,0x05,0x02,0x13,0x04,0x19,0x00, + 0x04,0x39,0x00,0x06,0x59,0x08,0x03,0x83,0x08,0x07, + 0x03,0x50,0x00,0xC0,0x40,0x00,0x86,0x01,0x01,0xA6, + 0x0D,0x02,0x03,0x11,0x01,0x05,0x37,0x00,0xAC,0x21, + 0x00, + }, +}; + const struct bttv_tvnorm bttv_tvnorms[] = { /* PAL-BDGHI */ - { V4L2_STD_PAL, 35468950, - 922, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1), - 1135, 186, 922, 0x20, 255}, - /* NTSC */ - { V4L2_STD_NTSC, 28636363, - 754, 480, 910, 0x70, 0x5d, (BT848_IFORM_NTSC|BT848_IFORM_XT0), - 910, 135, 754, 0x1a, 144}, - /* SECAM */ - { V4L2_STD_SECAM, 35468950, - 922, 576, 1135, 0x7f, 0xa0, (BT848_IFORM_SECAM|BT848_IFORM_XT1), - 1135, 186, 922, 0x20, 255}, - - /* these ones are bttv specific (for v4l1) */ - /* PAL-NC */ - { V4L2_STD_PAL_60, 28636363, - 640, 576, 910, 0x7f, 0x72, (BT848_IFORM_PAL_NC|BT848_IFORM_XT0), - 780, 130, 734, 0x1a, 144}, - /* PAL-M */ - { V4L2_STD_PAL_M, 28636363, - 640, 480, 910, 0x70, 0x5d, (BT848_IFORM_PAL_M|BT848_IFORM_XT0), - 780, 135, 754, 0x1a, 144}, - /* PAL-N */ - { V4L2_STD_PAL_N, 35468950, - 768, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_N|BT848_IFORM_XT0), - 944, 186, 922, 0x20, 144}, - /* NTSC-Japan */ - { V4L2_STD_NTSC_N, 28636363, - 640, 480, 910, 0x70, 0x5d, (BT848_IFORM_NTSC_J|BT848_IFORM_XT0), - 780, 135, 754, 0x16, 144}, + /* max. active video is actually 922, but 924 is divisible by 4 and 3! */ + /* actually, max active PAL with HSCALE=0 is 948, NTSC is 768 - nil */ + { + .v4l2_id = V4L2_STD_PAL, + .name = "PAL", + .Fsc = 35468950, + .swidth = 924, + .sheight = 576, + .totalwidth = 1135, + .adelay = 0x7f, + .bdelay = 0x72, + .iform = (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1), + .scaledtwidth = 1135, + .hdelayx1 = 186, + .hactivex1 = 924, + .vdelay = 0x20, + .vbipack = 255, + .sram = 0, + },{ + .v4l2_id = V4L2_STD_NTSC, + .name = "NTSC", + .Fsc = 28636363, + .swidth = 768, + .sheight = 480, + .totalwidth = 910, + .adelay = 0x68, + .bdelay = 0x5d, + .iform = (BT848_IFORM_NTSC|BT848_IFORM_XT0), + .scaledtwidth = 910, + .hdelayx1 = 128, + .hactivex1 = 910, + .vdelay = 0x1a, + .vbipack = 144, + .sram = 1, + },{ + .v4l2_id = V4L2_STD_SECAM, + .name = "SECAM", + .Fsc = 35468950, + .swidth = 924, + .sheight = 576, + .totalwidth = 1135, + .adelay = 0x7f, + .bdelay = 0xb0, + .iform = (BT848_IFORM_SECAM|BT848_IFORM_XT1), + .scaledtwidth = 1135, + .hdelayx1 = 186, + .hactivex1 = 922, + .vdelay = 0x20, + .vbipack = 255, + .sram = 0, /* like PAL, correct? */ + },{ + .v4l2_id = V4L2_STD_PAL_Nc, + .name = "PAL-Nc", + .Fsc = 28636363, + .swidth = 640, + .sheight = 576, + .totalwidth = 910, + .adelay = 0x68, + .bdelay = 0x5d, + .iform = (BT848_IFORM_PAL_NC|BT848_IFORM_XT0), + .scaledtwidth = 780, + .hdelayx1 = 130, + .hactivex1 = 734, + .vdelay = 0x1a, + .vbipack = 144, + .sram = -1, + },{ + .v4l2_id = V4L2_STD_PAL_M, + .name = "PAL-M", + .Fsc = 28636363, + .swidth = 640, + .sheight = 480, + .totalwidth = 910, + .adelay = 0x68, + .bdelay = 0x5d, + .iform = (BT848_IFORM_PAL_M|BT848_IFORM_XT0), + .scaledtwidth = 780, + .hdelayx1 = 135, + .hactivex1 = 754, + .vdelay = 0x1a, + .vbipack = 144, + .sram = -1, + },{ + .v4l2_id = V4L2_STD_PAL_N, + .name = "PAL-N", + .Fsc 35468950, + .swidth = 768, + .sheight = 576, + .totalwidth = 1135, + .adelay = 0x7f, + .bdelay = 0x72, + .iform = (BT848_IFORM_PAL_N|BT848_IFORM_XT1), + .scaledtwidth = 944, + .hdelayx1 = 186, + .hactivex1 = 922, + .vdelay = 0x20, + .vbipack = 144, + .sram = -1, + },{ + .v4l2_id = V4L2_STD_NTSC_M_JP, + .name = "NTSC-JP", + .Fsc = 28636363, + .swidth = 640, + .sheight = 480, + .totalwidth = 910, + .adelay = 0x68, + .bdelay = 0x5d, + .iform = (BT848_IFORM_NTSC_J|BT848_IFORM_XT0), + .scaledtwidth = 780, + .hdelayx1 = 135, + .hactivex1 = 754, + .vdelay = 0x16, + .vbipack = 144, + .sram = -1, + } }; const int BTTV_TVNORMS = (sizeof(bttv_tvnorms)/sizeof(struct bttv_tvnorm)); @@ -360,7 +428,6 @@ /* ----------------------------------------------------------------------- */ -#ifdef HAVE_V4L2 #define V4L2_CID_PRIVATE_CHROMA_AGC (V4L2_CID_PRIVATE_BASE + 0) #define V4L2_CID_PRIVATE_COMBFILTER (V4L2_CID_PRIVATE_BASE + 1) #define V4L2_CID_PRIVATE_AUTOMUTE (V4L2_CID_PRIVATE_BASE + 2) @@ -382,8 +449,6 @@ step: 256, default_value: 32768, type: V4L2_CTRL_TYPE_INTEGER, - category: V4L2_CTRL_CAT_VIDEO, - group: "Video", },{ id: V4L2_CID_CONTRAST, name: "Contrast", @@ -392,8 +457,6 @@ step: 128, default_value: 32768, type: V4L2_CTRL_TYPE_INTEGER, - category: V4L2_CTRL_CAT_VIDEO, - group: "Video", },{ id: V4L2_CID_SATURATION, name: "Saturation", @@ -402,8 +465,6 @@ step: 128, default_value: 32768, type: V4L2_CTRL_TYPE_INTEGER, - category: V4L2_CTRL_CAT_VIDEO, - group: "Video", },{ id: V4L2_CID_HUE, name: "Hue", @@ -412,8 +473,6 @@ step: 256, default_value: 32768, type: V4L2_CTRL_TYPE_INTEGER, - category: V4L2_CTRL_CAT_VIDEO, - group: "Video", }, /* --- audio --- */ { @@ -422,8 +481,6 @@ minimum: 0, maximum: 1, type: V4L2_CTRL_TYPE_BOOLEAN, - category: V4L2_CTRL_CAT_AUDIO, - group: "Audio", },{ id: V4L2_CID_AUDIO_VOLUME, name: "Volume", @@ -432,8 +489,6 @@ step: 65535/100, default_value: 65535, type: V4L2_CTRL_TYPE_INTEGER, - category: V4L2_CTRL_CAT_AUDIO, - group: "Audio", },{ id: V4L2_CID_AUDIO_BALANCE, name: "Balance", @@ -442,8 +497,6 @@ step: 65535/100, default_value: 32768, type: V4L2_CTRL_TYPE_INTEGER, - category: V4L2_CTRL_CAT_AUDIO, - group: "Audio", },{ id: V4L2_CID_AUDIO_BASS, name: "Bass", @@ -452,8 +505,6 @@ step: 65535/100, default_value: 32768, type: V4L2_CTRL_TYPE_INTEGER, - category: V4L2_CTRL_CAT_AUDIO, - group: "Audio", },{ id: V4L2_CID_AUDIO_TREBLE, name: "Treble", @@ -462,8 +513,6 @@ step: 65535/100, default_value: 32768, type: V4L2_CTRL_TYPE_INTEGER, - category: V4L2_CTRL_CAT_AUDIO, - group: "Audio", }, /* --- private --- */ { @@ -472,39 +521,33 @@ minimum: 0, maximum: 1, type: V4L2_CTRL_TYPE_BOOLEAN, - group: "Private", },{ id: V4L2_CID_PRIVATE_COMBFILTER, name: "combfilter", minimum: 0, maximum: 1, type: V4L2_CTRL_TYPE_BOOLEAN, - group: "Private", },{ id: V4L2_CID_PRIVATE_AUTOMUTE, name: "automute", minimum: 0, maximum: 1, type: V4L2_CTRL_TYPE_BOOLEAN, - group: "Private", },{ id: V4L2_CID_PRIVATE_LUMAFILTER, name: "luma decimation filter", minimum: 0, maximum: 1, type: V4L2_CTRL_TYPE_BOOLEAN, - group: "Private", },{ id: V4L2_CID_PRIVATE_AGC_CRUSH, name: "agc crush", minimum: 0, maximum: 1, type: V4L2_CTRL_TYPE_BOOLEAN, - group: "Private", } }; const int BTTV_CTLS = (sizeof(bttv_ctls)/sizeof(struct v4l2_queryctrl)); -#endif /* HAVE_V4L2 */ /* ----------------------------------------------------------------------- */ /* resource management */ @@ -558,57 +601,6 @@ } /* ----------------------------------------------------------------------- */ -/* - * sanity check for video framebuffer address ranges (overlay). - * let's see if that address range actually belongs to some - * pci display adapter. - * - * FIXME: stuff isn't portable. It's also a v4l API bug, pass a - * physical address in VIDIOCSFBUF isn't portable too ... - */ - -static int -find_videomem(unsigned long from, unsigned long to) -{ -#if PCI_DMA_BUS_IS_PHYS - struct pci_dev *dev = NULL; - int i,match,found; - - found = 0; - dprintk(KERN_DEBUG "bttv: checking video framebuffer address" - " (%lx-%lx)\n",from,to); - pci_for_each_dev(dev) { - if (dev->class != PCI_CLASS_NOT_DEFINED_VGA && - dev->class >> 16 != PCI_BASE_CLASS_DISPLAY) - continue; - dprintk(KERN_DEBUG - " pci display adapter %04x:%04x at %02x:%02x.%x\n", - dev->vendor,dev->device,dev->bus->number, - PCI_SLOT(dev->devfn),PCI_FUNC(dev->devfn)); - for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { - if (!(dev->resource[i].flags & IORESOURCE_MEM)) - continue; - if (dev->resource[i].flags & IORESOURCE_READONLY) - continue; - match = (from >= dev->resource[i].start) && - (to-1 <= dev->resource[i].end); - if (match) - found = 1; - dprintk(KERN_DEBUG " memory at %08lx-%08lx%s\n", - dev->resource[i].start, - dev->resource[i].end, - match ? " (check passed)" : ""); - } - } - return found; -#else - /* Hmm, the physical address passed to us is probably bogous */ - dprintk(KERN_DEBUG "bttv: no overlay for this arch, sorry\n"); - return 0; -#endif -} - -/* ----------------------------------------------------------------------- */ /* If Bt848a or Bt849, use PLL for PAL/SECAM and crystal for NTSC */ /* Frequency = (F_input / PLL_X) * PLL_I.PLL_F/PLL_C @@ -688,6 +680,34 @@ return -1; } +/* used to switch between the bt848's analog/digital video capture modes */ +void bt848A_set_timing(struct bttv *btv) +{ + u8 dvsif_val = 0; + int table_idx = bttv_tvnorms[btv->tvnorm].sram; + int i; + + /* timing change...reset timing generator address */ + btwrite(0x00, BT848_TGCTRL); + btwrite(0x02, BT848_TGCTRL); + btwrite(0x00, BT848_TGCTRL); + + if (btv->digital_video && -1 != table_idx) { + dprintk("bttv%d: load digital timing table (table_idx=%d)\n", + btv->nr,table_idx); + dvsif_val = 0x41; + for(i = 0; i < 51; i++) + btwrite(SRAM_Table[table_idx][i],BT848_TGLB); + + btwrite(0x75,BT848_PLL_F_LO); + btwrite(0x50,BT848_PLL_F_HI); + btwrite(0x8B,BT848_PLL_XCI); + btwrite(0x11,BT848_TGCTRL); + btv->pll.pll_current = 0; + } + btwrite(dvsif_val,BT848_DVSIF); +} + /* ----------------------------------------------------------------------- */ static void bt848_bright(struct bttv *btv, int bright) @@ -813,9 +833,11 @@ i2c_mux = mux = (btv->audio & AUDIO_MUTE) ? AUDIO_OFF : btv->audio; if (btv->opt_automute && !signal && !btv->radio_user) mux = AUDIO_OFF; +#if 0 printk("bttv%d: amux: mode=%d audio=%d signal=%s mux=%d/%d irq=%s\n", btv->nr, mode, btv->audio, signal ? "yes" : "no", mux, i2c_mux, in_interrupt() ? "yes" : "no"); +#endif val = bttv_tvcards[btv->type].audiomux[mux]; btaor(val,~bttv_tvcards[btv->type].gpiomask, BT848_GPIO_DATA); @@ -856,9 +878,24 @@ BT848_IFORM); btwrite(tvnorm->vbipack, BT848_VBI_PACK_SIZE); btwrite(1, BT848_VBI_PACK_DEL); - - btv->pll.pll_ofreq = tvnorm->Fsc; - set_pll(btv); + + if (btv->digital_video) { + bt848A_set_timing(btv); + } else { + btv->pll.pll_ofreq = tvnorm->Fsc; + set_pll(btv); + } + + switch (btv->type) { + case BTTV_VOODOOTV_FM: + bttv_tda9880_setnorm(btv,norm); + break; +#if 0 + case BTTV_OSPREY540: + osprey_540_set_norm(btv,norm); + break; +#endif + } return 0; } @@ -924,7 +961,6 @@ set_input(btv,btv->input); } -#ifdef HAVE_V4L2 static int get_control(struct bttv *btv, struct v4l2_control *c) { struct video_audio va; @@ -935,7 +971,7 @@ break; if (i == BTTV_CTLS) return -EINVAL; - if (bttv_ctls[i].category == V4L2_CTRL_CAT_AUDIO) { + if (i >= 4 && i <= 8) { memset(&va,0,sizeof(va)); bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); if (btv->audio_hook) @@ -1002,7 +1038,7 @@ break; if (i == BTTV_CTLS) return -EINVAL; - if (bttv_ctls[i].category == V4L2_CTRL_CAT_AUDIO) { + if (i >= 4 && i <= 8) { memset(&va,0,sizeof(va)); bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); if (btv->audio_hook) @@ -1074,14 +1110,13 @@ default: return -EINVAL; } - if (bttv_ctls[i].category == V4L2_CTRL_CAT_AUDIO) { + if (i >= 4 && i <= 8) { bttv_call_i2c_clients(btv, VIDIOCSAUDIO, &va); if (btv->audio_hook) btv->audio_hook(btv,&va,1); } return 0; } -#endif /* HAVE_V4L2 */ /* ----------------------------------------------------------------------- */ @@ -1098,9 +1133,9 @@ { int need_count = 0; - if (locked_btres(btv,RESOURCE_STREAMING)) + if (locked_btres(btv,RESOURCE_VIDEO)) need_count++; - if (btv->vbi.users) + if (locked_btres(btv,RESOURCE_VBI)) need_count++; if (need_count) { @@ -1127,7 +1162,6 @@ return NULL; } -#ifdef HAVE_V4L2 static const struct bttv_format* format_by_fourcc(int fourcc) { @@ -1141,7 +1175,6 @@ } return NULL; } -#endif /* ----------------------------------------------------------------------- */ /* misc helpers */ @@ -1173,7 +1206,8 @@ static int bttv_prepare_buffer(struct bttv *btv, struct bttv_buffer *buf, const struct bttv_format *fmt, - int width, int height, int field) + int width, int height, + enum v4l2_field field) { int redo_dma_risc = 0; int rc; @@ -1196,8 +1230,6 @@ buf->vb.size = (width * height * fmt->depth) >> 3; if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) return -EINVAL; - field = bttv_buffer_field(btv, field, VBUF_FIELD_EVEN, - btv->tvnorm, height); } /* alloc + fill struct bttv_buffer (if changed) */ @@ -1252,13 +1284,14 @@ } static int -buffer_prepare(struct file *file, struct videobuf_buffer *vb, int field) +buffer_prepare(struct file *file, struct videobuf_buffer *vb) { struct bttv_buffer *buf = (struct bttv_buffer*)vb; struct bttv_fh *fh = file->private_data; return bttv_prepare_buffer(fh->btv,buf,fh->buf.fmt, - fh->buf.vb.width,fh->buf.vb.height,field); + fh->buf.vb.width,fh->buf.vb.height, + fh->buf.vb.field); } static void @@ -1280,7 +1313,7 @@ bttv_dma_free(fh->btv,buf); } -static struct videobuf_queue_ops bttv_qops = { +static struct videobuf_queue_ops bttv_video_qops = { buf_setup: buffer_setup, buf_prepare: buffer_prepare, buf_queue: buffer_queue, @@ -1295,20 +1328,6 @@ "SMICROCODE", "GVBIFMT", "SVBIFMT" }; #define V4L1_IOCTLS (sizeof(v4l1_ioctls)/sizeof(char*)) -static const char *v4l2_ioctls[] = { - "QUERYCAP", "1", "ENUM_PIXFMT", "ENUM_FBUFFMT", "G_FMT", "S_FMT", - "G_COMP", "S_COMP", "REQBUFS", "QUERYBUF", "G_FBUF", "S_FBUF", - "G_WIN", "S_WIN", "PREVIEW", "QBUF", "16", "DQBUF", "STREAMON", - "STREAMOFF", "G_PERF", "G_PARM", "S_PARM", "G_STD", "S_STD", - "ENUMSTD", "ENUMINPUT", "G_CTRL", "S_CTRL", "G_TUNER", "S_TUNER", - "G_FREQ", "S_FREQ", "G_AUDIO", "S_AUDIO", "35", "QUERYCTRL", - "QUERYMENU", "G_INPUT", "S_INPUT", "ENUMCVT", "41", "42", "43", - "44", "45", "G_OUTPUT", "S_OUTPUT", "ENUMOUTPUT", "G_AUDOUT", - "S_AUDOUT", "ENUMFX", "G_EFFECT", "S_EFFECT", "G_MODULATOR", - "S_MODULATOR" -}; -#define V4L2_IOCTLS (sizeof(v4l2_ioctls)/sizeof(char*)) - int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) { switch (cmd) { @@ -1317,18 +1336,12 @@ /* *** v4l1 *** ************************************************ */ case VIDIOCGFREQ: -#ifdef HAVE_V4L2 - case VIDIOC_G_FREQ: -#endif { unsigned long *freq = arg; *freq = btv->freq; return 0; } case VIDIOCSFREQ: -#ifdef HAVE_V4L2 - case VIDIOC_S_FREQ: -#endif { unsigned long *freq = arg; down(&btv->lock); @@ -1456,32 +1469,30 @@ return 0; } -#ifdef HAVE_V4L2 /* *** v4l2 *** ************************************************ */ case VIDIOC_ENUMSTD: { - struct v4l2_enumstd *e = arg; - + struct v4l2_standard *e = arg; + if (e->index < 0 || e->index >= BTTV_TVNORMS) return -EINVAL; - v4l2_video_std_construct(&e->std, bttv_tvnorms[e->index].v4l2_id, 0); - e->inputs = 0x0f; - e->outputs = 0x00; + v4l2_video_std_construct(e, bttv_tvnorms[e->index].v4l2_id, + bttv_tvnorms[e->index].name); return 0; } case VIDIOC_G_STD: { - struct v4l2_standard *s = arg; - v4l2_video_std_construct(s,bttv_tvnorms[btv->tvnorm].v4l2_id,0); + v4l2_std_id *id = arg; + *id = bttv_tvnorms[btv->tvnorm].v4l2_id; return 0; } case VIDIOC_S_STD: { - struct v4l2_standard *s = arg; - int i, id = v4l2_video_std_confirm(s); + v4l2_std_id *id = arg; + int i; - for(i = 0; i < BTTV_TVNORMS; i++) - if (id == bttv_tvnorms[i].v4l2_id) + for (i = 0; i < BTTV_TVNORMS; i++) + if (*id & bttv_tvnorms[i].v4l2_id) break; if (i == BTTV_TVNORMS) return -EINVAL; @@ -1492,6 +1503,16 @@ up(&btv->lock); return 0; } + case VIDIOC_QUERYSTD: + { + v4l2_std_id *id = arg; + + if (btread(BT848_DSTATUS) & BT848_DSTATUS_NUML) + *id = V4L2_STD_625_50; + else + *id = V4L2_STD_525_60; + return 0; + } case VIDIOC_ENUMINPUT: { @@ -1500,12 +1521,10 @@ if (i->index >= bttv_tvcards[btv->type].video_inputs) return -EINVAL; i->type = V4L2_INPUT_TYPE_CAMERA; - i->capability = 0; - i->assoc_audio = 0; if (i->index == bttv_tvcards[btv->type].tuner) { sprintf(i->name, "Television"); - i->type = V4L2_INPUT_TYPE_TUNER; - i->capability = V4L2_INPUT_CAP_AUDIO; + i->type = V4L2_INPUT_TYPE_TUNER; + i->tuner = 0; } else if (i->index==bttv_tvcards[btv->type].svhs) { sprintf(i->name, "S-Video"); } else { @@ -1532,14 +1551,17 @@ return 0; } - case VIDIOC_G_TUNER: { + case VIDIOC_G_TUNER: + { struct v4l2_tuner *t = arg; + if (-1 == bttv_tvcards[btv->type].tuner) + return -EINVAL; + if (0 != t->index) + return -EINVAL; down(&btv->lock); memset(t,0,sizeof(*t)); - t->input = bttv_tvcards[btv->type].tuner; strcpy(t->name, "Television"); - v4l2_video_std_construct(&t->std, bttv_tvnorms[btv->tvnorm].v4l2_id, 0); t->capability = V4L2_TUNER_CAP_NORM; t->rangehigh = 0xffffffffUL; t->rxsubchans = V4L2_TUNER_SUB_MONO; @@ -1563,10 +1585,13 @@ up(&btv->lock); return 0; } - case VIDIOC_S_TUNER: { + case VIDIOC_S_TUNER: + { struct v4l2_tuner *t = arg; - - if(t->input!=bttv_tvcards[btv->type].tuner) + + if (-1 == bttv_tvcards[btv->type].tuner) + return -EINVAL; + if (0 != t->index) return -EINVAL; down(&btv->lock); { @@ -1587,7 +1612,32 @@ up(&btv->lock); return 0; } -#endif /* HAVE_V4L2 */ + + case VIDIOC_G_FREQUENCY: + { + struct v4l2_frequency *f = arg; + + memset(f,0,sizeof(*f)); + f->type = V4L2_TUNER_ANALOG_TV; + f->frequency = btv->freq; + return 0; + } + case VIDIOC_S_FREQUENCY: + { + struct v4l2_frequency *f = arg; + + if (unlikely(f->tuner != 0)) + return -EINVAL; + if (unlikely(f->type != V4L2_TUNER_ANALOG_TV)) + return -EINVAL; + down(&btv->lock); + btv->freq = f->frequency; + bttv_call_i2c_clients(btv,VIDIOCSFREQ,&btv->freq); + if (btv->has_matchbox && btv->radio_user) + tea5757_set_freq(btv,btv->freq); + up(&btv->lock); + return 0; + } default: return -ENOIOCTLCMD; @@ -1596,55 +1646,88 @@ return 0; } -static int setup_window(struct bttv_fh *fh, struct bttv *btv, - int x, int y, int width, int height, - struct video_clip *user_clips, int nclips) +static int verify_window(const struct bttv_tvnorm *tvn, + struct v4l2_window *win, int fixup) { - struct video_clip *clips = NULL; - int n,size,retval = 0; + enum v4l2_field field; + int maxw, maxh; - if (width < 48 || - height < 32 || - width > bttv_tvnorms[btv->tvnorm].swidth || - height > bttv_tvnorms[btv->tvnorm].sheight || - NULL == fh->ovfmt) + if (win->w.width < 48 || win->w.height < 32) return -EINVAL; - if (nclips > 2048) + if (win->clipcount > 2048) + return -EINVAL; + + field = win->field; + maxw = tvn->swidth; + maxh = tvn->sheight; + + if (V4L2_FIELD_ANY == field) { + field = (win->w.height > maxh/2) + ? V4L2_FIELD_INTERLACED + : V4L2_FIELD_TOP; + } + switch (field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + maxh = maxh / 2; + break; + case V4L2_FIELD_INTERLACED: + break; + default: return -EINVAL; + } + + if (!fixup && (win->w.width > maxw || win->w.height > maxh)) + return -EINVAL; + + if (win->w.width > maxw) + win->w.width = maxw; + if (win->w.height > maxh) + win->w.height = maxh; + win->field = field; + return 0; +} + +static int setup_window(struct bttv_fh *fh, struct bttv *btv, + struct v4l2_window *win, int fixup) +{ + struct v4l2_clip *clips = NULL; + int n,size,retval = 0; + + retval = verify_window(&bttv_tvnorms[btv->tvnorm],win,fixup); + if (0 != retval) + return retval; /* copy clips -- luckily v4l1 + v4l2 are binary compatible here ...*/ - n = nclips; + n = win->clipcount; size = sizeof(struct video_clip)*(n+4); clips = kmalloc(size,GFP_KERNEL); if (NULL == clips) return -ENOMEM; if (n > 0) { - if (copy_from_user(clips,user_clips, - sizeof(struct video_clip)*nclips)) { + if (copy_from_user(clips,win->clips, + sizeof(struct v4l2_clip)*win->clipcount)) { kfree(clips); return -EFAULT; } } /* clip against screen */ if (NULL != btv->fbuf.base) - n = bttv_screen_clips(&btv->fbuf, x, y, - width, height, - clips, n); - bttv_sort_clips(clips,nclips); + n = bttv_screen_clips(btv->fbuf.width, btv->fbuf.width, + &win->w, clips, n); + bttv_sort_clips(clips,n); - down(&fh->q.lock); + down(&fh->cap.lock); if (fh->ov.clips) kfree(fh->ov.clips); fh->ov.clips = clips; - fh->ov.nclips = nclips; + fh->ov.nclips = n; - fh->ov.x = x; - fh->ov.y = y; - fh->ov.width = width; - fh->ov.height = height; - btv->init.ov.width = width; - btv->init.ov.height = height; + fh->ov.w = win->w; + fh->ov.field = win->field; + btv->init.ov.w.width = win->w.width; + btv->init.ov.w.height = win->w.height; /* update overlay if needed */ retval = 0; @@ -1655,10 +1738,205 @@ bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new); retval = bttv_switch_overlay(btv,fh,new); } - up(&fh->q.lock); + up(&fh->cap.lock); return retval; } +/* ----------------------------------------------------------------------- */ + +static struct videobuf_queue* bttv_queue(struct bttv_fh *fh) +{ + struct videobuf_queue* q = NULL; + + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + q = &fh->cap; + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + q = &fh->vbi; + break; + default: + BUG(); + } + return q; +} + +static int bttv_resource(struct bttv_fh *fh) +{ + int res = 0; + + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + res = RESOURCE_VIDEO; + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + res = RESOURCE_VBI; + break; + default: + BUG(); + } + return res; +} + +static int bttv_switch_type(struct bttv_fh *fh, enum v4l2_buf_type type) +{ + struct videobuf_queue *q = bttv_queue(fh); + int res = bttv_resource(fh); + + if (check_btres(fh,res)) + return -EBUSY; + if (videobuf_queue_is_busy(q)) + return -EBUSY; + fh->type = type; + return 0; +} + +static int bttv_g_fmt(struct bttv_fh *fh, struct v4l2_format *f) +{ + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + memset(&f->fmt.pix,0,sizeof(struct v4l2_pix_format)); + f->fmt.pix.width = fh->buf.vb.width; + f->fmt.pix.height = fh->buf.vb.height; + f->fmt.pix.pixelformat = fh->buf.fmt->fourcc; + f->fmt.pix.sizeimage = + (fh->buf.vb.width*fh->buf.vb.height*fh->buf.fmt->depth)/8; + return 0; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + memset(&f->fmt.win,0,sizeof(struct v4l2_window)); + f->fmt.win.w = fh->ov.w; + return 0; + case V4L2_BUF_TYPE_VBI_CAPTURE: + bttv_vbi_fmt(fh,f); + return 0; + default: + return -EINVAL; + } +} + +static int bttv_try_fmt(struct bttv_fh *fh, struct bttv *btv, + struct v4l2_format *f) +{ + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + { + const struct bttv_format *fmt; + enum v4l2_field field; + int maxw,maxh; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (NULL == fmt) + return -EINVAL; + if (0 != f->fmt.pix.bytesperline) + /* FIXME -- not implemented yet */ + return -EINVAL; + + /* fixup format */ + maxw = bttv_tvnorms[btv->tvnorm].swidth; + maxh = bttv_tvnorms[btv->tvnorm].sheight; + field = f->fmt.pix.field; + if (V4L2_FIELD_ANY == field) + field = (f->fmt.pix.height > maxh/2) + ? V4L2_FIELD_INTERLACED + : V4L2_FIELD_BOTTOM; + if (V4L2_FIELD_SEQ_BT == field) + field = V4L2_FIELD_SEQ_TB; + switch (field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + maxh = maxh/2; + break; + case V4L2_FIELD_INTERLACED: + break; + case V4L2_FIELD_SEQ_TB: + if (fmt->flags & FORMAT_FLAGS_PLANAR) + return -EINVAL; + default: + return -EINVAL; + } + + /* update data for the application */ + f->fmt.pix.field = field; + if (f->fmt.pix.width < 48) + f->fmt.pix.width = 48; + if (f->fmt.pix.height < 32) + f->fmt.pix.height = 32; + if (f->fmt.pix.width > maxw) + f->fmt.pix.width = maxw; + if (f->fmt.pix.height > maxh) + f->fmt.pix.height = maxh; + f->fmt.pix.sizeimage = + (fh->buf.vb.width * fh->buf.vb.height * fmt->depth)/8; + + return 0; + } + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + return verify_window(&bttv_tvnorms[btv->tvnorm], + &f->fmt.win, 1); +#if 0 + case V4L2_BUF_TYPE_VBI_CAPTURE: + retval = bttv_switch_type(fh,f->type); + if (0 != retval) + return retval; + if (fh->vbi.reading || fh->vbi.streaming) + return -EBUSY; + bttv_vbi_setlines(fh,btv,f->fmt.vbi.count[0]); + bttv_vbi_fmt(fh,f); + return 0; +#endif + default: + return -EINVAL; + } +} + +static int bttv_s_fmt(struct bttv_fh *fh, struct bttv *btv, + struct v4l2_format *f) +{ + int retval; + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + { + const struct bttv_format *fmt; + + retval = bttv_switch_type(fh,f->type); + if (0 != retval) + return retval; + retval = bttv_try_fmt(fh,btv,f); + if (0 != retval) + return retval; + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + + /* update our state informations */ + down(&fh->cap.lock); + fh->buf.fmt = fmt; + fh->buf.vb.field = f->fmt.pix.field; + fh->buf.vb.width = f->fmt.pix.width; + fh->buf.vb.height = f->fmt.pix.height; + btv->init.buf.fmt = fmt; + btv->init.buf.vb.field = f->fmt.pix.field; + btv->init.buf.vb.width = f->fmt.pix.width; + btv->init.buf.vb.height = f->fmt.pix.height; + up(&fh->cap.lock); + + return 0; + } + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + return setup_window(fh, btv, &f->fmt.win, 1); + case V4L2_BUF_TYPE_VBI_CAPTURE: + retval = bttv_switch_type(fh,f->type); + if (0 != retval) + return retval; + if (fh->vbi.reading || fh->vbi.streaming) + return -EBUSY; + bttv_vbi_setlines(fh,btv,f->fmt.vbi.count[0]); + bttv_vbi_fmt(fh,f); + return 0; + default: + return -EINVAL; + } +} + static int bttv_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg) { @@ -1676,8 +1954,7 @@ break; case 'V': printk("bttv%d: ioctl 0x%x (v4l2, VIDIOC_%s)\n", - btv->nr, cmd, (_IOC_NR(cmd) < V4L2_IOCTLS) ? - v4l2_ioctls[_IOC_NR(cmd)] : "???"); + btv->nr, cmd, v4l2_ioctl_names[_IOC_NR(cmd)]); break; default: printk("bttv%d: ioctl 0x%x (???)\n", @@ -1696,17 +1973,23 @@ memset(cap,0,sizeof(*cap)); strcpy(cap->name,btv->video_dev.name); - cap->type = VID_TYPE_CAPTURE| - VID_TYPE_TUNER| - VID_TYPE_OVERLAY| - VID_TYPE_CLIPPING| - VID_TYPE_SCALES; - cap->channels = bttv_tvcards[btv->type].video_inputs; - cap->audios = bttv_tvcards[btv->type].audio_inputs; - cap->maxwidth = bttv_tvnorms[btv->tvnorm].swidth; - cap->maxheight = bttv_tvnorms[btv->tvnorm].sheight; - cap->minwidth = 48; - cap->minheight = 32; + if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) { + /* vbi */ + cap->type = VID_TYPE_TUNER|VID_TYPE_TELETEXT; + } else { + /* others */ + cap->type = VID_TYPE_CAPTURE| + VID_TYPE_TUNER| + VID_TYPE_OVERLAY| + VID_TYPE_CLIPPING| + VID_TYPE_SCALES; + cap->channels = bttv_tvcards[btv->type].video_inputs; + cap->audios = bttv_tvcards[btv->type].audio_inputs; + cap->maxwidth = bttv_tvnorms[btv->tvnorm].swidth; + cap->maxheight = bttv_tvnorms[btv->tvnorm].sheight; + cap->minwidth = 48; + cap->minheight = 32; + } return 0; } @@ -1733,7 +2016,7 @@ fmt = format_by_palette(pic->palette); if (NULL == fmt) return -EINVAL; - down(&fh->q.lock); + down(&fh->cap.lock); retval = -EINVAL; if (fmt->depth != pic->depth && !sloppy) goto fh_unlock_and_return; @@ -1754,7 +2037,7 @@ bt848_contrast(btv,pic->contrast); bt848_hue(btv,pic->hue); bt848_sat(btv,pic->colour); - up(&fh->q.lock); + up(&fh->cap.lock); return 0; } @@ -1763,26 +2046,31 @@ struct video_window *win = arg; memset(win,0,sizeof(*win)); - win->x = fh->ov.x; - win->y = fh->ov.y; - win->width = fh->ov.width; - win->height = fh->ov.height; + win->x = fh->ov.w.left; + win->y = fh->ov.w.top; + win->width = fh->ov.w.width; + win->height = fh->ov.w.height; return 0; } case VIDIOCSWIN: { struct video_window *win = arg; + struct v4l2_window w2; - retval = setup_window(fh,btv,win->x,win->y, - win->width,win->height, - win->clips, - win->clipcount); + w2.field = V4L2_FIELD_ANY; + w2.w.left = win->x; + w2.w.top = win->y; + w2.w.width = win->width; + w2.w.height = win->height; + w2.clipcount = win->clipcount; + w2.clips = (struct v4l2_clip*)win->clips; + retval = setup_window(fh, btv, &w2, 0); if (0 == retval) { /* on v4l1 this ioctl affects the read() size too */ - fh->buf.vb.width = fh->ov.width; - fh->buf.vb.height = fh->ov.height; - btv->init.buf.vb.width = fh->ov.width; - btv->init.buf.vb.height = fh->ov.height; + fh->buf.vb.width = fh->ov.w.width; + fh->buf.vb.height = fh->ov.w.height; + btv->init.buf.vb.width = fh->ov.w.width; + btv->init.buf.vb.height = fh->ov.w.height; } return retval; } @@ -1804,9 +2092,7 @@ return -EPERM; end = (unsigned long)fbuf->base + fbuf->height * fbuf->bytesperline; - if (0 == find_videomem((unsigned long)fbuf->base,end)) - return -EINVAL; - down(&fh->q.lock); + down(&fh->cap.lock); retval = -EINVAL; if (sloppy) { /* also set the default palette -- for backward @@ -1846,14 +2132,12 @@ goto fh_unlock_and_return; } btv->fbuf = *fbuf; - up(&fh->q.lock); + up(&fh->cap.lock); return 0; } case VIDIOCCAPTURE: -#ifdef HAVE_V4L2 - case VIDIOC_PREVIEW: -#endif + case VIDIOC_OVERLAY: { struct bttv_buffer *new; int *on = arg; @@ -1862,10 +2146,10 @@ /* verify args */ if (NULL == btv->fbuf.base) return -EINVAL; - if (fh->ov.width <48 || - fh->ov.height<32 || - fh->ov.width >bttv_tvnorms[btv->tvnorm].swidth || - fh->ov.height>bttv_tvnorms[btv->tvnorm].sheight || + if (fh->ov.w.width <48 || + fh->ov.w.height<32 || + fh->ov.w.width >bttv_tvnorms[btv->tvnorm].swidth || + fh->ov.w.height>bttv_tvnorms[btv->tvnorm].sheight|| NULL == fh->ovfmt) return -EINVAL; } @@ -1873,7 +2157,7 @@ if (!check_alloc_btres(btv,fh,RESOURCE_OVERLAY)) return -EBUSY; - down(&fh->q.lock); + down(&fh->cap.lock); if (*on) { fh->ov.tvnorm = btv->tvnorm; new = videobuf_alloc(sizeof(*new)); @@ -1884,7 +2168,7 @@ /* switch over */ retval = bttv_switch_overlay(btv,fh,new); - up(&fh->q.lock); + up(&fh->cap.lock); return retval; } @@ -1893,10 +2177,8 @@ struct video_mbuf *mbuf = arg; int i; - if (!mmap) - return -EINVAL; - down(&fh->q.lock); - retval = videobuf_mmap_setup(file,&fh->q,gbuffers,gbufsize); + down(&fh->cap.lock); + retval = videobuf_mmap_setup(file,&fh->cap,gbuffers,gbufsize); if (retval < 0) goto fh_unlock_and_return; memset(mbuf,0,sizeof(*mbuf)); @@ -1904,20 +2186,21 @@ mbuf->size = gbuffers * gbufsize; for (i = 0; i < gbuffers; i++) mbuf->offsets[i] = i * gbufsize; - up(&fh->q.lock); + up(&fh->cap.lock); return 0; } case VIDIOCMCAPTURE: { struct video_mmap *vm = arg; struct bttv_buffer *buf; + enum v4l2_field field; if (vm->frame >= VIDEO_MAX_FRAME) return -EINVAL; - down(&fh->q.lock); + down(&fh->cap.lock); retval = -EINVAL; - buf = (struct bttv_buffer *)fh->q.bufs[vm->frame]; + buf = (struct bttv_buffer *)fh->cap.bufs[vm->frame]; if (NULL == buf) goto fh_unlock_and_return; if (0 == buf->vb.baddr) @@ -1926,15 +2209,18 @@ buf->vb.state == STATE_ACTIVE) goto fh_unlock_and_return; + field = (vm->height > bttv_tvnorms[btv->tvnorm].sheight/2) + ? V4L2_FIELD_INTERLACED + : V4L2_FIELD_BOTTOM; retval = bttv_prepare_buffer(btv,buf, format_by_palette(vm->format), - vm->width,vm->height,0); + vm->width,vm->height,field); if (0 != retval) goto fh_unlock_and_return; spin_lock_irqsave(&btv->s_lock,flags); buffer_queue(file,&buf->vb); spin_unlock_irqrestore(&btv->s_lock,flags); - up(&fh->q.lock); + up(&fh->cap.lock); return 0; } case VIDIOCSYNC: @@ -1945,9 +2231,9 @@ if (*frame >= VIDEO_MAX_FRAME) return -EINVAL; - down(&fh->q.lock); + down(&fh->cap.lock); retval = -EINVAL; - buf = (struct bttv_buffer *)fh->q.bufs[*frame]; + buf = (struct bttv_buffer *)fh->cap.bufs[*frame]; if (NULL == buf) goto fh_unlock_and_return; retval = videobuf_waiton(&buf->vb,0,1); @@ -1965,7 +2251,7 @@ retval = -EINVAL; break; } - up(&fh->q.lock); + up(&fh->cap.lock); return retval; } @@ -1980,8 +2266,11 @@ case VIDIOCSAUDIO: return bttv_common_ioctls(btv,cmd,arg); + /* vbi/teletext ioctls */ + case BTTV_VBISIZE: + bttv_switch_type(fh,V4L2_BUF_TYPE_VBI_CAPTURE); + return fh->lines * 2 * 2048; -#ifdef HAVE_V4L2 /* *** v4l2 *** ************************************************ */ case VIDIOC_QUERYCAP: { @@ -1989,25 +2278,21 @@ if (0 == v4l2) return -EINVAL; - strcpy(cap->name,btv->video_dev.name); - cap->type = V4L2_TYPE_CAPTURE; - cap->flags = V4L2_FLAG_TUNER | V4L2_FLAG_PREVIEW - | V4L2_FLAG_READ | V4L2_FLAG_SELECT; - if (mmap) - cap->flags |= V4L2_FLAG_STREAMING; - cap->inputs = bttv_tvcards[btv->type].video_inputs; - cap->outputs = 0; - cap->audios = bttv_tvcards[btv->type].audio_inputs; - cap->maxwidth = bttv_tvnorms[btv->tvnorm].swidth; - cap->maxheight = bttv_tvnorms[btv->tvnorm].sheight; - cap->minwidth = 48; - cap->minheight = 32; - cap->maxframerate = 30; + strcpy(cap->driver,"bttv"); + strncpy(cap->card,btv->video_dev.name,sizeof(cap->card)); + sprintf(cap->bus_info,"PCI:%s",btv->dev->slot_name); + cap->version = BTTV_VERSION_CODE; + cap->capabilities = + V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_VIDEO_OVERLAY | + V4L2_CAP_VBI_CAPTURE | + V4L2_CAP_TUNER | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; return 0; } - case VIDIOC_ENUM_PIXFMT: - case VIDIOC_ENUM_FBUFFMT: + case VIDIOC_ENUM_FMT: { struct v4l2_fmtdesc *f = arg; int i, index; @@ -2021,81 +2306,38 @@ } if (BTTV_FORMATS == i) return -EINVAL; - if (cmd == VIDIOC_ENUM_FBUFFMT && - 0 == (bttv_formats[i].flags & FORMAT_FLAGS_PACKED)) + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + if (!(bttv_formats[i].flags & FORMAT_FLAGS_PACKED)) + return -EINVAL; + break; + default: return -EINVAL; + } memset(f,0,sizeof(*f)); f->index = index; strncpy(f->description,bttv_formats[i].name,31); f->pixelformat = bttv_formats[i].fourcc; - f->depth = bttv_formats[i].depth; return 0; } + case VIDIOC_TRY_FMT: + { + struct v4l2_format *f = arg; + return bttv_try_fmt(fh,btv,f); + } case VIDIOC_G_FMT: { struct v4l2_format *f = arg; - - memset(f,0,sizeof(*f)); - f->type = V4L2_BUF_TYPE_CAPTURE; - f->fmt.pix.width = fh->buf.vb.width; - f->fmt.pix.height = fh->buf.vb.height; - f->fmt.pix.depth = fh->buf.fmt->depth; - f->fmt.pix.pixelformat = fh->buf.fmt->fourcc; - f->fmt.pix.sizeimage = - (fh->buf.vb.width*fh->buf.vb.height*fh->buf.fmt->depth)/8; - return 0; + return bttv_g_fmt(fh,f); } case VIDIOC_S_FMT: { struct v4l2_format *f = arg; - const struct bttv_format *fmt; - - if ((f->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_CAPTURE) - return -EINVAL; - fmt = format_by_fourcc(f->fmt.pix.pixelformat); - if (NULL == fmt) - return -EINVAL; - if (f->fmt.pix.width < 48 || - f->fmt.pix.height < 32) - return -EINVAL; - if (f->fmt.pix.flags & V4L2_FMT_FLAG_BYTESPERLINE) - /* FIXME -- not implemented yet */ - return -EINVAL; - - down(&fh->q.lock); - /* fixup format */ - if (f->fmt.pix.width > bttv_tvnorms[btv->tvnorm].swidth) - f->fmt.pix.width = bttv_tvnorms[btv->tvnorm].swidth; - if (f->fmt.pix.height > bttv_tvnorms[btv->tvnorm].sheight) - f->fmt.pix.height = bttv_tvnorms[btv->tvnorm].sheight; - if (!(f->fmt.pix.flags & V4L2_FMT_FLAG_INTERLACED) && - f->fmt.pix.height>bttv_tvnorms[btv->tvnorm].sheight/2) - f->fmt.pix.height=bttv_tvnorms[btv->tvnorm].sheight/2; - - if (f->fmt.pix.height > bttv_tvnorms[btv->tvnorm].sheight/2) { - /* must interlace -- no field splitting available */ - f->fmt.pix.flags &= ~(V4L2_FMT_FLAG_TOPFIELD| - V4L2_FMT_FLAG_BOTFIELD); - } else { - /* one field is enouth -- no interlace needed */ - f->fmt.pix.flags &= ~V4L2_FMT_FLAG_INTERLACED; - } - - /* update our state informations */ - fh->buf.fmt = fmt; - fh->buf.vb.width = f->fmt.pix.width; - fh->buf.vb.height = f->fmt.pix.height; - btv->init.buf.fmt = fmt; - btv->init.buf.vb.width = f->fmt.pix.width; - btv->init.buf.vb.height = f->fmt.pix.height; - - /* update data for the application */ - f->fmt.pix.depth = fmt->depth; - f->fmt.pix.sizeimage = - (fh->buf.vb.width * fh->buf.vb.height * fmt->depth)/8; - up(&fh->q.lock); - return 0; + return bttv_s_fmt(fh,btv,f); } case VIDIOC_G_FBUF: @@ -2103,16 +2345,13 @@ struct v4l2_framebuffer *fb = arg; memset(fb,0,sizeof(*fb)); - fb->base[0] = btv->fbuf.base; + fb->base = btv->fbuf.base; fb->fmt.width = btv->fbuf.width; fb->fmt.height = btv->fbuf.height; fb->fmt.bytesperline = btv->fbuf.bytesperline; - fb->fmt.flags = V4L2_FMT_FLAG_BYTESPERLINE; - fb->capability = V4L2_FBUF_CAP_CLIPPING; - if (fh->ovfmt) { - fb->fmt.depth = fh->ovfmt->depth; + fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; + if (fh->ovfmt) fb->fmt.pixelformat = fh->ovfmt->fourcc; - } return 0; } case VIDIOC_S_FBUF: @@ -2126,18 +2365,16 @@ return -EPERM; /* check args */ - end = (unsigned long)fb->base[0] + + end = (unsigned long)fb->base + fb->fmt.height * fb->fmt.bytesperline; - if (0 == find_videomem((unsigned long)fb->base[0],end)) - return -EINVAL; - + fmt = format_by_fourcc(fb->fmt.pixelformat); if (NULL == fmt) return -EINVAL; if (0 == (fmt->flags & FORMAT_FLAGS_PACKED)) return -EINVAL; - down(&fh->q.lock); + down(&fh->cap.lock); retval = -EINVAL; if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) { if (fb->fmt.width > bttv_tvnorms[btv->tvnorm].swidth) @@ -2147,11 +2384,11 @@ } /* ok, accept it */ - btv->fbuf.base = fb->base[0]; + btv->fbuf.base = fb->base; btv->fbuf.width = fb->fmt.width; btv->fbuf.height = fb->fmt.height; btv->fbuf.depth = fmt->depth; - if (fb->fmt.flags & V4L2_FMT_FLAG_BYTESPERLINE) + if (0 != fb->fmt.bytesperline) btv->fbuf.bytesperline = fb->fmt.bytesperline; else btv->fbuf.bytesperline = btv->fbuf.width*fmt->depth/8; @@ -2160,12 +2397,12 @@ fh->ovfmt = fmt; btv->init.ovfmt = fmt; if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) { - fh->ov.x = 0; - fh->ov.y = 0; - fh->ov.width = fb->fmt.width; - fh->ov.height = fb->fmt.height; - btv->init.ov.width = fb->fmt.width; - btv->init.ov.height = fb->fmt.height; + fh->ov.w.left = 0; + fh->ov.w.top = 0; + fh->ov.w.width = fb->fmt.width; + fh->ov.w.height = fb->fmt.height; + btv->init.ov.w.width = fb->fmt.width; + btv->init.ov.w.height = fb->fmt.height; if (fh->ov.clips) kfree(fh->ov.clips); fh->ov.clips = NULL; @@ -2179,64 +2416,48 @@ retval = bttv_switch_overlay(btv,fh,new); } } - up(&fh->q.lock); + up(&fh->cap.lock); return retval; } - case VIDIOC_G_WIN: - { - struct v4l2_window *win = arg; - - memset(win,0,sizeof(*win)); - win->x = fh->ov.x; - win->y = fh->ov.y; - win->width = fh->ov.width; - win->height = fh->ov.height; - return 0; - } - case VIDIOC_S_WIN: - { - struct v4l2_window *win = arg; - - return setup_window(fh,btv,win->x,win->y, - win->width,win->height, - (struct video_clip*)win->clips, - win->clipcount); - } case VIDIOC_REQBUFS: - if (!mmap) - return -EINVAL; - return videobuf_reqbufs(file,&fh->q,arg); + return videobuf_reqbufs(file,bttv_queue(fh),arg); case VIDIOC_QUERYBUF: - return videobuf_querybuf(&fh->q,arg); + return videobuf_querybuf(bttv_queue(fh),arg); case VIDIOC_QBUF: - return videobuf_qbuf(file,&fh->q,arg); + return videobuf_qbuf(file,bttv_queue(fh),arg); case VIDIOC_DQBUF: - return videobuf_dqbuf(file,&fh->q,arg); + return videobuf_dqbuf(file,bttv_queue(fh),arg); case VIDIOC_STREAMON: - if (!check_alloc_btres(btv,fh,RESOURCE_STREAMING)) + { + int res = bttv_resource(fh); + + if (!check_alloc_btres(btv,fh,res)) return -EBUSY; bttv_field_count(btv); - return videobuf_streamon(file,&fh->q); - + return videobuf_streamon(file,bttv_queue(fh)); + } case VIDIOC_STREAMOFF: - retval = videobuf_streamoff(file,&fh->q); + { + int res = bttv_resource(fh); + + retval = videobuf_streamoff(file,bttv_queue(fh)); if (retval < 0) return retval; - free_btres(btv,fh,RESOURCE_STREAMING); + free_btres(btv,fh,res); bttv_field_count(btv); return 0; + } case VIDIOC_QUERYCTRL: { struct v4l2_queryctrl *c = arg; int i; - v4l2_fill_ctrl_category(c); if ((c->id < V4L2_CID_BASE || c->id >= V4L2_CID_LASTP1) && (c->id < V4L2_CID_PRIVATE_BASE || @@ -2250,7 +2471,7 @@ return 0; } *c = bttv_ctls[i]; - if (bttv_ctls[i].category == V4L2_CTRL_CAT_AUDIO) { + if (i >= 4 && i <= 8) { struct video_audio va; memset(&va,0,sizeof(va)); bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); @@ -2285,11 +2506,12 @@ { struct v4l2_streamparm *parm = arg; struct v4l2_standard s; - if (parm->type != V4L2_BUF_TYPE_CAPTURE) + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; memset(parm,0,sizeof(*parm)); - v4l2_video_std_construct(&s, bttv_tvnorms[btv->tvnorm].v4l2_id, 0); - parm->parm.capture.timeperframe = v4l2_video_std_tpf(&s); + v4l2_video_std_construct(&s, bttv_tvnorms[btv->tvnorm].v4l2_id, + bttv_tvnorms[btv->tvnorm].name); + parm->parm.capture.timeperframe = s.frameperiod; return 0; } @@ -2301,10 +2523,9 @@ case VIDIOC_S_INPUT: case VIDIOC_G_TUNER: case VIDIOC_S_TUNER: - case VIDIOC_G_FREQ: - case VIDIOC_S_FREQ: + case VIDIOC_G_FREQUENCY: + case VIDIOC_S_FREQUENCY: return bttv_common_ioctls(btv,cmd,arg); -#endif /* HAVE_V4L2 */ default: return -ENOIOCTLCMD; @@ -2312,7 +2533,7 @@ return 0; fh_unlock_and_return: - up(&fh->q.lock); + up(&fh->cap.lock); return retval; } @@ -2322,55 +2543,30 @@ return video_usercopy(inode, file, cmd, arg, bttv_do_ioctl); } -#if 0 -/* - * blocking read for a complete video frame - * => no kernel bounce buffer needed. - */ -static ssize_t -bttv_read_zerocopy(struct bttv_fh *fh, struct bttv *btv, - char *data,size_t count, loff_t *ppos) -{ - int rc; - - /* setup stuff */ - dprintk("bttv%d: read zerocopy\n",btv->nr); - fh->q.read_buf->baddr = (unsigned long)data; - fh->q.read_buf->bsize = count; - rc = bttv_prepare_buffer(btv,fh->q.read_buf,fh->buf.fmt, - fh->buf.vb.width,fh->buf.vb.height,0); - if (0 != rc) - goto done; - - /* start capture & wait */ - bttv_queue_buffer(btv,fh->q.read_buf); - rc = videobuf_waiton(fh->q.read_buf,0,1); - if (0 == rc) { - videobuf_dma_pci_sync(btv->dev,&fh->q.read_buf->dma); - rc = fh->q.read_buf->size; - } - - done: - /* cleanup */ - bttv_dma_free(btv,fh->q.read_buf); - fh->q.read_buf->baddr = 0; - fh->q.read_buf->size = 0; - return rc; -} -#endif - - static ssize_t bttv_read(struct file *file, char *data, size_t count, loff_t *ppos) { struct bttv_fh *fh = file->private_data; + int retval = 0; if (fh->btv->errors) bttv_reinit_bt848(fh->btv); - if (locked_btres(fh->btv,RESOURCE_STREAMING)) - return -EBUSY; + dprintk("read count=%d type=%s\n", + (int)count,v4l2_type_names[fh->type]); - return videobuf_read_one(file, &fh->q, data, count, ppos); + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (locked_btres(fh->btv,RESOURCE_VIDEO)) + return -EBUSY; + retval = videobuf_read_one(file, &fh->cap, data, count, ppos); + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + retval = videobuf_read_stream(file, &fh->vbi, data, count, ppos, 1); + break; + default: + BUG(); + } + return retval; } static unsigned int bttv_poll(struct file *file, poll_table *wait) @@ -2378,34 +2574,37 @@ struct bttv_fh *fh = file->private_data; struct bttv_buffer *buf; - if (check_btres(fh,RESOURCE_STREAMING)) { + if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) + return videobuf_poll_stream(file, &fh->vbi, wait); + + if (check_btres(fh,RESOURCE_VIDEO)) { /* streaming capture */ - if (list_empty(&fh->q.stream)) + if (list_empty(&fh->cap.stream)) return POLLERR; - buf = list_entry(fh->q.stream.next,struct bttv_buffer,vb.stream); + buf = list_entry(fh->cap.stream.next,struct bttv_buffer,vb.stream); } else { /* read() capture */ - down(&fh->q.lock); - if (NULL == fh->q.read_buf) { + down(&fh->cap.lock); + if (NULL == fh->cap.read_buf) { /* need to capture a new frame */ - if (locked_btres(fh->btv,RESOURCE_STREAMING)) { - up(&fh->q.lock); + if (locked_btres(fh->btv,RESOURCE_VIDEO)) { + up(&fh->cap.lock); return POLLERR; } - fh->q.read_buf = videobuf_alloc(fh->q.msize); - if (NULL == fh->q.read_buf) { - up(&fh->q.lock); + fh->cap.read_buf = videobuf_alloc(fh->cap.msize); + if (NULL == fh->cap.read_buf) { + up(&fh->cap.lock); return POLLERR; } - if (0 != fh->q.ops->buf_prepare(file,fh->q.read_buf,0)) { - up(&fh->q.lock); + if (0 != fh->cap.ops->buf_prepare(file,fh->cap.read_buf)) { + up(&fh->cap.lock); return POLLERR; } - fh->q.ops->buf_queue(file,fh->q.read_buf); - fh->q.read_off = 0; + fh->cap.ops->buf_queue(file,fh->cap.read_buf); + fh->cap.read_off = 0; } - up(&fh->q.lock); - buf = (struct bttv_buffer*)fh->q.read_buf; + up(&fh->cap.lock); + buf = (struct bttv_buffer*)fh->cap.read_buf; } poll_wait(file, &buf->vb.done, wait); @@ -2420,6 +2619,7 @@ unsigned int minor = minor(inode->i_rdev); struct bttv *btv = NULL; struct bttv_fh *fh; + enum v4l2_buf_type type = 0; int i; dprintk(KERN_DEBUG "bttv: open minor=%d\n",minor); @@ -2427,13 +2627,20 @@ for (i = 0; i < bttv_num; i++) { if (bttvs[i].video_dev.minor == minor) { btv = &bttvs[i]; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + break; + } + if (bttvs[i].vbi_dev.minor == minor) { + btv = &bttvs[i]; + type = V4L2_BUF_TYPE_VBI_CAPTURE; break; } } if (NULL == btv) return -ENODEV; - dprintk(KERN_DEBUG "bttv%d: open called (video)\n",btv->nr); + dprintk(KERN_DEBUG "bttv%d: open called (type=%s)\n", + btv->nr,v4l2_type_names[type]); /* allocate per filehandle data */ fh = kmalloc(sizeof(*fh),GFP_KERNEL); @@ -2441,8 +2648,15 @@ return -ENOMEM; file->private_data = fh; *fh = btv->init; - videobuf_queue_init(&fh->q, &bttv_qops, btv->dev, &btv->s_lock, - V4L2_BUF_TYPE_CAPTURE,sizeof(struct bttv_buffer)); + fh->type = type; + videobuf_queue_init(&fh->cap, &bttv_video_qops, + btv->dev, &btv->s_lock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + sizeof(struct bttv_buffer)); + videobuf_queue_init(&fh->vbi, &bttv_vbi_qops, + btv->dev, &btv->s_lock, + V4L2_BUF_TYPE_VBI_CAPTURE, + sizeof(struct bttv_buffer)); i2c_vidiocschan(btv); return 0; @@ -2456,16 +2670,24 @@ /* turn off overlay, stop outstanding captures */ if (check_btres(fh, RESOURCE_OVERLAY)) bttv_switch_overlay(btv,fh,NULL); - if (check_btres(fh, RESOURCE_STREAMING)) { - videobuf_streamoff(file,&fh->q); - free_btres(btv,fh,RESOURCE_STREAMING); + + /* stop video capture */ + if (check_btres(fh, RESOURCE_VIDEO)) { + videobuf_streamoff(file,&fh->cap); + free_btres(btv,fh,RESOURCE_VIDEO); bttv_field_count(btv); } - if (fh->q.read_buf) { - buffer_release(file,fh->q.read_buf); - kfree(fh->q.read_buf); + if (fh->cap.read_buf) { + buffer_release(file,fh->cap.read_buf); + kfree(fh->cap.read_buf); } + /* stop vbi capture */ + if (fh->vbi.streaming) + videobuf_streamoff(file,&fh->vbi); + if (fh->vbi.reading) + videobuf_read_stop(file,&fh->vbi); + file->private_data = NULL; kfree(fh); return 0; @@ -2476,11 +2698,10 @@ { struct bttv_fh *fh = file->private_data; - if (!mmap) - return -EINVAL; - dprintk("mmap 0x%lx+%ld\n",vma->vm_start, - vma->vm_end - vma->vm_start); - return videobuf_mmap_mapper(vma,&fh->q); + dprintk("bttv%d: mmap type=%s 0x%lx+%ld\n", + fh->btv->nr, v4l2_type_names[fh->type], + vma->vm_start, vma->vm_end - vma->vm_start); + return videobuf_mmap_mapper(vma,bttv_queue(fh)); } static struct file_operations bttv_fops = @@ -2495,7 +2716,7 @@ poll: bttv_poll, }; -static struct video_device bttv_template = +static struct video_device bttv_video_template = { name: "UNSET", type: VID_TYPE_CAPTURE|VID_TYPE_TUNER|VID_TYPE_OVERLAY| @@ -2505,6 +2726,15 @@ minor: -1, }; +struct video_device bttv_vbi_template = +{ + name: "bt848/878 vbi", + type: VID_TYPE_TUNER|VID_TYPE_TELETEXT, + hardware: VID_HARDWARE_BT848, + fops: &bttv_fops, + minor: -1, +}; + /* ----------------------------------------------------------------------- */ /* radio interface */ @@ -2653,20 +2883,20 @@ printk(" main: %08Lx\n", (u64)btv->main.dma); printk(" vbi : o=%08Lx e=%08Lx\n", - btv->vcurr ? (u64)btv->vcurr->odd.dma : 0, - btv->vcurr ? (u64)btv->vcurr->even.dma : 0); + btv->vcurr ? (u64)btv->vcurr->top.dma : 0, + btv->vcurr ? (u64)btv->vcurr->bottom.dma : 0); printk(" cap : o=%08Lx e=%08Lx\n", - btv->odd ? (u64)btv->odd->odd.dma : 0, - btv->even ? (u64)btv->even->even.dma : 0); + btv->top ? (u64)btv->top->top.dma : 0, + btv->bottom ? (u64)btv->bottom->bottom.dma : 0); printk(" scr : o=%08Lx e=%08Lx\n", - btv->screen ? (u64)btv->screen->odd.dma : 0, - btv->screen ? (u64)btv->screen->even.dma : 0); + btv->screen ? (u64)btv->screen->top.dma : 0, + btv->screen ? (u64)btv->screen->bottom.dma : 0); } static void bttv_irq_timeout(unsigned long data) { struct bttv *btv = (struct bttv *)data; - struct bttv_buffer *o_even,*o_odd,*o_vcurr; + struct bttv_buffer *o_bottom,*o_top,*o_vcurr; struct bttv_buffer *capture; if (bttv_verbose) { @@ -2677,12 +2907,12 @@ } spin_lock(&btv->s_lock); - o_odd = btv->odd; - o_even = btv->even; - o_vcurr = btv->vcurr; - btv->odd = NULL; - btv->even = NULL; - btv->vcurr = NULL; + o_top = btv->top; + o_bottom = btv->bottom; + o_vcurr = btv->vcurr; + btv->top = NULL; + btv->bottom = NULL; + btv->vcurr = NULL; /* deactivate stuff */ bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0); @@ -2692,19 +2922,19 @@ bttv_set_dma(btv, 0, 0); /* wake up + free */ - if (o_odd == o_even) { - if (NULL != o_odd) { - o_odd->vb.state = STATE_ERROR; - wake_up(&o_odd->vb.done); + if (o_top == o_bottom) { + if (NULL != o_top) { + o_top->vb.state = STATE_ERROR; + wake_up(&o_top->vb.done); } } else { - if (NULL != o_odd) { - o_odd->vb.state = STATE_ERROR; - wake_up(&o_odd->vb.done); - } - if (NULL != o_even) { - o_even->vb.state = STATE_ERROR; - wake_up(&o_even->vb.done); + if (NULL != o_top) { + o_top->vb.state = STATE_ERROR; + wake_up(&o_top->vb.done); + } + if (NULL != o_bottom) { + o_bottom->vb.state = STATE_ERROR; + wake_up(&o_bottom->vb.done); } } if (NULL != o_vcurr) { @@ -2733,20 +2963,18 @@ static void bttv_irq_switch_fields(struct bttv *btv) { - struct bttv_buffer *o_even,*o_odd,*o_vcurr; + struct bttv_buffer *o_bottom,*o_top,*o_vcurr; struct bttv_buffer *capture; int irqflags = 0; -#ifdef HAVE_V4L2 - stamp_t ts; -#endif + struct timeval ts; spin_lock(&btv->s_lock); - o_odd = btv->odd; - o_even = btv->even; - o_vcurr = btv->vcurr; - btv->odd = NULL; - btv->even = NULL; - btv->vcurr = NULL; + o_top = btv->top; + o_bottom = btv->bottom; + o_vcurr = btv->vcurr; + btv->top = NULL; + btv->bottom = NULL; + btv->vcurr = NULL; /* vbi request ? */ if (!list_empty(&btv->vcapture)) { @@ -2760,24 +2988,24 @@ irqflags = 1; capture = list_entry(btv->capture.next, struct bttv_buffer, vb.queue); list_del(&capture->vb.queue); - if (capture->vb.field & VBUF_FIELD_ODD) - btv->odd = capture; - if (capture->vb.field & VBUF_FIELD_EVEN) - btv->even = capture; + if (V4L2_FIELD_HAS_TOP(capture->vb.field)) + btv->top = capture; + if (V4L2_FIELD_HAS_BOTTOM(capture->vb.field)) + btv->bottom = capture; /* capture request for other field ? */ - if (!(capture->vb.field & VBUF_FIELD_INTER) && + if (!V4L2_FIELD_HAS_BOTH(capture->vb.field) && !list_empty(&btv->capture)) { capture = list_entry(btv->capture.next, struct bttv_buffer, vb.queue); - if (!(capture->vb.field & VBUF_FIELD_INTER)) { - if (NULL == btv->odd && - capture->vb.field & VBUF_FIELD_ODD) { - btv->odd = capture; + if (!V4L2_FIELD_HAS_BOTH(capture->vb.field)) { + if (NULL == btv->top && + V4L2_FIELD_TOP == capture->vb.field) { + btv->top = capture; list_del(&capture->vb.queue); } - if (NULL == btv->even && - capture->vb.field & VBUF_FIELD_EVEN) { - btv->even = capture; + if (NULL == btv->bottom && + V4L2_FIELD_BOTTOM == capture->vb.field) { + btv->bottom = capture; list_del(&capture->vb.queue); } } @@ -2786,34 +3014,34 @@ /* screen overlay ? */ if (NULL != btv->screen) { - if (btv->screen->vb.field & VBUF_FIELD_INTER) { - if (NULL == btv->odd && NULL == btv->even) { - btv->odd = btv->screen; - btv->even = btv->screen; + if (V4L2_FIELD_HAS_BOTH(btv->screen->vb.field)) { + if (NULL == btv->top && NULL == btv->bottom) { + btv->top = btv->screen; + btv->bottom = btv->screen; } } else { - if ((btv->screen->vb.field & VBUF_FIELD_ODD) && - NULL == btv->odd) { - btv->odd = btv->screen; + if (V4L2_FIELD_TOP == btv->screen->vb.field && + NULL == btv->top) { + btv->top = btv->screen; } - if ((btv->screen->vb.field & VBUF_FIELD_EVEN) && - NULL == btv->even) { - btv->even = btv->screen; + if (V4L2_FIELD_BOTTOM == btv->screen->vb.field && + NULL == btv->bottom) { + btv->bottom = btv->screen; } } } if (irq_debug) printk(KERN_DEBUG - "bttv: irq odd=%p even=%p screen=%p vbi=%p\n", - btv->odd,btv->even,btv->screen,btv->vcurr); + "bttv: irq top=%p bottom=%p screen=%p vbi=%p\n", + btv->top,btv->bottom,btv->screen,btv->vcurr); /* activate new fields */ - bttv_buffer_activate(btv,btv->odd,btv->even); + bttv_buffer_activate(btv,btv->top,btv->bottom); if (btv->vcurr) { btv->vcurr->vb.state = STATE_ACTIVE; - bttv_risc_hook(btv, RISC_SLOT_O_VBI, &btv->vcurr->odd, 0); - bttv_risc_hook(btv, RISC_SLOT_E_VBI, &btv->vcurr->even, 0); + bttv_risc_hook(btv, RISC_SLOT_O_VBI, &btv->vcurr->top, 0); + bttv_risc_hook(btv, RISC_SLOT_E_VBI, &btv->vcurr->bottom, 0); } else { bttv_risc_hook(btv, RISC_SLOT_O_VBI, NULL, 0); bttv_risc_hook(btv, RISC_SLOT_E_VBI, NULL, 0); @@ -2821,40 +3049,30 @@ bttv_set_dma(btv, 0, irqflags); /* wake up + free */ -#ifdef HAVE_V4L2 - v4l2_masterclock_gettime(&ts); -#endif - if (o_odd == o_even) { - if (NULL != o_odd && btv->odd != o_odd) { -#ifdef HAVE_V4L2 - o_odd->vb.ts = ts; -#endif - o_odd->vb.field_count = btv->field_count; - o_odd->vb.state = STATE_DONE; - wake_up(&o_odd->vb.done); + do_gettimeofday(&ts); + if (o_top == o_bottom) { + if (NULL != o_top && btv->top != o_top) { + o_top->vb.ts = ts; + o_top->vb.field_count = btv->field_count; + o_top->vb.state = STATE_DONE; + wake_up(&o_top->vb.done); } } else { - if (NULL != o_odd && btv->odd != o_odd) { -#ifdef HAVE_V4L2 - o_odd->vb.ts = ts; -#endif - o_odd->vb.field_count = btv->field_count; - o_odd->vb.state = STATE_DONE; - wake_up(&o_odd->vb.done); - } - if (NULL != o_even && btv->even != o_even) { -#ifdef HAVE_V4L2 - o_even->vb.ts = ts; -#endif - o_even->vb.field_count = btv->field_count; - o_even->vb.state = STATE_DONE; - wake_up(&o_even->vb.done); + if (NULL != o_top && btv->top != o_top) { + o_top->vb.ts = ts; + o_top->vb.field_count = btv->field_count; + o_top->vb.state = STATE_DONE; + wake_up(&o_top->vb.done); + } + if (NULL != o_bottom && btv->bottom != o_bottom) { + o_bottom->vb.ts = ts; + o_bottom->vb.field_count = btv->field_count; + o_bottom->vb.state = STATE_DONE; + wake_up(&o_bottom->vb.done); } } if (NULL != o_vcurr) { -#ifdef HAVE_V4L2 o_vcurr->vb.ts = ts; -#endif o_vcurr->vb.field_count = btv->field_count; o_vcurr->vb.state = STATE_DONE; wake_up(&o_vcurr->vb.done); @@ -2896,7 +3114,7 @@ printk(" PRES => %s", (dstat & BT848_DSTATUS_PRES) ? "yes" : "no"); if (stat & BT848_INT_FMTCHG) - printk(" NUML => %s", (dstat & BT848_DSTATUS_PRES) + printk(" NUML => %s", (dstat & BT848_DSTATUS_NUML) ? "625" : "525"); printk("\n"); } @@ -3006,8 +3224,6 @@ init_waitqueue_head(&btv->gpioq); INIT_LIST_HEAD(&btv->capture); INIT_LIST_HEAD(&btv->vcapture); - videobuf_queue_init(&btv->vbi.q, &vbi_qops, btv->dev, &btv->s_lock, - V4L2_BUF_TYPE_VBI,sizeof(struct bttv_buffer)); btv->timeout.function = bttv_irq_timeout; btv->timeout.data = (unsigned long)btv; @@ -3015,9 +3231,9 @@ btv->i2c_rc = -1; btv->tuner_type = -1; - memcpy(&btv->video_dev, &bttv_template, sizeof(bttv_template)); - memcpy(&btv->radio_dev, &radio_template, sizeof(radio_template)); - memcpy(&btv->vbi_dev, &bttv_vbi_template, sizeof(bttv_vbi_template)); + memcpy(&btv->video_dev, &bttv_video_template, sizeof(bttv_video_template)); + memcpy(&btv->radio_dev, &radio_template, sizeof(radio_template)); + memcpy(&btv->vbi_dev, &bttv_vbi_template, sizeof(bttv_vbi_template)); btv->video_dev.minor = -1; btv->video_dev.priv = btv; btv->radio_dev.minor = -1; @@ -3060,9 +3276,8 @@ } pci_read_config_byte(dev, PCI_CLASS_REVISION, &btv->revision); 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(KERN_INFO "bttv%d: Bt%d (rev %d) at %s, ", + bttv_num,btv->id, btv->revision, dev->slot_name); printk("irq: %d, latency: %d, mmio: 0x%lx\n", btv->dev->irq, lat, pci_resource_start(dev,0)); @@ -3100,11 +3315,13 @@ /* fill struct bttv with some useful defaults */ btv->init.btv = btv; - btv->init.ov.width = 320; - btv->init.ov.height = 240; + btv->init.ov.w.width = 320; + btv->init.ov.w.height = 240; btv->init.buf.fmt = format_by_palette(VIDEO_PALETTE_RGB24); btv->init.buf.vb.width = 320; btv->init.buf.vb.height = 240; + btv->init.buf.vb.field = V4L2_FIELD_BOTTOM; + btv->init.lines = 16; btv->input = 0; /* initialize hardware */ @@ -3173,7 +3390,7 @@ /* shutdown everything (DMA+IRQs) */ btand(~15, BT848_GPIO_DMA_CTL); btwrite(0, BT848_INT_MASK); - btwrite(~0x0UL,BT848_INT_STAT); + btwrite(~0x0, BT848_INT_STAT); btwrite(0x0, BT848_GPIO_OUT_EN); if (bttv_gpio) bttv_gpio_tracking(btv,"cleanup"); @@ -3231,8 +3448,7 @@ { bttv_num = 0; - printk(KERN_INFO "bttv: driver version %d.%d.%d loaded " - BTTV_APIS "\n", + printk(KERN_INFO "bttv: driver version %d.%d.%d loaded\n", (BTTV_VERSION_CODE >> 16) & 0xff, (BTTV_VERSION_CODE >> 8) & 0xff, BTTV_VERSION_CODE & 0xff); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/media/video/bttv-if.c linux-2.5-bk/drivers/media/video/bttv-if.c --- linux-2.5.45/drivers/media/video/bttv-if.c Wed Oct 30 16:41:56 2002 +++ linux-2.5-bk/drivers/media/video/bttv-if.c Thu Oct 31 22:12:49 2002 @@ -41,11 +41,13 @@ static struct i2c_client bttv_i2c_client_template; EXPORT_SYMBOL(bttv_get_cardinfo); +EXPORT_SYMBOL(bttv_get_pcidev); EXPORT_SYMBOL(bttv_get_id); EXPORT_SYMBOL(bttv_gpio_enable); EXPORT_SYMBOL(bttv_read_gpio); EXPORT_SYMBOL(bttv_write_gpio); EXPORT_SYMBOL(bttv_get_gpio_queue); +EXPORT_SYMBOL(bttv_i2c_call); /* ----------------------------------------------------------------------- */ /* Exported functions - for other modules which want to access the */ @@ -62,6 +64,13 @@ return 0; } +struct pci_dev* bttv_get_pcidev(unsigned int card) +{ + if (card >= bttv_num) + return NULL; + return bttvs[card].dev; +} + int bttv_get_id(unsigned int card) { printk("bttv_get_id is obsolete, use bttv_get_cardinfo instead\n"); @@ -243,6 +252,13 @@ btv->i2c_clients[i]->driver->command( btv->i2c_clients[i],cmd,arg); } +} + +void bttv_i2c_call(unsigned int card, unsigned int cmd, void *arg) +{ + if (card >= bttv_num) + return; + bttv_call_i2c_clients(&bttvs[card], cmd, arg); } static struct i2c_algo_bit_data bttv_i2c_algo_template = { diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/media/video/bttv-risc.c linux-2.5-bk/drivers/media/video/bttv-risc.c --- linux-2.5.45/drivers/media/video/bttv-risc.c Wed Oct 30 16:42:56 2002 +++ linux-2.5-bk/drivers/media/video/bttv-risc.c Thu Oct 31 22:12:49 2002 @@ -5,7 +5,7 @@ - memory management - generation - (c) 2000 Gerd Knorr + (c) 2000 Gerd Knorr 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 @@ -43,8 +43,8 @@ struct bttv_riscmem *risc, unsigned int size) { - unsigned long *cpu; - dma_addr_t dma; + u32 *cpu; + dma_addr_t dma; cpu = pci_alloc_consistent(pci, size, &dma); if (NULL == cpu) @@ -82,7 +82,7 @@ { int instructions,rc,line,todo; struct scatterlist *sg; - unsigned long *rp; + u32 *rp; /* estimate risc mem: worst case is one write per page border + one write per scan line + sync + jump (all 2 dwords) */ @@ -146,7 +146,7 @@ int cpadding) { int instructions,rc,line,todo,ylen,chroma; - unsigned long *rp,ri; + u32 *rp,ri; struct scatterlist *ysg; struct scatterlist *usg; struct scatterlist *vsg; @@ -241,54 +241,53 @@ }; int -bttv_screen_clips(struct video_buffer *fbuf, - int x, int y, int width, int height, - struct video_clip *clips, int n) +bttv_screen_clips(int swidth, int sheight, struct v4l2_rect *win, + struct v4l2_clip *clips, int n) { - if (x < 0) { + if (win->left < 0) { /* left */ - clips[n].x = 0; - clips[n].y = 0; - clips[n].width = -x; - clips[n].height = height; + clips[n].c.left = 0; + clips[n].c.top = 0; + clips[n].c.width = -win->left; + clips[n].c.height = win->height; n++; } - if (x+width > fbuf->width) { + if (win->left + win->width > swidth) { /* right */ - clips[n].x = fbuf->width - x; - clips[n].y = 0; - clips[n].width = width - clips[n].x; - clips[n].height = height; + clips[n].c.left = swidth - win->left; + clips[n].c.top = 0; + clips[n].c.width = win->width - clips[n].c.left; + clips[n].c.height = win->height; n++; } - if (y < 0) { + if (win->top < 0) { /* top */ - clips[n].x = 0; - clips[n].y = 0; - clips[n].width = width; - clips[n].height = -y; + clips[n].c.left = 0; + clips[n].c.top = 0; + clips[n].c.width = win->width; + clips[n].c.height = -win->top; n++; } - if (y+height > fbuf->height) { + if (win->top + win->height > sheight) { /* bottom */ - clips[n].x = 0; - clips[n].y = fbuf->height - y; - clips[n].width = width; - clips[n].height = height - clips[n].y; + clips[n].c.left = 0; + clips[n].c.top = sheight - win->top; + clips[n].c.width = win->width; + clips[n].c.height = win->height - clips[n].c.top; n++; } return n; } void -bttv_sort_clips(struct video_clip *clips, int nclips) +bttv_sort_clips(struct v4l2_clip *clips, int nclips) { - struct video_clip swap; + struct v4l2_clip swap; int i,j,n; for (i = nclips-2; i >= 0; i--) { for (n = 0, j = 0; j <= i; j++) { - if (clips[j].x > clips[j+1].x) { + if (clips[j].c.left > clips[j+1].c.left) { swap = clips[j]; clips[j] = clips[j+1]; clips[j+1] = swap; @@ -303,7 +302,7 @@ static void calc_skips(int line, int width, int *maxy, struct SKIPLIST *skips, int *nskips, - const struct video_clip *clips, int nclips) + const struct v4l2_clip *clips, int nclips) { int clip,skip,maxline,end; @@ -312,35 +311,35 @@ for (clip = 0; clip < nclips; clip++) { /* sanity checks */ - if (clips[clip].x + clips[clip].width <= 0) + if (clips[clip].c.left + clips[clip].c.width <= 0) continue; - if (clips[clip].x > width) + if (clips[clip].c.left > width) break; /* vertical range */ - if (line > clips[clip].y+clips[clip].height-1) + if (line > clips[clip].c.top+clips[clip].c.height-1) continue; - if (line < clips[clip].y) { - if (maxline > clips[clip].y-1) - maxline = clips[clip].y-1; + if (line < clips[clip].c.top) { + if (maxline > clips[clip].c.top-1) + maxline = clips[clip].c.top-1; continue; } - if (maxline > clips[clip].y+clips[clip].height-1) - maxline = clips[clip].y+clips[clip].height-1; + if (maxline > clips[clip].c.top+clips[clip].c.height-1) + maxline = clips[clip].c.top+clips[clip].c.height-1; /* horizontal range */ - if (0 == skip || clips[clip].x > skips[skip-1].end) { + if (0 == skip || clips[clip].c.left > skips[skip-1].end) { /* new one */ - skips[skip].start = clips[clip].x; + skips[skip].start = clips[clip].c.left; if (skips[skip].start < 0) skips[skip].start = 0; - skips[skip].end = clips[clip].x + clips[clip].width; + skips[skip].end = clips[clip].c.left + clips[clip].c.width; if (skips[skip].end > width) skips[skip].end = width; skip++; } else { /* overlaps -- expand last one */ - end = clips[clip].x + clips[clip].width; + end = clips[clip].c.left + clips[clip].c.width; if (skips[skip-1].end < end) skips[skip-1].end = end; if (skips[skip-1].end > width) @@ -362,12 +361,12 @@ int bttv_risc_overlay(struct bttv *btv, struct bttv_riscmem *risc, const struct bttv_format *fmt, struct bttv_overlay *ov, - int fields) + int skip_even, int skip_odd) { int instructions,rc,line,maxy,start,end,skip,nskips; struct SKIPLIST *skips; - unsigned long *rp,ri,ra; - unsigned long addr; + u32 *rp,ri,ra; + u32 addr; /* skip list for window clipping */ if (NULL == (skips = kmalloc(sizeof(*skips) * ov->nclips,GFP_KERNEL))) @@ -376,7 +375,7 @@ /* estimate risc mem: worst case is (clip+1) * lines instructions + sync + jump (all 2 dwords) */ instructions = (ov->nclips + 1) * - ((fields & VBUF_FIELD_INTER) ? ov->height>>1 : ov->height); + ((skip_even || skip_odd) ? ov->w.height>>1 : ov->w.height); instructions += 2; if ((rc = bttv_riscmem_alloc(btv->dev,risc,instructions*8)) < 0) return rc; @@ -387,29 +386,27 @@ *(rp++) = cpu_to_le32(0); addr = (unsigned long)btv->fbuf.base; - addr += btv->fbuf.bytesperline * ov->y; - addr += ((btv->fbuf.depth+7) >> 3) * ov->x; + addr += btv->fbuf.bytesperline * ov->w.top; + addr += ((btv->fbuf.depth+7) >> 3) * ov->w.left; /* scan lines */ - for (maxy = -1, line = 0; line < ov->height; + for (maxy = -1, line = 0; line < ov->w.height; line++, addr += btv->fbuf.bytesperline) { - if (fields & VBUF_FIELD_INTER) { - if ((line%2) != 0 && (fields & VBUF_FIELD_ODD)) - continue; - if ((line%2) != 1 && (fields & VBUF_FIELD_EVEN)) - continue; - } + if ((line%2) == 0 && skip_even) + continue; + if ((line%2) == 1 && skip_odd) + continue; /* calculate clipping */ if (line > maxy) - calc_skips(line, ov->width, &maxy, + calc_skips(line, ov->w.width, &maxy, skips, &nskips, ov->clips, ov->nclips); /* write out risc code */ - for (start = 0, skip = 0; start < ov->width; start = end) { + for (start = 0, skip = 0; start < ov->w.width; start = end) { if (skip >= nskips) { ri = BT848_RISC_WRITE; - end = ov->width; + end = ov->w.width; } else if (start < skips[skip].start) { ri = BT848_RISC_WRITE; end = skips[skip].start; @@ -425,7 +422,7 @@ if (0 == start) ri |= BT848_RISC_SOL; - if (ov->width == end) + if (ov->w.width == end) ri |= BT848_RISC_EOL; ri |= (fmt->depth>>3) * (end-start); @@ -448,19 +445,30 @@ bttv_calc_geo(struct bttv *btv, struct bttv_geometry *geo, int width, int height, int interleaved, int norm) { - const struct bttv_tvnorm *tvnorm; + const struct bttv_tvnorm *tvnorm = &bttv_tvnorms[norm]; u32 xsf, sr; int vdelay; - tvnorm = &bttv_tvnorms[norm]; + int swidth = tvnorm->swidth; + int totalwidth = tvnorm->totalwidth; + int scaledtwidth = tvnorm->scaledtwidth; + + if (btv->digital_video) { + swidth = 720; + totalwidth = 858; + scaledtwidth = 858; + } + vdelay = tvnorm->vdelay; +#if 0 /* FIXME */ if (vdelay < btv->vbi.lines*2) vdelay = btv->vbi.lines*2; +#endif - xsf = (width*tvnorm->scaledtwidth)/tvnorm->swidth; - geo->hscale = ((tvnorm->totalwidth*4096UL)/xsf-4096); + xsf = (width*scaledtwidth)/swidth; + geo->hscale = ((totalwidth*4096UL)/xsf-4096); geo->hdelay = tvnorm->hdelayx1; - geo->hdelay = (geo->hdelay*width)/tvnorm->swidth; + geo->hdelay = (geo->hdelay*width)/swidth; geo->hdelay &= 0x3fe; sr = ((tvnorm->sheight >> (interleaved?0:1))*512)/height - 512; geo->vscale = (0x10000UL-sr) & 0x1fff; @@ -512,8 +520,8 @@ int capctl; btv->cap_ctl = 0; - if (NULL != btv->odd) btv->cap_ctl |= 0x02; - if (NULL != btv->even) btv->cap_ctl |= 0x01; + if (NULL != btv->top) btv->cap_ctl |= 0x02; + if (NULL != btv->bottom) btv->cap_ctl |= 0x01; if (NULL != btv->vcurr) btv->cap_ctl |= 0x0c; capctl = 0; @@ -522,12 +530,12 @@ capctl |= override; d2printk(KERN_DEBUG - "bttv%d: capctl=%x irq=%d odd=%08Lx/%08Lx even=%08Lx/%08Lx\n", + "bttv%d: capctl=%x irq=%d top=%08Lx/%08Lx even=%08Lx/%08Lx\n", btv->nr,capctl,irqflags, - btv->vcurr ? (u64)btv->vcurr->odd.dma : 0, - btv->odd ? (u64)btv->odd->odd.dma : 0, - btv->vcurr ? (u64)btv->vcurr->even.dma : 0, - btv->even ? (u64)btv->even->even.dma : 0); + btv->vcurr ? (u64)btv->vcurr->top.dma : 0, + btv->top ? (u64)btv->top->top.dma : 0, + btv->vcurr ? (u64)btv->vcurr->bottom.dma : 0, + btv->bottom ? (u64)btv->bottom->bottom.dma : 0); cmd = BT848_RISC_JUMP; if (irqflags) { @@ -570,7 +578,7 @@ btv->main.cpu[2] = cpu_to_le32(BT848_RISC_JUMP); btv->main.cpu[3] = cpu_to_le32(btv->main.dma + (4<<2)); - /* odd field */ + /* top field */ btv->main.cpu[4] = cpu_to_le32(BT848_RISC_JUMP); btv->main.cpu[5] = cpu_to_le32(btv->main.dma + (6<<2)); btv->main.cpu[6] = cpu_to_le32(BT848_RISC_JUMP); @@ -580,13 +588,13 @@ BT848_FIFO_STATUS_VRO); btv->main.cpu[9] = cpu_to_le32(0); - /* even field */ + /* bottom field */ btv->main.cpu[10] = cpu_to_le32(BT848_RISC_JUMP); btv->main.cpu[11] = cpu_to_le32(btv->main.dma + (12<<2)); btv->main.cpu[12] = cpu_to_le32(BT848_RISC_JUMP); btv->main.cpu[13] = cpu_to_le32(btv->main.dma + (14<<2)); - /* jump back to odd field */ + /* jump back to top field */ btv->main.cpu[14] = cpu_to_le32(BT848_RISC_JUMP); btv->main.cpu[15] = cpu_to_le32(btv->main.dma + (0<<2)); @@ -625,43 +633,43 @@ videobuf_waiton(&buf->vb,0,0); videobuf_dma_pci_unmap(btv->dev, &buf->vb.dma); videobuf_dma_free(&buf->vb.dma); - bttv_riscmem_free(btv->dev,&buf->even); - bttv_riscmem_free(btv->dev,&buf->odd); + bttv_riscmem_free(btv->dev,&buf->bottom); + bttv_riscmem_free(btv->dev,&buf->top); buf->vb.state = STATE_NEEDS_INIT; } int bttv_buffer_activate(struct bttv *btv, - struct bttv_buffer *odd, - struct bttv_buffer *even) + struct bttv_buffer *top, + struct bttv_buffer *bottom) { - if (NULL != odd && NULL != even) { - odd->vb.state = STATE_ACTIVE; - even->vb.state = STATE_ACTIVE; - bttv_apply_geo(btv, &odd->geo, 1); - bttv_apply_geo(btv, &even->geo,0); - bttv_risc_hook(btv, RISC_SLOT_O_FIELD, &odd->odd, 0); - bttv_risc_hook(btv, RISC_SLOT_E_FIELD, &even->even, 0); - btaor((odd->btformat & 0xf0) | (even->btformat & 0x0f), + if (NULL != top && NULL != bottom) { + top->vb.state = STATE_ACTIVE; + bottom->vb.state = STATE_ACTIVE; + bttv_apply_geo(btv, &top->geo, 1); + bttv_apply_geo(btv, &bottom->geo,0); + bttv_risc_hook(btv, RISC_SLOT_O_FIELD, &top->top, 0); + bttv_risc_hook(btv, RISC_SLOT_E_FIELD, &bottom->bottom, 0); + btaor((top->btformat & 0xf0) | (bottom->btformat & 0x0f), ~0xff, BT848_COLOR_FMT); - btaor((odd->btswap & 0x0a) | (even->btswap & 0x05), + btaor((top->btswap & 0x0a) | (bottom->btswap & 0x05), ~0x0f, BT848_COLOR_CTL); - } else if (NULL != odd) { - odd->vb.state = STATE_ACTIVE; - bttv_apply_geo(btv, &odd->geo,1); - bttv_apply_geo(btv, &odd->geo,0); - bttv_risc_hook(btv, RISC_SLOT_O_FIELD, &odd->odd, 0); + } else if (NULL != top) { + top->vb.state = STATE_ACTIVE; + bttv_apply_geo(btv, &top->geo,1); + bttv_apply_geo(btv, &top->geo,0); + bttv_risc_hook(btv, RISC_SLOT_O_FIELD, &top->top, 0); bttv_risc_hook(btv, RISC_SLOT_E_FIELD, NULL, 0); - btaor(odd->btformat & 0xff, ~0xff, BT848_COLOR_FMT); - btaor(odd->btswap & 0x0f, ~0x0f, BT848_COLOR_CTL); - } else if (NULL != even) { - even->vb.state = STATE_ACTIVE; - bttv_apply_geo(btv, &even->geo,1); - bttv_apply_geo(btv, &even->geo,0); - bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0); - bttv_risc_hook(btv, RISC_SLOT_E_FIELD, &even->even, 0); - btaor(even->btformat & 0xff, ~0xff, BT848_COLOR_FMT); - btaor(even->btswap & 0x0f, ~0x0f, BT848_COLOR_CTL); + btaor(top->btformat & 0xff, ~0xff, BT848_COLOR_FMT); + btaor(top->btswap & 0x0f, ~0x0f, BT848_COLOR_CTL); + } else if (NULL != bottom) { + bottom->vb.state = STATE_ACTIVE; + bttv_apply_geo(btv, &bottom->geo,1); + bttv_apply_geo(btv, &bottom->geo,0); + bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0); + bttv_risc_hook(btv, RISC_SLOT_E_FIELD, &bottom->bottom, 0); + btaor(bottom->btformat & 0xff, ~0xff, BT848_COLOR_FMT); + btaor(bottom->btswap & 0x0f, ~0x0f, BT848_COLOR_CTL); } else { bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0); bttv_risc_hook(btv, RISC_SLOT_E_FIELD, NULL, 0); @@ -671,72 +679,53 @@ /* ---------------------------------------------------------- */ -int -bttv_buffer_field(struct bttv *btv, int field, int def_field, - int norm, int height) -{ - const struct bttv_tvnorm *tvnorm = bttv_tvnorms + norm; - - /* check interleave, even+odd fields */ - if (height > (tvnorm->sheight >> 1)) { - field = VBUF_FIELD_ODD | VBUF_FIELD_EVEN | VBUF_FIELD_INTER; - } else { - if (field & def_field) { - field = def_field; - } else { - field = (def_field & VBUF_FIELD_EVEN) - ? VBUF_FIELD_ODD : VBUF_FIELD_EVEN; - } - } - return field; -} - /* calculate geometry, build risc code */ int bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf) { const struct bttv_tvnorm *tvnorm = bttv_tvnorms + buf->tvnorm; - buf->vb.field = bttv_buffer_field(btv,buf->vb.field, VBUF_FIELD_EVEN, - buf->tvnorm,buf->vb.height); dprintk(KERN_DEBUG - "bttv%d: buffer flags:%s%s%s format: %s size: %dx%d\n", - btv->nr, - (buf->vb.field & VBUF_FIELD_INTER) ? " interleave" : "", - (buf->vb.field & VBUF_FIELD_ODD) ? " odd" : "", - (buf->vb.field & VBUF_FIELD_EVEN) ? " even" : "", - buf->fmt->name,buf->vb.width,buf->vb.height); + "bttv%d: buffer field: %s format: %s size: %dx%d\n", + btv->nr, v4l2_field_names[buf->vb.field], + buf->fmt->name, buf->vb.width, buf->vb.height); /* packed pixel modes */ if (buf->fmt->flags & FORMAT_FLAGS_PACKED) { - int bpl, padding, lines; - bpl = (buf->fmt->depth >> 3) * buf->vb.width; + int bpl = (buf->fmt->depth >> 3) * buf->vb.width; + int bpf = bpl * (buf->vb.height >> 1); - /* calculate geometry */ - if (buf->vb.field & VBUF_FIELD_INTER) { - bttv_calc_geo(btv,&buf->geo,buf->vb.width, - buf->vb.height,1,buf->tvnorm); - lines = buf->vb.height >> 1; - padding = bpl; - } else { - bttv_calc_geo(btv,&buf->geo,buf->vb.width, - buf->vb.height,0,buf->tvnorm); - lines = buf->vb.height; - padding = 0; - } + bttv_calc_geo(btv,&buf->geo,buf->vb.width,buf->vb.height, + V4L2_FIELD_HAS_BOTH(buf->vb.field),buf->tvnorm); - /* build risc code */ - if (buf->vb.field & VBUF_FIELD_ODD) - bttv_risc_packed(btv,&buf->odd,buf->vb.dma.sglist, - 0,bpl,padding,lines); - if (buf->vb.field & VBUF_FIELD_EVEN) - bttv_risc_packed(btv,&buf->even,buf->vb.dma.sglist, - padding,bpl,padding,lines); + switch (buf->vb.field) { + case V4L2_FIELD_TOP: + bttv_risc_packed(btv,&buf->top,buf->vb.dma.sglist, + 0,bpl,0,buf->vb.height); + break; + case V4L2_FIELD_BOTTOM: + bttv_risc_packed(btv,&buf->bottom,buf->vb.dma.sglist, + 0,bpl,0,buf->vb.height); + break; + case V4L2_FIELD_INTERLACED: + bttv_risc_packed(btv,&buf->top,buf->vb.dma.sglist, + 0,bpl,bpl,buf->vb.height >> 1); + bttv_risc_packed(btv,&buf->bottom,buf->vb.dma.sglist, + bpl,bpl,bpl,buf->vb.height >> 1); + break; + case V4L2_FIELD_SEQ_TB: + bttv_risc_packed(btv,&buf->top,buf->vb.dma.sglist, + 0,bpl,0,buf->vb.height >> 1); + bttv_risc_packed(btv,&buf->bottom,buf->vb.dma.sglist, + bpf,bpl,0,buf->vb.height >> 1); + break; + default: + BUG(); + } } /* planar modes */ if (buf->fmt->flags & FORMAT_FLAGS_PLANAR) { - struct bttv_riscmem *risc; int uoffset, voffset; int ypadding, cpadding, lines; @@ -755,54 +744,59 @@ voffset += uoffset; } - if (buf->vb.field & VBUF_FIELD_INTER) { + switch (buf->vb.field) { + case V4L2_FIELD_TOP: + bttv_calc_geo(btv,&buf->geo,buf->vb.width, + buf->vb.height,0,buf->tvnorm); + bttv_risc_planar(btv, &buf->top, buf->vb.dma.sglist, + 0,buf->vb.width,0,buf->vb.height, + uoffset,voffset,buf->fmt->hshift, + buf->fmt->vshift,0); + break; + case V4L2_FIELD_BOTTOM: + bttv_calc_geo(btv,&buf->geo,buf->vb.width, + buf->vb.height,0,buf->tvnorm); + bttv_risc_planar(btv, &buf->bottom, buf->vb.dma.sglist, + 0,buf->vb.width,0,buf->vb.height, + uoffset,voffset,buf->fmt->hshift, + buf->fmt->vshift,0); + break; + case V4L2_FIELD_INTERLACED: bttv_calc_geo(btv,&buf->geo,buf->vb.width, buf->vb.height,1,buf->tvnorm); lines = buf->vb.height >> 1; ypadding = buf->vb.width; - if (!buf->fmt->vshift) { - /* chroma planes are interleaved too */ - cpadding = buf->vb.width >> buf->fmt->hshift; - } else { - cpadding = 0; - } - bttv_risc_planar(btv,&buf->odd, + cpadding = buf->vb.width >> buf->fmt->hshift; + bttv_risc_planar(btv,&buf->top, buf->vb.dma.sglist, 0,buf->vb.width,ypadding,lines, - uoffset,voffset,buf->fmt->hshift, + uoffset,voffset, + buf->fmt->hshift, buf->fmt->vshift >> 1, cpadding); - bttv_risc_planar(btv,&buf->even, + bttv_risc_planar(btv,&buf->bottom, buf->vb.dma.sglist, ypadding,buf->vb.width,ypadding,lines, uoffset+cpadding, voffset+cpadding, buf->fmt->hshift, - cpadding ? (buf->fmt->vshift>>1) : -1, + buf->fmt->vshift >> 1, cpadding); - } else { - if (buf->vb.field & VBUF_FIELD_ODD) - risc = &buf->odd; - else - risc = &buf->even; - bttv_calc_geo(btv,&buf->geo,buf->vb.width, - buf->vb.height,0,buf->tvnorm); - bttv_risc_planar(btv, risc, buf->vb.dma.sglist, - 0,buf->vb.width,0,buf->vb.height, - uoffset,voffset,buf->fmt->hshift, - buf->fmt->vshift,0); + break; + default: + BUG(); } } /* raw data */ if (buf->fmt->flags & FORMAT_FLAGS_RAW) { /* build risc code */ - buf->vb.field = VBUF_FIELD_INTER | VBUF_FIELD_ODD | VBUF_FIELD_EVEN; + buf->vb.field = V4L2_FIELD_SEQ_TB; bttv_calc_geo(btv,&buf->geo,tvnorm->swidth,tvnorm->sheight, 1,buf->tvnorm); - bttv_risc_packed(btv, &buf->odd, buf->vb.dma.sglist, + bttv_risc_packed(btv, &buf->top, buf->vb.dma.sglist, 0, RAW_BPL, 0, RAW_LINES); - bttv_risc_packed(btv, &buf->even, buf->vb.dma.sglist, + bttv_risc_packed(btv, &buf->bottom, buf->vb.dma.sglist, buf->vb.size/2 , RAW_BPL, 0, RAW_LINES); } @@ -821,35 +815,41 @@ const struct bttv_format *fmt, struct bttv_buffer *buf) { - int interleave; - - /* check interleave, even+odd fields */ - buf->vb.field = bttv_buffer_field(btv, 0, VBUF_FIELD_ODD, - ov->tvnorm,ov->height); + /* check interleave, bottom+top fields */ dprintk(KERN_DEBUG - "bttv%d: overlay flags:%s%s%s format: %s size: %dx%d\n", - btv->nr, - (buf->vb.field & VBUF_FIELD_INTER) ? " interleave" : "", - (buf->vb.field & VBUF_FIELD_ODD) ? " odd" : "", - (buf->vb.field & VBUF_FIELD_EVEN) ? " even" : "", - fmt->name,ov->width,ov->height); + "bttv%d: overlay fields: %s format: %s size: %dx%d\n", + btv->nr, v4l2_field_names[buf->vb.field], + fmt->name,ov->w.width,ov->w.height); /* calculate geometry */ - interleave = buf->vb.field & VBUF_FIELD_INTER; - bttv_calc_geo(btv,&buf->geo,ov->width,ov->height, - interleave, ov->tvnorm); + bttv_calc_geo(btv,&buf->geo,ov->w.width,ov->w.height, + V4L2_FIELD_HAS_BOTH(ov->field), ov->tvnorm); /* build risc code */ - if (buf->vb.field & VBUF_FIELD_ODD) - bttv_risc_overlay(btv, &buf->odd, fmt, ov, - interleave | VBUF_FIELD_ODD); - if (buf->vb.field & VBUF_FIELD_EVEN) - bttv_risc_overlay(btv, &buf->even,fmt, ov, - interleave | VBUF_FIELD_EVEN); + switch (ov->field) { + case V4L2_FIELD_TOP: + bttv_risc_overlay(btv, &buf->top, fmt, ov, 0, 0); + break; + case V4L2_FIELD_BOTTOM: + bttv_risc_overlay(btv, &buf->bottom, fmt, ov, 0, 0); + break; + case V4L2_FIELD_INTERLACED: +#if 0 + bttv_risc_overlay(btv, &buf->top, fmt, ov, 1, 0); + bttv_risc_overlay(btv, &buf->bottom, fmt, ov, 0, 1); +#else + bttv_risc_overlay(btv, &buf->top, fmt, ov, 0, 1); + bttv_risc_overlay(btv, &buf->bottom, fmt, ov, 1, 0); +#endif + break; + default: + BUG(); + } /* copy format info */ buf->btformat = fmt->btformat; buf->btswap = fmt->btswap; + buf->vb.field = ov->field; return 0; } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/media/video/bttv-vbi.c linux-2.5-bk/drivers/media/video/bttv-vbi.c --- linux-2.5.45/drivers/media/video/bttv-vbi.c Wed Oct 30 16:43:06 2002 +++ linux-2.5-bk/drivers/media/video/bttv-vbi.c Thu Oct 31 22:12:49 2002 @@ -47,58 +47,51 @@ #define dprintk(fmt, arg...) if (vbi_debug) \ printk(KERN_DEBUG "bttv%d/vbi: " fmt, btv->nr, ## arg) -#ifndef HAVE_V4L2 -/* some dummy defines to avoid cluttering up the source code with - a huge number of ifdef's for V4L2 */ -# define V4L2_BUF_TYPE_CAPTURE -1 -# define V4L2_BUF_TYPE_VBI -1 -#endif - /* ----------------------------------------------------------------------- */ /* vbi risc code + mm */ static int -vbi_buffer_risc(struct bttv *btv, struct bttv_buffer *buf) +vbi_buffer_risc(struct bttv *btv, struct bttv_buffer *buf, int lines) { int bpl = 2048; - bttv_risc_packed(btv, &buf->odd, buf->vb.dma.sglist, - 0, bpl-4, 4, btv->vbi.lines); - bttv_risc_packed(btv, &buf->even, buf->vb.dma.sglist, - btv->vbi.lines * bpl, bpl-4, 4, btv->vbi.lines); + bttv_risc_packed(btv, &buf->top, buf->vb.dma.sglist, + 0, bpl-4, 4, lines); + bttv_risc_packed(btv, &buf->bottom, buf->vb.dma.sglist, + lines * bpl, bpl-4, 4, lines); return 0; } static int vbi_buffer_setup(struct file *file, int *count, int *size) { - struct bttv *btv = file->private_data; + struct bttv_fh *fh = file->private_data; if (0 == *count) *count = vbibufs; - *size = btv->vbi.lines * 2 * 2048; + *size = fh->lines * 2 * 2048; return 0; } -static int vbi_buffer_prepare(struct file *file, struct videobuf_buffer *vb, - int fields) +static int vbi_buffer_prepare(struct file *file, struct videobuf_buffer *vb) { - struct bttv *btv = file->private_data; + struct bttv_fh *fh = file->private_data; + struct bttv *btv = fh->btv; struct bttv_buffer *buf = (struct bttv_buffer*)vb; int rc; - buf->vb.size = btv->vbi.lines * 2 * 2048; + buf->vb.size = fh->lines * 2 * 2048; if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) return -EINVAL; if (STATE_NEEDS_INIT == buf->vb.state) { if (0 != (rc = videobuf_iolock(btv->dev,&buf->vb))) goto fail; - if (0 != (rc = vbi_buffer_risc(btv,buf))) + if (0 != (rc = vbi_buffer_risc(btv,buf,fh->lines))) goto fail; } buf->vb.state = STATE_PREPARED; - dprintk("buf prepare %p: odd=%p even=%p\n", - vb,&buf->odd,&buf->even); + dprintk("buf prepare %p: top=%p bottom=%p\n", + vb,&buf->top,&buf->bottom); return 0; fail: @@ -109,7 +102,8 @@ static void vbi_buffer_queue(struct file *file, struct videobuf_buffer *vb) { - struct bttv *btv = file->private_data; + struct bttv_fh *fh = file->private_data; + struct bttv *btv = fh->btv; struct bttv_buffer *buf = (struct bttv_buffer*)vb; dprintk("queue %p\n",vb); @@ -120,14 +114,15 @@ static void vbi_buffer_release(struct file *file, struct videobuf_buffer *vb) { - struct bttv *btv = file->private_data; + struct bttv_fh *fh = file->private_data; + struct bttv *btv = fh->btv; struct bttv_buffer *buf = (struct bttv_buffer*)vb; dprintk("free %p\n",vb); - bttv_dma_free(btv,buf); + bttv_dma_free(fh->btv,buf); } -struct videobuf_queue_ops vbi_qops = { +struct videobuf_queue_ops bttv_vbi_qops = { buf_setup: vbi_buffer_setup, buf_prepare: vbi_buffer_prepare, buf_queue: vbi_buffer_queue, @@ -136,7 +131,7 @@ /* ----------------------------------------------------------------------- */ -static void vbi_setlines(struct bttv *btv, int lines) +void bttv_vbi_setlines(struct bttv_fh *fh, struct bttv *btv, int lines) { int vdelay; @@ -144,7 +139,7 @@ lines = 1; if (lines > VBI_MAXLINES) lines = VBI_MAXLINES; - btv->vbi.lines = lines; + fh->lines = lines; vdelay = btread(BT848_E_VDELAY_LO); if (vdelay < lines*2) { @@ -154,19 +149,18 @@ } } -#ifdef HAVE_V4L2 -static void vbi_fmt(struct bttv *btv, struct v4l2_format *f) +void bttv_vbi_fmt(struct bttv_fh *fh, struct v4l2_format *f) { memset(f,0,sizeof(*f)); - f->type = V4L2_BUF_TYPE_VBI; + f->type = V4L2_BUF_TYPE_VBI_CAPTURE; f->fmt.vbi.sampling_rate = 35468950; f->fmt.vbi.samples_per_line = 2048; - f->fmt.vbi.sample_format = V4L2_VBI_SF_UBYTE; + f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; f->fmt.vbi.offset = 244; - f->fmt.vbi.count[0] = btv->vbi.lines; - f->fmt.vbi.count[1] = btv->vbi.lines; + f->fmt.vbi.count[0] = fh->lines; + f->fmt.vbi.count[1] = fh->lines; f->fmt.vbi.flags = 0; - switch (btv->tvnorm) { + switch (fh->btv->tvnorm) { case 1: /* NTSC */ f->fmt.vbi.start[0] = 10; f->fmt.vbi.start[1] = 273; @@ -178,212 +172,8 @@ f->fmt.vbi.start[1] = 319; } } -#endif /* ----------------------------------------------------------------------- */ -/* vbi interface */ - -static int vbi_open(struct inode *inode, struct file *file) -{ - unsigned int minor = minor(inode->i_rdev); - struct bttv *btv = NULL; - int i; - - for (i = 0; i < bttv_num; i++) { - if (bttvs[i].vbi_dev.minor == minor) { - btv = &bttvs[i]; - break; - } - } - if (NULL == btv) - return -ENODEV; - - down(&btv->vbi.q.lock); - if (btv->vbi.users) { - up(&btv->vbi.q.lock); - return -EBUSY; - } - dprintk("open minor=%d\n",minor); - file->private_data = btv; - btv->vbi.users++; - vbi_setlines(btv,VBI_DEFLINES); - bttv_field_count(btv); - - up(&btv->vbi.q.lock); - - return 0; -} - -static int vbi_release(struct inode *inode, struct file *file) -{ - struct bttv *btv = file->private_data; - - if (btv->vbi.q.streaming) - videobuf_streamoff(file,&btv->vbi.q); - down(&btv->vbi.q.lock); - if (btv->vbi.q.reading) - videobuf_read_stop(file,&btv->vbi.q); - btv->vbi.users--; - bttv_field_count(btv); - vbi_setlines(btv,0); - up(&btv->vbi.q.lock); - return 0; -} - -static int vbi_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) -{ - struct bttv *btv = file->private_data; - - if (btv->errors) - bttv_reinit_bt848(btv); - switch (cmd) { - case VIDIOCGCAP: - { - struct video_capability *cap = arg; - - memset(cap,0,sizeof(*cap)); - strcpy(cap->name,btv->vbi_dev.name); - cap->type = VID_TYPE_TUNER|VID_TYPE_TELETEXT; - return 0; - } - - /* vbi/teletext ioctls */ - case BTTV_VBISIZE: - return btv->vbi.lines * 2 * 2048; - - case BTTV_VERSION: - case VIDIOCGFREQ: - case VIDIOCSFREQ: - case VIDIOCGTUNER: - case VIDIOCSTUNER: - case VIDIOCGCHAN: - case VIDIOCSCHAN: - return bttv_common_ioctls(btv,cmd,arg); - -#ifdef HAVE_V4L2 - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *cap = arg; - - memset(cap,0,sizeof(*cap)); - strcpy(cap->name, btv->name); - cap->type = V4L2_TYPE_VBI; - cap->flags = V4L2_FLAG_TUNER | V4L2_FLAG_READ | - V4L2_FLAG_STREAMING | V4L2_FLAG_SELECT; - return 0; - } - case VIDIOC_G_FMT: - { - struct v4l2_format *f = arg; - - vbi_fmt(btv,f); - return 0; - } - case VIDIOC_S_FMT: - { - struct v4l2_format *f = arg; - - if (btv->vbi.q.reading || btv->vbi.q.streaming) - return -EBUSY; - vbi_setlines(btv,f->fmt.vbi.count[0]); - vbi_fmt(btv,f); - return 0; - } - - case VIDIOC_REQBUFS: - return videobuf_reqbufs(file,&btv->vbi.q,arg); - - case VIDIOC_QUERYBUF: - return videobuf_querybuf(&btv->vbi.q, arg); - - case VIDIOC_QBUF: - return videobuf_qbuf(file, &btv->vbi.q, arg); - - case VIDIOC_DQBUF: - return videobuf_dqbuf(file, &btv->vbi.q, arg); - - case VIDIOC_STREAMON: - return videobuf_streamon(file, &btv->vbi.q); - - case VIDIOC_STREAMOFF: - return videobuf_streamoff(file, &btv->vbi.q); - - case VIDIOC_ENUMSTD: - case VIDIOC_G_STD: - case VIDIOC_S_STD: - case VIDIOC_ENUMINPUT: - case VIDIOC_G_INPUT: - case VIDIOC_S_INPUT: - case VIDIOC_G_TUNER: - case VIDIOC_S_TUNER: - case VIDIOC_G_FREQ: - case VIDIOC_S_FREQ: - return bttv_common_ioctls(btv,cmd,arg); -#endif /* HAVE_V4L2 */ - - default: - return -ENOIOCTLCMD; - } - return 0; -} - -static int vbi_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return video_usercopy(inode, file, cmd, arg, vbi_do_ioctl); -} - -static ssize_t vbi_read(struct file *file, char *data, - size_t count, loff_t *ppos) -{ - struct bttv *btv = file->private_data; - - if (btv->errors) - bttv_reinit_bt848(btv); - dprintk("read %d\n",count); - return videobuf_read_stream(file, &btv->vbi.q, data, count, ppos, 1); -} - -static unsigned int vbi_poll(struct file *file, poll_table *wait) -{ - struct bttv *btv = file->private_data; - - dprintk("poll%s\n",""); - return videobuf_poll_stream(file, &btv->vbi.q, wait); -} - -static int -vbi_mmap(struct file *file, struct vm_area_struct * vma) -{ - struct bttv *btv = file->private_data; - - dprintk("mmap 0x%lx+%ld\n",vma->vm_start, - vma->vm_end - vma->vm_start); - return videobuf_mmap_mapper(vma, &btv->vbi.q); -} - -static struct file_operations vbi_fops = -{ - owner: THIS_MODULE, - open: vbi_open, - release: vbi_release, - ioctl: vbi_ioctl, - llseek: no_llseek, - read: vbi_read, - poll: vbi_poll, - mmap: vbi_mmap, -}; - -struct video_device bttv_vbi_template = -{ - name: "bt848/878 vbi", - type: VID_TYPE_TUNER|VID_TYPE_TELETEXT, - hardware: VID_HARDWARE_BT848, - fops: &vbi_fops, - minor: -1, -}; - /* * Local variables: * c-basic-offset: 8 diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/media/video/bttv.h linux-2.5-bk/drivers/media/video/bttv.h --- linux-2.5.45/drivers/media/video/bttv.h Wed Oct 30 16:41:56 2002 +++ linux-2.5-bk/drivers/media/video/bttv.h Thu Oct 31 22:12:49 2002 @@ -90,10 +90,26 @@ #define BTTV_SENSORAY311 0x49 #define BTTV_RV605 0x4a #define BTTV_WINDVR 0x4c +#define BTTV_HAUPPAUGEPVR 0x50 +#define BTTV_GVBCTV5PCI 0x51 +#define BTTV_OSPREY1x0 0x52 +#define BTTV_OSPREY1x0_848 0x53 +#define BTTV_OSPREY101_848 0x54 +#define BTTV_OSPREY1x1 0x55 +#define BTTV_OSPREY1x1_SVID 0x56 +#define BTTV_OSPREY2xx 0x57 +#define BTTV_OSPREY2x0_SVID 0x58 +#define BTTV_OSPREY2x0 0x59 +#define BTTV_OSPREY500 0x5a +#define BTTV_OSPREY540 0x5b +#define BTTV_OSPREY2000 0x5c +#define BTTV_IDS_EAGLE 0x5d /* i2c address list */ #define I2C_TSA5522 0xc2 #define I2C_TDA7432 0x8a +#define I2C_BT832_ALT1 0x88 +#define I2C_BT832_ALT2 0x8a // alternate setting #define I2C_TDA8425 0x82 #define I2C_TDA9840 0x84 #define I2C_TDA9850 0xb6 /* also used by 9855,9873 */ @@ -105,6 +121,7 @@ #define I2C_MSP3400 0x80 #define I2C_TEA6300 0x80 #define I2C_DPL3518 0x84 +#define I2C_TDA9887 0x86 /* more card-specific defines */ #define PT2254_L_CHANNEL 0x10 @@ -132,6 +149,7 @@ /* i2c audio flags */ int no_msp34xx:1; int no_tda9875:1; + int no_tda7432:1; int needs_tvaudio:1; /* other settings */ @@ -174,6 +192,7 @@ returns negative value if error occurred */ extern int bttv_get_cardinfo(unsigned int card, int *type, int *cardid); +extern struct pci_dev* bttv_get_pcidev(unsigned int card); /* obsolete, use bttv_get_cardinfo instead */ extern int bttv_get_id(unsigned int card); @@ -207,6 +226,11 @@ process data ASAP */ extern wait_queue_head_t* bttv_get_gpio_queue(unsigned int card); + +/* call i2c clients +*/ +extern void bttv_i2c_call(unsigned int card, unsigned int cmd, void *arg); + /* i2c */ #define I2C_CLIENTS_MAX 16 diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/media/video/bttvp.h linux-2.5-bk/drivers/media/video/bttvp.h --- linux-2.5.45/drivers/media/video/bttvp.h Wed Oct 30 16:42:26 2002 +++ linux-2.5-bk/drivers/media/video/bttvp.h Thu Oct 31 22:12:49 2002 @@ -24,7 +24,7 @@ #ifndef _BTTVP_H_ #define _BTTVP_H_ -#define BTTV_VERSION_CODE KERNEL_VERSION(0,8,42) +#define BTTV_VERSION_CODE KERNEL_VERSION(0,9,1) #include #include @@ -54,7 +54,8 @@ #define RISC_SLOT_LOOP 14 #define RESOURCE_OVERLAY 1 -#define RESOURCE_STREAMING 2 +#define RESOURCE_VIDEO 2 +#define RESOURCE_VBI 3 #define RAW_LINES 640 #define RAW_BPL 1024 @@ -63,15 +64,17 @@ struct bttv_tvnorm { - int v4l2_id; - u32 Fsc; - u16 swidth, sheight; /* scaled standard width, height */ - u16 totalwidth; - u8 adelay, bdelay, iform; - u32 scaledtwidth; - u16 hdelayx1, hactivex1; - u16 vdelay; - u8 vbipack; + int v4l2_id; + char *name; + u32 Fsc; + u16 swidth, sheight; /* scaled standard width, height */ + u16 totalwidth; + u8 adelay, bdelay, iform; + u32 scaledtwidth; + u16 hdelayx1, hactivex1; + u16 vdelay; + u8 vbipack; + int sram; }; extern const struct bttv_tvnorm bttv_tvnorms[]; extern const int BTTV_TVNORMS; @@ -99,8 +102,8 @@ struct bttv_riscmem { unsigned int size; - unsigned long *cpu; - unsigned long *jmp; + u32 *cpu; + u32 *jmp; dma_addr_t dma; }; @@ -114,32 +117,34 @@ int btformat; int btswap; struct bttv_geometry geo; - struct bttv_riscmem even; - struct bttv_riscmem odd; + struct bttv_riscmem top; + struct bttv_riscmem bottom; }; struct bttv_overlay { int tvnorm; - int x,y,width,height; - struct video_clip *clips; + struct v4l2_rect w; + enum v4l2_field field; + struct v4l2_clip *clips; int nclips; }; -struct bttv_vbi { - int users; - int lines; - struct videobuf_queue q; -}; - struct bttv_fh { struct bttv *btv; - struct videobuf_queue q; int resources; - + enum v4l2_buf_type type; + + /* video capture */ + struct videobuf_queue cap; + struct bttv_buffer buf; + /* current settings */ const struct bttv_format *ovfmt; struct bttv_overlay ov; - struct bttv_buffer buf; + + /* video overlay */ + struct videobuf_queue vbi; + int lines; }; /* ---------------------------------------------------------- */ @@ -163,18 +168,18 @@ int cpadding); /* risc code generator + helpers - screen overlay */ -int bttv_screen_clips(struct video_buffer *fbuf, - int x, int y, int width, int height, - struct video_clip *clips, int n); -void bttv_sort_clips(struct video_clip *clips, int nclips); +int bttv_screen_clips(int swidth, int sheight, struct v4l2_rect *win, + struct v4l2_clip *clips, int n); +void bttv_sort_clips(struct v4l2_clip *clips, int nclips); int bttv_risc_overlay(struct bttv *btv, struct bttv_riscmem *risc, const struct bttv_format *fmt, - struct bttv_overlay *ov, int flags); + struct bttv_overlay *ov, + int skip_top, int skip_bottom); /* calculate / apply geometry settings */ void bttv_calc_geo(struct bttv *btv, struct bttv_geometry *geo, int width, int height, int interleaved, int norm); -void bttv_apply_geo(struct bttv *btv, struct bttv_geometry *geo, int odd); +void bttv_apply_geo(struct bttv *btv, struct bttv_geometry *geo, int top); /* control dma register + risc main loop */ void bttv_set_dma(struct bttv *btv, int override, int irqflags); @@ -183,11 +188,9 @@ int irqflags); /* capture buffer handling */ -int bttv_buffer_field(struct bttv *btv, int field, int def_field, - int tvnorm, int height); int bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf); -int bttv_buffer_activate(struct bttv *btv, struct bttv_buffer *odd, - struct bttv_buffer *even); +int bttv_buffer_activate(struct bttv *btv, struct bttv_buffer *top, + struct bttv_buffer *bottom); void bttv_dma_free(struct bttv *btv, struct bttv_buffer *buf); /* overlay handling */ @@ -199,8 +202,10 @@ /* ---------------------------------------------------------- */ /* bttv-vbi.c */ -extern struct video_device bttv_vbi_template; -extern struct videobuf_queue_ops vbi_qops; +void bttv_vbi_fmt(struct bttv_fh *fh, struct v4l2_format *f); +void bttv_vbi_setlines(struct bttv_fh *fh, struct bttv *btv, int lines); + +extern struct videobuf_queue_ops bttv_vbi_qops; /* ---------------------------------------------------------- */ @@ -212,6 +217,7 @@ extern unsigned int bttv_gpio; extern void bttv_gpio_tracking(struct bttv *btv, char *comment); extern int init_bttv_i2c(struct bttv *btv); +extern int pvr_boot(struct bttv *btv); extern int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg); extern void bttv_reinit_bt848(struct bttv *btv); @@ -285,6 +291,7 @@ int tvnorm,hue,contrast,bright,saturation; struct video_buffer fbuf; int field_count; + int digital_video; /* various options */ int opt_combfilter; @@ -293,27 +300,32 @@ int opt_chroma_agc; int opt_adc_crush; - /* vbi data/state */ - struct bttv_vbi vbi; - /* radio data/state */ int has_radio; + int radio_user; + + /* miro/pinnacle + Aimslab VHX + philips matchbox (tea5757 radio tuner) support */ int has_matchbox; - int mbox_we; + int mbox_we; int mbox_data; int mbox_clk; int mbox_most; int mbox_mask; - int radio_user; - + + /* ISA stuff (Terratec Active Radio Upgrade) */ + int mbox_ior; + int mbox_iow; + int mbox_csel; + /* risc memory management data - must aquire s_lock before changing these - only the irq handler is supported to touch odd + even */ struct bttv_riscmem main; - struct bttv_buffer *odd; /* current active odd field */ - struct bttv_buffer *even; /* current active even field */ - struct bttv_buffer *screen; /* overlay */ - struct list_head capture; /* capture buffer queue */ + struct bttv_buffer *top; /* current active top field */ + struct bttv_buffer *bottom; /* current active bottom field */ + struct bttv_buffer *screen; /* overlay */ + struct list_head capture; /* capture buffer queue */ struct bttv_buffer *vcurr; struct list_head vcapture; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/media/video/saa7134/Makefile linux-2.5-bk/drivers/media/video/saa7134/Makefile --- linux-2.5.45/drivers/media/video/saa7134/Makefile Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/drivers/media/video/saa7134/Makefile Thu Oct 31 22:12:49 2002 @@ -0,0 +1,11 @@ + +saa7134-objs := saa7134-cards.o saa7134-core.o saa7134-i2c.o \ + saa7134-oss.o saa7134-ts.o saa7134-tvaudio.o \ + saa7134-vbi.o saa7134-video.o + +obj-$(CONFIG_VIDEO_SAA7134) += saa7134.o + +EXTRA_CFLAGS = -I$(src)/.. + +include $(TOPDIR)/Rules.make + diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/media/video/saa7134/saa7134-cards.c linux-2.5-bk/drivers/media/video/saa7134/saa7134-cards.c --- linux-2.5.45/drivers/media/video/saa7134/saa7134-cards.c Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/drivers/media/video/saa7134/saa7134-cards.c Thu Oct 31 22:12:49 2002 @@ -0,0 +1,361 @@ +/* + * device driver for philips saa7134 based TV cards + * card-specific stuff. + * + * (c) 2001,02 Gerd Knorr [SuSE Labs] + * + * 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. + */ + +#include +#include + +#include "saa7134-reg.h" +#include "saa7134.h" +#include "tuner.h" + +/* commly used strings */ +static char name_mute[] = "mute"; +static char name_radio[] = "Radio"; +static char name_tv[] = "Television"; +static char name_comp1[] = "Composite1"; +static char name_comp2[] = "Composite2"; +static char name_svideo[] = "S-Video"; + +/* ------------------------------------------------------------------ */ +/* board config info */ + +struct saa7134_board saa7134_boards[] = { + [SAA7134_BOARD_UNKNOWN] = { + name: "UNKNOWN/GENERIC", + audio_clock: 0x00187de7, + tuner_type: TUNER_ABSENT, + inputs: {{ + name: "default", + vmux: 0, + amux: LINE1, + }}, + }, + [SAA7134_BOARD_PROTEUS_PRO] = { + /* /me */ + name: "Proteus Pro [philips reference design]", + audio_clock: 0x00187de7, + tuner_type: TUNER_PHILIPS_PAL, + inputs: {{ + name: name_comp1, + vmux: 0, + amux: LINE1, + },{ + name: name_tv, + vmux: 1, + amux: TV, + tv: 1, + }}, + }, + [SAA7134_BOARD_FLYVIDEO3000] = { + /* "Marco d'Itri" */ + name: "LifeView FlyVIDEO3000", + audio_clock: 0x00200000, + tuner_type: TUNER_PHILIPS_PAL, + inputs: {{ + name: name_tv, + vmux: 1, + amux: TV, + tv: 1, + },{ + name: name_comp1, + vmux: 0, + amux: LINE1, + },{ + name: name_comp2, + vmux: 3, + amux: LINE1, + },{ + name: name_svideo, + vmux: 8, + amux: LINE1, + }}, + radio: { + name: name_radio, + amux: LINE2, + }, + }, + [SAA7134_BOARD_FLYVIDEO2000] = { + /* "TC Wan" */ + name: "LifeView FlyVIDEO2000", + audio_clock: 0x00200000, + tuner_type: TUNER_LG_PAL_NEW_TAPC, + gpiomask: 0x6000, + inputs: {{ + name: name_tv, + vmux: 1, + amux: LINE2, + gpio: 0x0000, + tv: 1, + },{ + name: name_comp1, + vmux: 0, + amux: LINE2, + gpio: 0x4000, + },{ + name: name_comp2, + vmux: 3, + amux: LINE2, + gpio: 0x4000, + },{ + name: name_svideo, + vmux: 8, + amux: LINE2, + gpio: 0x4000, + }}, + radio: { + name: name_radio, + amux: LINE2, + }, + mute: { + name: name_mute, + amux: LINE1, + }, + }, + [SAA7134_BOARD_EMPRESS] = { + /* "Gert Vervoort" */ + name: "EMPRESS", + audio_clock: 0x00187de7, + tuner_type: TUNER_PHILIPS_PAL, + inputs: {{ + name: name_comp1, + vmux: 0, + amux: LINE1, + },{ + name: name_svideo, + vmux: 8, + amux: LINE1, + },{ + name: name_tv, + vmux: 1, + amux: LINE2, + tv: 1, + }}, + radio: { + name: name_radio, + amux: LINE2, + }, + i2s_rate: 48000, + has_ts: 1, + video_out: CCIR656, + }, + [SAA7134_BOARD_MONSTERTV] = { + /* "K.Ohta" */ + name: "SKNet Monster TV", + audio_clock: 0x00187de7, + tuner_type: TUNER_PHILIPS_NTSC_M, + inputs: {{ + name: name_tv, + vmux: 1, + amux: TV, + tv: 1, + },{ + name: name_comp1, + vmux: 0, + amux: LINE1, + },{ + name: name_svideo, + vmux: 8, + amux: LINE1, + }}, + radio: { + name: name_radio, + amux: LINE2, + }, + }, + [SAA7134_BOARD_MD9717] = { + name: "Tevion MD 9717", + audio_clock: 0x00200000, + tuner_type: TUNER_PHILIPS_PAL, + inputs: {{ + name: name_tv, + vmux: 1, + amux: TV, + tv: 1, + },{ + /* workaround for problems with normal TV sound */ + name: "TV (mono only)", + vmux: 1, + amux: LINE2, + tv: 1, + },{ + name: name_comp1, + vmux: 2, + amux: LINE1, + },{ + name: name_comp2, + vmux: 3, + amux: LINE1, + },{ + name: name_svideo, + vmux: 8, + amux: LINE1, + }}, + radio: { + name: name_radio, + amux: LINE2, + }, + }, + [SAA7134_BOARD_TVSTATION_RDS] = { + name: "KNC One TV-Station RDS", + audio_clock: 0x00200000, + tuner_type: TUNER_PHILIPS_FM1216ME_MK3, + need_tda9887: 1, + inputs: {{ + name: name_tv, + vmux: 1, + amux: TV, + tv: 1, + },{ + name: name_comp1, + vmux: 2, + amux: LINE1, + },{ + name: name_comp2, + vmux: 3, + amux: LINE1, + }}, + radio: { + name: name_radio, + amux: LINE2, + }, + }, + [SAA7134_BOARD_CINERGY400] = { + name: "Terratec Cinergy 400 TV", + audio_clock: 0x00200000, + tuner_type: TUNER_PHILIPS_PAL, + inputs: {{ + name: name_tv, + vmux: 1, + amux: TV, + tv: 1, + },{ + name: name_comp1, + vmux: 4, + amux: LINE1, + },{ + name: name_svideo, + vmux: 8, + amux: LINE1, + },{ + name: name_comp2, // CVideo over SVideo Connector + vmux: 0, + amux: LINE1, + }} + }, + [SAA7134_BOARD_MD5044] = { + name: "Medion 5044", + audio_clock: 0x00200000, + tuner_type: TUNER_PHILIPS_FM1216ME_MK3, + need_tda9887: 1, + inputs: {{ + name: name_tv, + vmux: 1, + amux: TV, + tv: 1, + },{ + name: name_comp1, + vmux: 0, + amux: LINE2, + },{ + name: name_comp2, + vmux: 3, + amux: LINE2, + },{ + name: name_svideo, + vmux: 8, + amux: LINE2, + }}, + radio: { + name: name_radio, + amux: LINE2, + }, + }, +}; +const int saa7134_bcount = (sizeof(saa7134_boards)/sizeof(struct saa7134_board)); + +/* ------------------------------------------------------------------ */ +/* PCI ids + subsystem IDs */ + +struct pci_device_id __devinitdata saa7134_pci_tbl[] = { + { + vendor: PCI_VENDOR_ID_PHILIPS, + device: PCI_DEVICE_ID_PHILIPS_SAA7134, + subvendor: PCI_VENDOR_ID_PHILIPS, + subdevice: 0x2001, + driver_data: SAA7134_BOARD_PROTEUS_PRO, + },{ + vendor: PCI_VENDOR_ID_PHILIPS, + device: PCI_DEVICE_ID_PHILIPS_SAA7134, + subvendor: PCI_VENDOR_ID_PHILIPS, + subdevice: 0x6752, + driver_data: SAA7134_BOARD_EMPRESS, + },{ + vendor: PCI_VENDOR_ID_PHILIPS, + device: PCI_DEVICE_ID_PHILIPS_SAA7134, + subvendor: 0x1131, + subdevice: 0x4e85, + driver_data: SAA7134_BOARD_MONSTERTV, + },{ + vendor: PCI_VENDOR_ID_PHILIPS, + device: PCI_DEVICE_ID_PHILIPS_SAA7134, + subvendor: 0x153B, + subdevice: 0x1142, + driver_data: SAA7134_BOARD_CINERGY400, + },{ + + /* --- boards without eeprom + subsystem ID --- */ + vendor: PCI_VENDOR_ID_PHILIPS, + device: PCI_DEVICE_ID_PHILIPS_SAA7134, + subvendor: PCI_VENDOR_ID_PHILIPS, + subdevice: 0, + driver_data: SAA7134_BOARD_NOAUTO, + },{ + vendor: PCI_VENDOR_ID_PHILIPS, + device: PCI_DEVICE_ID_PHILIPS_SAA7130, + subvendor: PCI_VENDOR_ID_PHILIPS, + subdevice: 0, + driver_data: SAA7134_BOARD_NOAUTO, + },{ + + /* --- default catch --- */ + vendor: PCI_VENDOR_ID_PHILIPS, + device: PCI_DEVICE_ID_PHILIPS_SAA7130, + subvendor: PCI_ANY_ID, + subdevice: PCI_ANY_ID, + driver_data: SAA7134_BOARD_UNKNOWN, + },{ + vendor: PCI_VENDOR_ID_PHILIPS, + device: PCI_DEVICE_ID_PHILIPS_SAA7134, + subvendor: PCI_ANY_ID, + subdevice: PCI_ANY_ID, + driver_data: SAA7134_BOARD_UNKNOWN, + },{ + /* --- end of list --- */ + } +}; +MODULE_DEVICE_TABLE(pci, saa7134_pci_tbl); + +/* ----------------------------------------------------------- */ +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/media/video/saa7134/saa7134-core.c linux-2.5-bk/drivers/media/video/saa7134/saa7134-core.c --- linux-2.5.45/drivers/media/video/saa7134/saa7134-core.c Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/drivers/media/video/saa7134/saa7134-core.c Thu Oct 31 22:12:49 2002 @@ -0,0 +1,928 @@ +/* + * device driver for philips saa7134 based TV cards + * driver core + * + * (c) 2001,02 Gerd Knorr [SuSE Labs] + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "saa7134-reg.h" +#include "saa7134.h" +#include "tuner.h" + +MODULE_DESCRIPTION("v4l2 driver module for saa7130/34 based TV cards"); +MODULE_AUTHOR("Gerd Knorr [SuSE Labs]"); +MODULE_LICENSE("GPL"); + +#define SAA7134_MAXBOARDS 4 + +/* ------------------------------------------------------------------ */ + +static unsigned int irq_debug = 0; +MODULE_PARM(irq_debug,"i"); +MODULE_PARM_DESC(irq_debug,"enable debug messages [IRQ handler]"); + +static unsigned int core_debug = 0; +MODULE_PARM(core_debug,"i"); +MODULE_PARM_DESC(core_debug,"enable debug messages [core]"); + +static unsigned int gpio_tracking = 0; +MODULE_PARM(gpio_tracking,"i"); +MODULE_PARM_DESC(gpio_tracking,"enable debug messages [gpio]"); + +static unsigned int video_nr = -1; +MODULE_PARM(video_nr,"i"); +MODULE_PARM_DESC(video_nr,"video device number"); + +static unsigned int ts_nr = -1; +MODULE_PARM(ts_nr,"i"); +MODULE_PARM_DESC(ts_nr,"ts device number"); + +static unsigned int vbi_nr = -1; +MODULE_PARM(vbi_nr,"i"); +MODULE_PARM_DESC(vbi_nr,"vbi device number"); + +static unsigned int radio_nr = -1; +MODULE_PARM(radio_nr,"i"); +MODULE_PARM_DESC(radio_nr,"radio device number"); + +static unsigned int dsp_nr = -1; +MODULE_PARM(dsp_nr,"i"); +MODULE_PARM_DESC(dsp_nr,"oss dsp device number"); + +static unsigned int mixer_nr = -1; +MODULE_PARM(mixer_nr,"i"); +MODULE_PARM_DESC(mixer_nr,"oss mixer device number"); + +static int tuner[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = -1}; +MODULE_PARM(tuner,"1-" __stringify(SAA7134_MAXBOARDS) "i"); +MODULE_PARM_DESC(tuner,"tuner type"); + +static int card[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = -1}; +MODULE_PARM(card,"1-" __stringify(SAA7134_MAXBOARDS) "i"); +MODULE_PARM_DESC(card,"card type"); + +static int latency = -1; +MODULE_PARM(latency,"i"); +MODULE_PARM_DESC(latency,"pci latency timer"); + +struct list_head saa7134_devlist; +int saa7134_devcount; + +#define dprintk(fmt, arg...) if (core_debug) \ + printk(KERN_DEBUG "%s/core: " fmt, dev->name, ## arg) + +/* ------------------------------------------------------------------ */ +/* debug help functions */ + +static const char *v4l1_ioctls[] = { + "0", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", "GPICT", "SPICT", + "CCAPTURE", "GWIN", "SWIN", "GFBUF", "SFBUF", "KEY", "GFREQ", + "SFREQ", "GAUDIO", "SAUDIO", "SYNC", "MCAPTURE", "GMBUF", "GUNIT", + "GCAPTURE", "SCAPTURE", "SPLAYMODE", "SWRITEMODE", "GPLAYINFO", + "SMICROCODE", "GVBIFMT", "SVBIFMT" }; +#define V4L1_IOCTLS (sizeof(v4l1_ioctls)/sizeof(char*)) + +static const char *v4l2_ioctls[] = { + "QUERYCAP", "1", "ENUM_PIXFMT", "ENUM_FBUFFMT", "G_FMT", "S_FMT", + "G_COMP", "S_COMP", "REQBUFS", "QUERYBUF", "G_FBUF", "S_FBUF", + "G_WIN", "S_WIN", "PREVIEW", "QBUF", "16", "DQBUF", "STREAMON", + "STREAMOFF", "G_PERF", "G_PARM", "S_PARM", "G_STD", "S_STD", + "ENUMSTD", "ENUMINPUT", "G_CTRL", "S_CTRL", "G_TUNER", "S_TUNER", + "G_FREQ", "S_FREQ", "G_AUDIO", "S_AUDIO", "35", "QUERYCTRL", + "QUERYMENU", "G_INPUT", "S_INPUT", "ENUMCVT", "41", "42", "43", + "44", "45", "G_OUTPUT", "S_OUTPUT", "ENUMOUTPUT", "G_AUDOUT", + "S_AUDOUT", "ENUMFX", "G_EFFECT", "S_EFFECT", "G_MODULATOR", + "S_MODULATOR" +}; +#define V4L2_IOCTLS (sizeof(v4l2_ioctls)/sizeof(char*)) + +static const char *osspcm_ioctls[] = { + "RESET", "SYNC", "SPEED", "STEREO", "GETBLKSIZE", "SETFMT", + "CHANNELS", "?", "POST", "SUBDIVIDE", "SETFRAGMENT", "GETFMTS", + "GETOSPACE", "GETISPACE", "NONBLOCK", "GETCAPS", "GET/SETTRIGGER", + "GETIPTR", "GETOPTR", "MAPINBUF", "MAPOUTBUF", "SETSYNCRO", + "SETDUPLEX", "GETODELAY" +}; +#define OSSPCM_IOCTLS (sizeof(v4l2_ioctls)/sizeof(char*)) + +void saa7134_print_ioctl(char *name, unsigned int cmd) +{ + char *dir; + + switch (_IOC_DIR(cmd)) { + case _IOC_NONE: dir = "--"; break; + case _IOC_READ: dir = "r-"; break; + case _IOC_WRITE: dir = "-w"; break; + case _IOC_READ | _IOC_WRITE: dir = "rw"; break; + default: dir = "??"; break; + } + switch (_IOC_TYPE(cmd)) { + case 'v': + printk(KERN_DEBUG "%s: ioctl 0x%08x (v4l1, %s, VIDIOC%s)\n", + name, cmd, dir, (_IOC_NR(cmd) < V4L1_IOCTLS) ? + v4l1_ioctls[_IOC_NR(cmd)] : "???"); + break; + case 'V': + printk(KERN_DEBUG "%s: ioctl 0x%08x (v4l2, %s, VIDIOC_%s)\n", + name, cmd, dir, (_IOC_NR(cmd) < V4L2_IOCTLS) ? + v4l2_ioctls[_IOC_NR(cmd)] : "???"); + break; + case 'P': + printk(KERN_DEBUG "%s: ioctl 0x%08x (oss dsp, %s, SNDCTL_DSP_%s)\n", + name, cmd, dir, (_IOC_NR(cmd) < OSSPCM_IOCTLS) ? + osspcm_ioctls[_IOC_NR(cmd)] : "???"); + break; + case 'M': + printk(KERN_DEBUG "%s: ioctl 0x%08x (oss mixer, %s, #%d)\n", + name, cmd, dir, _IOC_NR(cmd)); + break; + default: + printk(KERN_DEBUG "%s: ioctl 0x%08x (???, %s, #%d)\n", + name, cmd, dir, _IOC_NR(cmd)); + } +} + +void saa7134_track_gpio(struct saa7134_dev *dev, char *msg) +{ + unsigned long mode,status; + + if (!gpio_tracking) + return; + /* rising SAA7134_GPIO_GPRESCAN reads the status */ + saa_andorb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN,0); + saa_andorb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN,SAA7134_GPIO_GPRESCAN); + mode = saa_readl(SAA7134_GPIO_GPMODE0 >> 2) & 0xfffffff; + status = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2) & 0xfffffff; + printk(KERN_DEBUG + "%s: gpio: mode=0x%07lx in=0x%07lx out=0x%07lx [%s]\n", + dev->name, mode, (~mode) & status, mode & status, msg); +} + +/* ------------------------------------------------------------------ */ + +#if 0 +static char *dec1_bits[8] = { + "DCSTD0", "DCSCT1", "WIPA", "GLIMB", + "GLIMT", "SLTCA", "HLCK" +}; +static char *dec2_bits[8] = { + "RDCAP", "COPRO", "COLSTR", "TYPE3", + NULL, "FIDT", "HLVLN", "INTL" +}; +static char *scale1_bits[8] = { + "VID_A", "VBI_A", NULL, NULL, "VID_B", "VBI_B" +}; +static char *scale2_bits[8] = { + "TRERR", "CFERR", "LDERR", "WASRST", + "FIDSCI", "FIDSCO", "D6^D5", "TASK" +}; + +static void dump_statusreg(struct saa7134_dev *dev, int reg, + char *regname, char **bits) +{ + int value,i; + + value = saa_readb(reg); + printk(KERN_DEBUG "%s: %s:", dev->name, regname); + for (i = 7; i >= 0; i--) { + if (NULL == bits[i]) + continue; + printk(" %s=%d", bits[i], (value & (1 << i)) ? 1 : 0); + } + printk("\n"); +} + +static void dump_statusregs(struct saa7134_dev *dev) +{ + dump_statusreg(dev,SAA7134_STATUS_VIDEO1,"dec1",dec1_bits); + dump_statusreg(dev,SAA7134_STATUS_VIDEO2,"dec2",dec2_bits); + dump_statusreg(dev,SAA7134_SCALER_STATUS0,"scale0",scale1_bits); + dump_statusreg(dev,SAA7134_SCALER_STATUS1,"scale1",scale2_bits); +} +#endif + +/* ------------------------------------------------------------------ */ + +/* nr of (saa7134-)pages for the given buffer size */ +int saa7134_buffer_pages(int size) +{ + size = PAGE_ALIGN(size); + size += PAGE_SIZE; /* for non-page-aligned buffers */ + size /= 4096; + return size; +} + +/* calc max # of buffers from size (must not exceed the 4MB virtual + * address space per DMA channel) */ +int saa7134_buffer_count(int size, int count) +{ + int maxcount; + + maxcount = 1024 / saa7134_buffer_pages(size); + if (count > maxcount) + count = maxcount; + return count; +} + +int saa7134_buffer_startpage(struct saa7134_buf *buf) +{ + return saa7134_buffer_pages(buf->vb.bsize) * buf->vb.i; +} + +unsigned long saa7134_buffer_base(struct saa7134_buf *buf) +{ + unsigned long base; + + base = saa7134_buffer_startpage(buf) * 4096; + base += buf->vb.dma.sglist[0].offset; + return base; +} + +/* ------------------------------------------------------------------ */ + +int saa7134_pgtable_alloc(struct pci_dev *pci, struct saa7134_pgtable *pt) +{ + u32 *cpu; + dma_addr_t dma_addr; + + cpu = pci_alloc_consistent(pci, SAA7134_PGTABLE_SIZE, &dma_addr); + if (NULL == cpu) + return -ENOMEM; + pt->size = SAA7134_PGTABLE_SIZE; + pt->cpu = cpu; + pt->dma = dma_addr; + return 0; +} + +int saa7134_pgtable_build(struct pci_dev *pci, struct saa7134_pgtable *pt, + struct scatterlist *list, int length, + int startpage) +{ + u32 *ptr; + int i,p; + + BUG_ON(NULL == pt || NULL == pt->cpu); + + ptr = pt->cpu + startpage; + for (i = 0; i < length; i++, list++) + for (p = 0; p * 4096 < list->length; p++, ptr++) + *ptr = sg_dma_address(list) - list->offset; + return 0; +} + +void saa7134_pgtable_free(struct pci_dev *pci, struct saa7134_pgtable *pt) +{ + if (NULL == pt->cpu) + return; + pci_free_consistent(pci, pt->size, pt->cpu, pt->dma); + pt->cpu = NULL; +} + +/* ------------------------------------------------------------------ */ + +void saa7134_dma_free(struct saa7134_dev *dev,struct saa7134_buf *buf) +{ + if (in_interrupt()) + BUG(); + + videobuf_waiton(&buf->vb,0,0); + videobuf_dma_pci_unmap(dev->pci, &buf->vb.dma); + videobuf_dma_free(&buf->vb.dma); + buf->vb.state = STATE_NEEDS_INIT; +} + +/* ------------------------------------------------------------------ */ + +int saa7134_buffer_queue(struct saa7134_dev *dev, + struct saa7134_dmaqueue *q, + struct saa7134_buf *buf) +{ +#if DEBUG_SPINLOCKS + BUG_ON(!spin_is_locked(&dev->slock)); +#endif + + dprintk("buffer_queue %p\n",buf); + if (NULL == q->curr) { + q->curr = buf; + buf->activate(dev,buf,NULL); + } else { + list_add_tail(&buf->vb.queue,&q->queue); + buf->vb.state = STATE_QUEUED; + } + return 0; +} + +void saa7134_buffer_finish(struct saa7134_dev *dev, + struct saa7134_dmaqueue *q, + int state) +{ +#if DEBUG_SPINLOCKS + BUG_ON(!spin_is_locked(&dev->slock)); +#endif + dprintk("buffer_finish %p\n",q->curr); + + /* finish current buffer */ + q->curr->vb.state = state; + do_gettimeofday(&q->curr->vb.ts); + wake_up(&q->curr->vb.done); + q->curr = NULL; +} + +void saa7134_buffer_next(struct saa7134_dev *dev, + struct saa7134_dmaqueue *q) +{ + struct saa7134_buf *buf,*next = NULL; + +#if DEBUG_SPINLOCKS + BUG_ON(!spin_is_locked(&dev->slock)); +#endif + BUG_ON(NULL != q->curr); + + if (!list_empty(&q->queue)) { + /* activate next one from queue */ + buf = list_entry(q->queue.next,struct saa7134_buf,vb.queue); + dprintk("buffer_next %p [prev=%p/next=%p]\n", + buf,q->queue.prev,q->queue.next); + list_del(&buf->vb.queue); + if (!list_empty(&q->queue)) + next = list_entry(q->queue.next,struct saa7134_buf, + vb.queue); + q->curr = buf; + buf->activate(dev,buf,next); + dprintk("buffer_next #2 prev=%p/next=%p\n", + q->queue.prev,q->queue.next); + } else { + /* nothing to do -- just stop DMA */ + dprintk("buffer_next %p\n",NULL); + saa7134_set_dmabits(dev); + del_timer(&q->timeout); + } +} + +void saa7134_buffer_timeout(unsigned long data) +{ + struct saa7134_dmaqueue *q = (struct saa7134_dmaqueue*)data; + struct saa7134_dev *dev = q->dev; + unsigned long flags; + + spin_lock_irqsave(&dev->slock,flags); + if (q->curr) { + dprintk("timeout on %p\n",q->curr); + saa7134_buffer_finish(dev,q,STATE_ERROR); + } + saa7134_buffer_next(dev,q); + spin_unlock_irqrestore(&dev->slock,flags); +} + +/* ------------------------------------------------------------------ */ + +int saa7134_set_dmabits(struct saa7134_dev *dev) +{ + unsigned long task=0, ctrl=0, irq=0, split = 0; + enum v4l2_field cap = V4L2_FIELD_ANY; + enum v4l2_field ov = V4L2_FIELD_ANY; + +#if DEBUG_SPINLOCKS + BUG_ON(!spin_is_locked(&dev->slock)); +#endif + + /* video capture -- dma 0 + video task A */ + if (dev->video_q.curr) { + task |= 0x01; + ctrl |= SAA7134_MAIN_CTRL_TE0; + irq |= SAA7134_IRQ1_INTE_RA0_1 | + SAA7134_IRQ1_INTE_RA0_0; + cap = dev->video_q.curr->vb.field; + } + + /* video capture -- dma 1+2 (planar modes) */ + if (dev->video_q.curr && + dev->video_q.curr->fmt->planar) { + ctrl |= SAA7134_MAIN_CTRL_TE4 | + SAA7134_MAIN_CTRL_TE5; + } + + /* screen overlay -- dma 0 + video task B */ + if (dev->ovenable) { + task |= 0x10; + ctrl |= SAA7134_MAIN_CTRL_TE1; + ov = dev->ovfield; + } + + /* vbi capture -- dma 0 + vbi task A+B */ + if (dev->vbi_q.curr) { + task |= 0x22; + ctrl |= SAA7134_MAIN_CTRL_TE2 | + SAA7134_MAIN_CTRL_TE3; + irq |= SAA7134_IRQ1_INTE_RA0_7 | + SAA7134_IRQ1_INTE_RA0_6 | + SAA7134_IRQ1_INTE_RA0_5 | + SAA7134_IRQ1_INTE_RA0_4; + } + + /* audio capture -- dma 3 */ + if (dev->oss.recording) { + ctrl |= SAA7134_MAIN_CTRL_TE6; + irq |= SAA7134_IRQ1_INTE_RA3_1 | + SAA7134_IRQ1_INTE_RA3_0; + } + + /* TS capture -- dma 5 */ + if (dev->ts_q.curr) { + ctrl |= SAA7134_MAIN_CTRL_TE5; + irq |= SAA7134_IRQ1_INTE_RA2_3 | + SAA7134_IRQ1_INTE_RA2_2 | + SAA7134_IRQ1_INTE_RA2_1 | + SAA7134_IRQ1_INTE_RA2_0; + } + + /* set task conditions + field handling */ + if (V4L2_FIELD_HAS_BOTH(cap) || V4L2_FIELD_HAS_BOTH(ov) || cap == ov) { + /* default config -- use full frames: + odd A, even A, odd B, even B, repeat */ + saa_writeb(SAA7134_TASK_CONDITIONS(TASK_A), 0x0d); + saa_writeb(SAA7134_TASK_CONDITIONS(TASK_B), 0x0d); + saa_writeb(SAA7134_FIELD_HANDLING(TASK_A), 0x02); + saa_writeb(SAA7134_FIELD_HANDLING(TASK_B), 0x02); + } else { + /* split fields between tasks */ + if (V4L2_FIELD_TOP == cap) { + /* odd A, even B, repeat */ + saa_writeb(SAA7134_TASK_CONDITIONS(TASK_A), 0x0d); + saa_writeb(SAA7134_TASK_CONDITIONS(TASK_B), 0x0e); + } else { + /* odd B, even A, repeat */ + saa_writeb(SAA7134_TASK_CONDITIONS(TASK_A), 0x0e); + saa_writeb(SAA7134_TASK_CONDITIONS(TASK_B), 0x0d); + } + saa_writeb(SAA7134_FIELD_HANDLING(TASK_A), 0x01); + saa_writeb(SAA7134_FIELD_HANDLING(TASK_B), 0x01); + split = 1; + } + + /* irqs */ + saa_writeb(SAA7134_REGION_ENABLE, task); + saa_writel(SAA7134_IRQ1, irq); + saa_andorl(SAA7134_MAIN_CTRL, + SAA7134_MAIN_CTRL_TE0 | + SAA7134_MAIN_CTRL_TE1 | + SAA7134_MAIN_CTRL_TE2 | + SAA7134_MAIN_CTRL_TE3 | + SAA7134_MAIN_CTRL_TE4 | + SAA7134_MAIN_CTRL_TE5 | + SAA7134_MAIN_CTRL_TE6, + ctrl); + dprintk("dmabits: task=0x%02lx ctrl=0x%02lx irq=0x%lx split=%s\n", + task, ctrl, irq, split ? "no" : "yes"); + + return 0; +} + +/* ------------------------------------------------------------------ */ +/* IRQ handler + helpers */ + +static char *irqbits[] = { + "DONE_RA0", "DONE_RA1", "DONE_RA2", "DONE_RA3", + "AR", "PE", "PWR_ON", "RDCAP", "INTL", "FIDT", "MMC", + "TRIG_ERR", "CONF_ERR", "LOAD_ERR" +}; +#define IRQBITS (sizeof(irqbits)/sizeof(char*)) + +static void print_irqstatus(struct saa7134_dev *dev, int loop, + unsigned long report, unsigned long status) +{ + int i; + + printk(KERN_DEBUG "%s/irq[%d,%ld]: r=0x%lx s=0x%02lx", + dev->name,loop,jiffies,report,status); + for (i = 0; i < IRQBITS; i++) { + if (!(report & (1 << i))) + continue; + printk(" %s",irqbits[i]); + } + if (report & SAA7134_IRQ_REPORT_DONE_RA0) { + printk(" | RA0=%s,%s,%s,%ld", + (status & 0x40) ? "vbi" : "video", + (status & 0x20) ? "b" : "a", + (status & 0x10) ? "odd" : "even", + (status & 0x0f)); + } + printk("\n"); +} + +static void saa7134_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + struct saa7134_dev *dev = (struct saa7134_dev*) dev_id; + unsigned long report,status; + int loop; + + for (loop = 0; loop < 10; loop++) { + report = saa_readl(SAA7134_IRQ_REPORT); + status = saa_readl(SAA7134_IRQ_STATUS); + saa_writel(SAA7134_IRQ_REPORT,report); + if (0 == report) { + if (irq_debug > 1) + printk(KERN_DEBUG "%s/irq: no (more) work\n", + dev->name); + return; + } + if (irq_debug) + print_irqstatus(dev,loop,report,status); + +#if 0 + if (report & SAA7134_IRQ_REPORT_CONF_ERR) + dump_statusregs(dev); +#endif + + if (report & SAA7134_IRQ_REPORT_INTL) + saa7134_irq_video_intl(dev); + + if ((report & SAA7134_IRQ_REPORT_DONE_RA0) && + (status & 0x60) == 0) + saa7134_irq_video_done(dev,status); + + if ((report & SAA7134_IRQ_REPORT_DONE_RA0) && + (status & 0x40) == 0x40) + saa7134_irq_vbi_done(dev,status); + + if ((report & SAA7134_IRQ_REPORT_DONE_RA2) && + card_has_ts(dev)) + saa7134_irq_ts_done(dev,status); + + if ((report & SAA7134_IRQ_REPORT_DONE_RA3)) + saa7134_irq_oss_done(dev,status); + + }; + if (10 == loop) { + print_irqstatus(dev,loop,report,status); + printk(KERN_WARNING "%s/irq: looping -- clearing enable bits\n",dev->name); + /* disable all irqs */ + saa_writel(SAA7134_IRQ1,0); + saa_writel(SAA7134_IRQ2,0); + } +} + +/* ------------------------------------------------------------------ */ + +static int saa7134_hwinit(struct saa7134_dev *dev) +{ + init_MUTEX(&dev->lock); + dev->slock = SPIN_LOCK_UNLOCKED; + + saa7134_track_gpio(dev,"pre-init"); + saa7134_tvaudio_init(dev); + saa7134_video_init(dev); + saa7134_vbi_init(dev); + if (card_has_ts(dev)) + saa7134_ts_init(dev); + if (card_has_audio(dev)) + saa7134_oss_init(dev); + + /* RAM FIFO config */ + saa_writel(SAA7134_FIFO_SIZE, 0x08070503); + saa_writel(SAA7134_THRESHOULD,0x02020202); + + /* enable audio + video processing */ + saa_writel(SAA7134_MAIN_CTRL, + SAA7134_MAIN_CTRL_VPLLE | + SAA7134_MAIN_CTRL_APLLE | + SAA7134_MAIN_CTRL_EXOSC | + SAA7134_MAIN_CTRL_EVFE1 | + SAA7134_MAIN_CTRL_EVFE2 | + SAA7134_MAIN_CTRL_ESFE | + SAA7134_MAIN_CTRL_EBADC | + SAA7134_MAIN_CTRL_EBDAC); + + /* IRQ's */ + saa_writel(SAA7134_IRQ1, 0); + saa_writel(SAA7134_IRQ2, + SAA7134_IRQ2_INTE_SC2 | + SAA7134_IRQ2_INTE_SC1 | + SAA7134_IRQ2_INTE_SC0 | + /* SAA7134_IRQ2_INTE_DEC5 | FIXME: TRIG_ERR ??? */ + SAA7134_IRQ2_INTE_DEC3 | + SAA7134_IRQ2_INTE_DEC2 | + /* SAA7134_IRQ2_INTE_DEC1 | */ + SAA7134_IRQ2_INTE_DEC0 | + SAA7134_IRQ2_INTE_PE | + SAA7134_IRQ2_INTE_AR); + + /* enable peripheral devices */ + saa_writeb(SAA7134_SPECIAL_MODE, 0x01); + + return 0; +} + +static void __devinit must_configure_manually(void) +{ + int i,p; + + printk(KERN_WARNING + "saa7134: \n" + "saa7134: Congratulations! Your TV card vendor saved a few\n" + "saa7134: cents for a eeprom, thus your pci board has no\n" + "saa7134: subsystem ID and I can't identify it automatically\n" + "saa7134: \n" + "saa7134: I feel better now. Ok, here are the good news:\n" + "saa7134: You can use the card= insmod option to specify\n" + "saa7134: which board do you have. The list:\n"); + for (i = 0; i < saa7134_bcount; i++) { + printk(KERN_WARNING "saa7134: card=%d -> %-40.40s", + i,saa7134_boards[i].name); + for (p = 0; saa7134_pci_tbl[p].driver_data; p++) { + if (saa7134_pci_tbl[p].driver_data != i) + continue; + printk(" %04x:%04x", + saa7134_pci_tbl[p].subvendor, + saa7134_pci_tbl[p].subdevice); + } + printk("\n"); + } +} + +static int __devinit saa7134_initdev(struct pci_dev *pci_dev, + const struct pci_device_id *pci_id) +{ + struct saa7134_dev *dev; + int err; + + dev = kmalloc(sizeof(*dev),GFP_KERNEL); + if (NULL == dev) + return -ENOMEM; + memset(dev,0,sizeof(*dev)); + + /* pci stuff */ + dev->pci = pci_dev; + if (pci_enable_device(pci_dev)) { + err = -EIO; + goto fail1; + } + sprintf(dev->name,"saa%x[%d]",pci_dev->device,saa7134_devcount); + if (-1 != latency) { + printk(KERN_INFO "%s: setting pci latency timer to %d\n", + dev->name,latency); + pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, latency); + } + pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev); + pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat); + printk(KERN_INFO "%s: found at %s, rev: %d, irq: %d, " + "latency: %d, mmio: 0x%lx\n", dev->name, + pci_dev->slot_name, dev->pci_rev, pci_dev->irq, + dev->pci_lat,pci_resource_start(pci_dev,0)); + pci_set_master(pci_dev); + if (!pci_dma_supported(pci_dev,0xffffffff)) { + printk("%s: Oops: no 32bit PCI DMA ???\n",dev->name); + err = -EIO; + goto fail1; + } + + /* board config */ + dev->board = pci_id->driver_data; + if (card[saa7134_devcount] >= 0 && + card[saa7134_devcount] < saa7134_bcount) + dev->board = card[saa7134_devcount]; + if (SAA7134_BOARD_NOAUTO == dev->board) { + must_configure_manually(); + dev->board = SAA7134_BOARD_UNKNOWN; + } + dev->tuner_type = saa7134_boards[dev->board].tuner_type; + if (-1 != tuner[saa7134_devcount]) + dev->tuner_type = tuner[saa7134_devcount]; + printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n", + dev->name,pci_dev->subsystem_vendor, + pci_dev->subsystem_device,saa7134_boards[dev->board].name, + dev->board, card[saa7134_devcount] == dev->board ? + "insmod option" : "autodetected"); + + /* get mmio */ + if (!request_mem_region(pci_resource_start(pci_dev,0), + pci_resource_len(pci_dev,0), + dev->name)) { + err = -EBUSY; + printk(KERN_ERR "%s: can't get MMIO memory @ 0x%lx\n", + dev->name,pci_resource_start(pci_dev,0)); + goto fail1; + } + dev->lmmio = ioremap(pci_resource_start(pci_dev,0), 0x1000); + dev->bmmio = (__u8*)dev->lmmio; + + /* get irq */ + err = request_irq(pci_dev->irq, saa7134_irq, + SA_SHIRQ | SA_INTERRUPT, dev->name, dev); + if (err < 0) { + printk(KERN_ERR "%s: can't get IRQ %d\n", + dev->name,pci_dev->irq); + goto fail2; + } + + /* initialize hardware */ + saa7134_hwinit(dev); + + /* register i2c bus + load i2c helpers */ + saa7134_i2c_register(dev); + if (TUNER_ABSENT != card(dev).tuner_type) + request_module("tuner"); + if (saa7134_boards[dev->board].need_tda9887) + request_module("tda9887"); + + /* register v4l devices */ + dev->video_dev = saa7134_video_template; + dev->video_dev.priv = dev; + err = video_register_device(&dev->video_dev,VFL_TYPE_GRABBER,video_nr); + if (err < 0) { + printk(KERN_INFO "%s: can't register video device\n", + dev->name); + goto fail3; + } + printk(KERN_INFO "%s: registered device video%d [v4l2]\n", + dev->name,dev->video_dev.minor & 0x1f); + + dev->ts_dev = saa7134_ts_template; + dev->ts_dev.priv = dev; + if (card_has_ts(dev)) { + err = video_register_device(&dev->ts_dev,VFL_TYPE_GRABBER,ts_nr); + if (err < 0) { + printk(KERN_INFO "%s: can't register video device\n", + dev->name); + goto fail4; + } + printk(KERN_INFO "%s: registered device video%d [ts]\n", + dev->name,dev->ts_dev.minor & 0x1f); + } + + dev->vbi_dev = saa7134_vbi_template; + dev->vbi_dev.priv = dev; + err = video_register_device(&dev->vbi_dev,VFL_TYPE_VBI,vbi_nr); + if (err < 0) + goto fail5; + printk(KERN_INFO "%s: registered device vbi%d\n", + dev->name,dev->vbi_dev.minor & 0x1f); + + dev->radio_dev = saa7134_radio_template; + dev->radio_dev.priv = dev; + if (card_has_radio(dev)) { + err = video_register_device(&dev->radio_dev,VFL_TYPE_RADIO,radio_nr); + if (err < 0) + goto fail6; + printk(KERN_INFO "%s: registered device radio%d\n", + dev->name,dev->radio_dev.minor & 0x1f); + } + + /* register oss devices */ + if (card_has_audio(dev)) { + dev->oss.minor_dsp = register_sound_dsp(&saa7134_dsp_fops,dsp_nr); + if (dev->oss.minor_dsp < 0) + goto fail7; + printk(KERN_INFO "%s: registered device dsp%d\n", + dev->name,dev->oss.minor_dsp >> 4); + + dev->oss.minor_mixer = + register_sound_mixer(&saa7134_mixer_fops,mixer_nr); + if (dev->oss.minor_mixer < 0) + goto fail8; + printk(KERN_INFO "%s: registered device mixer%d\n", + dev->name,dev->oss.minor_mixer >> 4); + } + + /* everything worked */ + list_add_tail(&dev->devlist,&saa7134_devlist); + pci_set_drvdata(pci_dev,dev); + saa7134_devcount++; + return 0; + + fail8: + if (card_has_audio(dev)) + unregister_sound_dsp(dev->oss.minor_dsp); + fail7: + if (card_has_radio(dev)) + video_unregister_device(&dev->radio_dev); + fail6: + video_unregister_device(&dev->vbi_dev); + fail5: + if (card_has_ts(dev)) + video_unregister_device(&dev->ts_dev); + fail4: + video_unregister_device(&dev->video_dev); + fail3: + if (card_has_audio(dev)) + saa7134_oss_fini(dev); + if (card_has_ts(dev)) + saa7134_ts_fini(dev); + saa7134_vbi_fini(dev); + saa7134_video_fini(dev); + saa7134_tvaudio_fini(dev); + saa7134_i2c_unregister(dev); + free_irq(pci_dev->irq, dev); + fail2: + release_mem_region(pci_resource_start(pci_dev,0), + pci_resource_len(pci_dev,0)); + fail1: + kfree(dev); + return err; +} + +static void __devexit saa7134_finidev(struct pci_dev *pci_dev) +{ + struct saa7134_dev *dev = pci_get_drvdata(pci_dev); + + /* disable peripheral devices */ + saa_writeb(SAA7134_SPECIAL_MODE,0); + + /* shutdown hardware */ + saa_writel(SAA7134_IRQ1,0); + saa_writel(SAA7134_IRQ2,0); + saa_writel(SAA7134_MAIN_CTRL,0); + + /* shutdown subsystems */ + if (card_has_audio(dev)) + saa7134_oss_fini(dev); + if (card_has_ts(dev)) + saa7134_ts_fini(dev); + saa7134_vbi_fini(dev); + saa7134_video_fini(dev); + saa7134_tvaudio_fini(dev); + saa7134_i2c_unregister(dev); + + /* release ressources */ + free_irq(pci_dev->irq, dev); + release_mem_region(pci_resource_start(pci_dev,0), + pci_resource_len(pci_dev,0)); + + /* unregister */ + saa7134_i2c_unregister(dev); + if (card_has_audio(dev)) { + unregister_sound_mixer(dev->oss.minor_mixer); + unregister_sound_dsp(dev->oss.minor_dsp); + } + if (card_has_radio(dev)) + video_unregister_device(&dev->radio_dev); + video_unregister_device(&dev->vbi_dev); + if (card_has_ts(dev)) + video_unregister_device(&dev->ts_dev); + video_unregister_device(&dev->video_dev); + pci_set_drvdata(pci_dev, NULL); +#if 0 + /* causes some trouble when reinserting the driver ... */ + pci_disable_device(pci_dev); +#endif + + /* free memory */ + list_del(&dev->devlist); + saa7134_devcount--; + kfree(dev); +} + +static struct pci_driver saa7134_pci_driver = { + name: "saa7134", + id_table: saa7134_pci_tbl, + probe: saa7134_initdev, + remove: saa7134_finidev, +}; + +static int saa7134_init(void) +{ + INIT_LIST_HEAD(&saa7134_devlist); + printk(KERN_INFO "saa7130/34: v4l2 driver version %d.%d.%d loaded\n", + (SAA7134_VERSION_CODE >> 16) & 0xff, + (SAA7134_VERSION_CODE >> 8) & 0xff, + SAA7134_VERSION_CODE & 0xff); + return pci_module_init(&saa7134_pci_driver); +} + +static void saa7134_fini(void) +{ + pci_unregister_driver(&saa7134_pci_driver); +} + +module_init(saa7134_init); +module_exit(saa7134_fini); + +/* ----------------------------------------------------------- */ +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/media/video/saa7134/saa7134-i2c.c linux-2.5-bk/drivers/media/video/saa7134/saa7134-i2c.c --- linux-2.5.45/drivers/media/video/saa7134/saa7134-i2c.c Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/drivers/media/video/saa7134/saa7134-i2c.c Thu Oct 31 22:12:49 2002 @@ -0,0 +1,451 @@ +/* + * device driver for philips saa7134 based TV cards + * i2c interface support + * + * (c) 2001,02 Gerd Knorr [SuSE Labs] + * + * 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. + */ + +#define __NO_VERSION__ 1 + +#include +#include +#include +#include +#include +#include + +#include "saa7134-reg.h" +#include "saa7134.h" +#include "tuner.h" +#include "id.h" + +/* ----------------------------------------------------------- */ + +static unsigned int i2c_debug = 0; +MODULE_PARM(i2c_debug,"i"); +MODULE_PARM_DESC(i2c_debug,"enable debug messages [i2c]"); + +static unsigned int i2c_scan = 0; +MODULE_PARM(i2c_scan,"i"); +MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time"); + +#define d1printk if (1 == i2c_debug) printk +#define d2printk if (2 == i2c_debug) printk + +#define I2C_WAIT_DELAY 32 +#define I2C_WAIT_RETRY 16 + +/* ----------------------------------------------------------- */ + +static char *str_i2c_status[] = { + "IDLE", "DONE_STOP", "BUSY", "TO_SCL", "TO_ARB", "DONE_WRITE", + "DONE_READ", "DONE_WRITE_TO", "DONE_READ_TO", "NO_DEVICE", + "NO_ACKN", "BUS_ERR", "ARB_LOST", "SEQ_ERR", "ST_ERR", "SW_ERR" +}; + +enum i2c_status { + IDLE = 0, // no I2C command pending + DONE_STOP = 1, // I2C command done and STOP executed + BUSY = 2, // executing I2C command + TO_SCL = 3, // executing I2C command, time out on clock stretching + TO_ARB = 4, // time out on arbitration trial, still trying + DONE_WRITE = 5, // I2C command done and awaiting next write command + DONE_READ = 6, // I2C command done and awaiting next read command + DONE_WRITE_TO = 7, // see 5, and time out on status echo + DONE_READ_TO = 8, // see 6, and time out on status echo + NO_DEVICE = 9, // no acknowledge on device slave address + NO_ACKN = 10, // no acknowledge after data byte transfer + BUS_ERR = 11, // bus error + ARB_LOST = 12, // arbitration lost during transfer + SEQ_ERR = 13, // erroneous programming sequence + ST_ERR = 14, // wrong status echoing + SW_ERR = 15 // software error +}; + +static char *str_i2c_attr[] = { + "NOP", "STOP", "CONTINUE", "START" +}; + +enum i2c_attr { + NOP = 0, // no operation on I2C bus + STOP = 1, // stop condition, no associated byte transfer + CONTINUE = 2, // continue with byte transfer + START = 3 // start condition with byte transfer +}; + +static inline enum i2c_status i2c_get_status(struct saa7134_dev *dev) +{ + enum i2c_status status; + + status = saa_readb(SAA7134_I2C_ATTR_STATUS) & 0x0f; + d2printk(KERN_DEBUG "%s: i2c stat <= %s\n",dev->name, + str_i2c_status[status]); + return status; +} + +static inline void i2c_set_status(struct saa7134_dev *dev, + enum i2c_status status) +{ + d2printk(KERN_DEBUG "%s: i2c stat => %s\n",dev->name, + str_i2c_status[status]); + saa_andorb(SAA7134_I2C_ATTR_STATUS,0x0f,status); +} + +static inline void i2c_set_attr(struct saa7134_dev *dev, enum i2c_attr attr) +{ + d2printk(KERN_DEBUG "%s: i2c attr => %s\n",dev->name, + str_i2c_attr[attr]); + saa_andorb(SAA7134_I2C_ATTR_STATUS,0xc0,attr << 6); +} + +static inline int i2c_is_error(enum i2c_status status) +{ + switch (status) { + case NO_DEVICE: + case NO_ACKN: + case BUS_ERR: + case ARB_LOST: + case SEQ_ERR: + case ST_ERR: + return TRUE; + default: + return FALSE; + } +} + +static inline int i2c_is_idle(enum i2c_status status) +{ + switch (status) { + case IDLE: + case DONE_STOP: + return TRUE; + default: + return FALSE; + } +} + +static inline int i2c_is_busy(enum i2c_status status) +{ + switch (status) { + case BUSY: + return TRUE; + default: + return FALSE; + } +} + +static int i2c_is_busy_wait(struct saa7134_dev *dev) +{ + enum i2c_status status; + int count; + + for (count = 0; count < I2C_WAIT_RETRY; count++) { + status = i2c_get_status(dev); + if (!i2c_is_busy(status)) + break; + if (need_resched()) + schedule(); + else + udelay(I2C_WAIT_DELAY); + } + if (I2C_WAIT_RETRY == count) + return FALSE; + return TRUE; +} + +static int i2c_reset(struct saa7134_dev *dev) +{ + enum i2c_status status; + int count; + + d2printk(KERN_DEBUG "%s: i2c reset\n",dev->name); + status = i2c_get_status(dev); + if (!i2c_is_error(status)) + return TRUE; + i2c_set_status(dev,status); + + for (count = 0; count < I2C_WAIT_RETRY; count++) { + status = i2c_get_status(dev); + if (!i2c_is_error(status)) + break; + udelay(I2C_WAIT_DELAY); + } + if (I2C_WAIT_RETRY == count) + return FALSE; + + if (!i2c_is_idle(status)) + return FALSE; + + i2c_set_attr(dev,NOP); + return TRUE; +} + +static inline int i2c_send_byte(struct saa7134_dev *dev, + enum i2c_attr attr, + unsigned char data) +{ + enum i2c_status status; + __u32 dword; + +#if 0 + i2c_set_attr(dev,attr); + saa_writeb(SAA7134_I2C_DATA, data); +#else + /* have to write both attr + data in one 32bit word */ + dword = saa_readl(SAA7134_I2C_ATTR_STATUS >> 2); + dword &= 0x0f; + dword |= (attr << 6); + dword |= ((__u32)data << 8); + dword |= 0x00 << 16; + dword |= 0xf0 << 24; + saa_writel(SAA7134_I2C_ATTR_STATUS >> 2, dword); +#endif + d2printk(KERN_DEBUG "%s: i2c data => 0x%x\n",dev->name,data); + + if (!i2c_is_busy_wait(dev)) + return -EIO; + status = i2c_get_status(dev); + if (i2c_is_error(status)) + return -EIO; + return 0; +} + +static inline int i2c_recv_byte(struct saa7134_dev *dev) +{ + enum i2c_status status; + unsigned char data; + + i2c_set_attr(dev,CONTINUE); + if (!i2c_is_busy_wait(dev)) + return -EIO; + status = i2c_get_status(dev); + if (i2c_is_error(status)) + return -EIO; + data = saa_readb(SAA7134_I2C_DATA); + d2printk(KERN_DEBUG "%s: i2c data <= 0x%x\n",dev->name,data); + return data; +} + +static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], int num) +{ + struct saa7134_dev *dev = i2c_adap->algo_data; + enum i2c_status status; + unsigned char data; + int addr,rc,i,byte; + + status = i2c_get_status(dev); + if (!i2c_is_idle(status)) + if (!i2c_reset(dev)) + return -EIO; + + d1printk(KERN_DEBUG "%s: i2c xfer:",dev->name); + for (i = 0; i < num; i++) { + if (!(msgs[i].flags & I2C_M_NOSTART) || 0 == i) { + /* send address */ + addr = msgs[i].addr << 1; + if (msgs[i].flags & I2C_M_RD) + addr |= 1; + d1printk(" < %02x", addr); + rc = i2c_send_byte(dev,START,addr); + if (rc < 0) + goto err; + } + if (msgs[i].flags & I2C_M_RD) { + /* read bytes */ + for (byte = 0; byte < msgs[i].len; byte++) { + d1printk(" ="); + rc = i2c_recv_byte(dev); + if (rc < 0) + goto err; + d1printk("%02x", rc); + msgs[i].buf[byte] = rc; + } + } else { + /* write bytes */ + for (byte = 0; byte < msgs[i].len; byte++) { + data = msgs[i].buf[byte]; + d1printk(" %02x", data); + rc = i2c_send_byte(dev,CONTINUE,data); + if (rc < 0) + goto err; + } + } + } + d1printk(" >"); + i2c_set_attr(dev,STOP); + rc = -EIO; + if (!i2c_is_busy_wait(dev)) + goto err; + status = i2c_get_status(dev); + if (i2c_is_error(status)) + goto err; + + d1printk("\n"); + return num; + err: + if (1 == i2c_debug) { + status = i2c_get_status(dev); + printk(" ERROR: %s\n",str_i2c_status[status]); + } + return rc; +} + +/* ----------------------------------------------------------- */ + +static int algo_control(struct i2c_adapter *adapter, + unsigned int cmd, unsigned long arg) +{ + return 0; +} + +static u32 functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL; +} + +static void inc_use(struct i2c_adapter *adap) +{ + MOD_INC_USE_COUNT; +} + +static void dec_use(struct i2c_adapter *adap) +{ + MOD_DEC_USE_COUNT; +} + +static int attach_inform(struct i2c_client *client) +{ + struct saa7134_dev *dev = client->adapter->algo_data; + int tuner = card(dev).tuner_type; + + saa7134_i2c_call_clients(dev,TUNER_SET_TYPE,&tuner); + return 0; +} + +static struct i2c_algorithm saa7134_algo = { + name: "saa7134", + id: I2C_ALGO_SAA7134, + master_xfer: saa7134_i2c_xfer, + algo_control: algo_control, + functionality: functionality, +}; + +static struct i2c_adapter saa7134_adap_template = { + name: "saa7134", + id: I2C_ALGO_SAA7134, + algo: &saa7134_algo, + inc_use: inc_use, + dec_use: dec_use, + client_register: attach_inform, +}; + +static struct i2c_client saa7134_client_template = { + name: "saa7134 internal", + id: -1, +}; + +/* ----------------------------------------------------------- */ + +static int +saa7134_i2c_eeprom(struct saa7134_dev *dev, unsigned char *eedata, int len) +{ + unsigned char buf; + int i,err; + + dev->i2c_client.addr = 0xa0 >> 1; + buf = 0; + if (1 != (err = i2c_master_send(&dev->i2c_client,&buf,1))) { + printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n", + dev->name,err); + return -1; + } + if (len != (err = i2c_master_recv(&dev->i2c_client,eedata,len))) { + printk(KERN_WARNING "%s: i2c eeprom read error (err=%d)\n", + dev->name,err); + return -1; + } + for (i = 0; i < len; i++) { + if (0 == (i % 16)) + printk(KERN_INFO "%s: i2c eeprom %02x:",dev->name,i); + printk(" %02x",eedata[i]); + if (15 == (i % 16)) + printk("\n"); + } + return 0; +} + +static int +saa7134_i2c_scan(struct saa7134_dev *dev) +{ + unsigned char buf; + int i,rc; + + for (i = 0; i < 256; i+= 2) { + dev->i2c_client.addr = i >> 1; + rc = i2c_master_recv(&dev->i2c_client,&buf,0); + if (rc < 0) + continue; + printk("%s: i2c scan: found device @ %x%s\n", + dev->name, i, (i == 0xa0) ? " [eeprom]" : ""); + } + return 0; +} + +void saa7134_i2c_call_clients(struct saa7134_dev *dev, + unsigned int cmd, void *arg) +{ + int i; + + for (i = 0; i < I2C_CLIENT_MAX; i++) { + if (NULL == dev->i2c_adap.clients[i]) + continue; + if (NULL == dev->i2c_adap.clients[i]->driver->command) + continue; + dev->i2c_adap.clients[i]->driver->command + (dev->i2c_adap.clients[i],cmd,arg); + } +} + +int saa7134_i2c_register(struct saa7134_dev *dev) +{ + dev->i2c_adap = saa7134_adap_template; + strcpy(dev->i2c_adap.name,dev->name); + dev->i2c_adap.algo_data = dev; + i2c_add_adapter(&dev->i2c_adap); + + dev->i2c_client = saa7134_client_template; + dev->i2c_client.adapter = &dev->i2c_adap; + + saa7134_i2c_eeprom(dev,dev->eedata,sizeof(dev->eedata)); + if (i2c_scan) + saa7134_i2c_scan(dev); + return 0; +} + +int saa7134_i2c_unregister(struct saa7134_dev *dev) +{ + i2c_del_adapter(&dev->i2c_adap); + return 0; +} + +/* ----------------------------------------------------------- */ +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/media/video/saa7134/saa7134-oss.c linux-2.5-bk/drivers/media/video/saa7134/saa7134-oss.c --- linux-2.5.45/drivers/media/video/saa7134/saa7134-oss.c Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/drivers/media/video/saa7134/saa7134-oss.c Thu Oct 31 22:12:49 2002 @@ -0,0 +1,762 @@ +/* + * device driver for philips saa7134 based TV cards + * oss dsp interface + * + * (c) 2001,02 Gerd Knorr [SuSE Labs] + * + * 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. + */ + +#define __NO_VERSION__ 1 + +#include +#include +#include +#include +#include +#include + +#include "saa7134-reg.h" +#include "saa7134.h" + +/* ------------------------------------------------------------------ */ + +static unsigned int oss_debug = 0; +MODULE_PARM(oss_debug,"i"); +MODULE_PARM_DESC(oss_debug,"enable debug messages [oss]"); + +static unsigned int oss_rate = 0; +MODULE_PARM(oss_rate,"i"); +MODULE_PARM_DESC(oss_rate,"sample rate (valid are: 32000,48000)"); + +#define dprintk(fmt, arg...) if (oss_debug) \ + printk(KERN_DEBUG "%s/oss: " fmt, dev->name, ## arg) + +/* ------------------------------------------------------------------ */ + +static int dsp_buffer_conf(struct saa7134_dev *dev, int blksize, int blocks) +{ + blksize &= ~0xff; + if (blksize < 0x100) + blksize = 0x100; + if (blksize > 0x10000) + blksize = 0x100; + + if (blocks < 2) + blocks = 2; + while ((blksize * blocks) & ~PAGE_MASK) + blocks++; + if ((blksize * blocks) > 1024*1024) + blocks = 1024*1024 / blksize; + + dev->oss.blocks = blocks; + dev->oss.blksize = blksize; + dev->oss.bufsize = blksize * blocks; + + dprintk("buffer config: %d blocks / %d bytes, %d kB total\n", + blocks,blksize,blksize * blocks / 1024); + return 0; +} + +static int dsp_buffer_init(struct saa7134_dev *dev) +{ + int err; + + if (!dev->oss.bufsize) + BUG(); + err = videobuf_dma_init_kernel(&dev->oss.dma, PCI_DMA_FROMDEVICE, + dev->oss.bufsize >> PAGE_SHIFT); + if (0 != err) + return err; + return 0; +} + +static int dsp_buffer_free(struct saa7134_dev *dev) +{ + if (!dev->oss.blksize) + BUG(); + videobuf_dma_free(&dev->oss.dma); + dev->oss.blocks = 0; + dev->oss.blksize = 0; + dev->oss.bufsize = 0; + return 0; +} + +static int dsp_rec_start(struct saa7134_dev *dev) +{ + int err, fmt, bswap, wswap; + unsigned long control,flags; + + /* prepare buffer */ + if (0 != (err = videobuf_dma_pci_map(dev->pci,&dev->oss.dma))) + return err; + if (0 != (err = saa7134_pgtable_build(dev->pci,&dev->oss.pt, + dev->oss.dma.sglist, + dev->oss.dma.sglen, + 0))) + goto fail1; + + /* sample format */ + switch (dev->oss.afmt) { + case AFMT_U8: fmt = 0x00; break; + case AFMT_S8: fmt = 0x00 | 0x04; break; + case AFMT_U16_LE: + case AFMT_U16_BE: fmt = 0x01; break; + case AFMT_S16_LE: + case AFMT_S16_BE: fmt = 0x01 | 0x04; break; +/* 4front API specs mention these ones, + the (2.4.15) kernel header hasn't them ... */ +#ifdef AFMT_S32_LE + case AFMT_S32_LE: + case AFMT_S32_BE: fmt = 0x02 | 0x04; break; +#endif + default: + err = -EINVAL; + goto fail2; + } + + switch (dev->oss.afmt) { + case AFMT_U16_BE: + case AFMT_S16_BE: bswap = 1; wswap = 0; break; +#ifdef AFMT_S32_LE + case AFMT_S32_BE: bswap = 1; wswap = 1; break; +#endif + default: bswap = 0; wswap = 0; break; + } + + if (1 == dev->oss.channels) + fmt |= (1 << 3); + if (2 == dev->oss.channels) + fmt |= (3 << 3); + fmt |= (TV == dev->oss.input) ? 0xc0 : 0x80; + + saa_writeb(SAA7134_NUM_SAMPLES0, (dev->oss.blksize & 0x0000ff)); + saa_writeb(SAA7134_NUM_SAMPLES1, (dev->oss.blksize & 0x00ff00) >> 8); + saa_writeb(SAA7134_NUM_SAMPLES2, (dev->oss.blksize & 0xff0000) >> 16); + saa_writeb(SAA7134_AUDIO_FORMAT_CTRL, fmt); + dprintk("rec_start: afmt=%d ch=%d => fmt=0x%x swap=%c%c\n", + dev->oss.afmt, dev->oss.channels, fmt, + bswap ? 'b' : '-', wswap ? 'w' : '-'); + + /* dma: setup channel 6 (= AUDIO) */ + control = SAA7134_RS_CONTROL_BURST_16 | + SAA7134_RS_CONTROL_ME | + (dev->oss.pt.dma >> 12); + if (bswap) + control |= SAA7134_RS_CONTROL_BSWAP; + if (wswap) + control |= SAA7134_RS_CONTROL_WSWAP; + saa_writel(SAA7134_RS_BA1(6),0); + saa_writel(SAA7134_RS_BA2(6),dev->oss.blksize); + saa_writel(SAA7134_RS_PITCH(6),0); + saa_writel(SAA7134_RS_CONTROL(6),control); + + /* start dma */ + spin_lock_irqsave(&dev->slock,flags); + dev->oss.recording = 1; + dev->oss.dma_blk = 0; + saa7134_set_dmabits(dev); + spin_unlock_irqrestore(&dev->slock,flags); + return 0; + + fail2: + saa7134_pgtable_free(dev->pci,&dev->oss.pt); + fail1: + videobuf_dma_pci_unmap(dev->pci,&dev->oss.dma); + return err; +} + +static int dsp_rec_stop(struct saa7134_dev *dev) +{ + unsigned long flags; + + dprintk("rec_stop dma_blk=%d\n",dev->oss.dma_blk); + + /* stop dma */ + spin_lock_irqsave(&dev->slock,flags); + dev->oss.dma_blk = -1; + dev->oss.recording = 0; + saa7134_set_dmabits(dev); + spin_unlock_irqrestore(&dev->slock,flags); + + /* unlock buffer */ + saa7134_pgtable_free(dev->pci,&dev->oss.pt); + videobuf_dma_pci_unmap(dev->pci,&dev->oss.dma); + return 0; +} + +/* ------------------------------------------------------------------ */ + +static int dsp_open(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + struct saa7134_dev *h,*dev = NULL; + struct list_head *list; + int err; + + list_for_each(list,&saa7134_devlist) { + h = list_entry(list, struct saa7134_dev, devlist); + if (h->oss.minor_dsp == minor) + dev = h; + } + if (NULL == dev) + return -ENODEV; + + down(&dev->oss.lock); + err = -EBUSY; + if (dev->oss.users_dsp) + goto fail1; + dev->oss.users_dsp++; + file->private_data = dev; + + dev->oss.afmt = AFMT_U8; + dev->oss.channels = 1; + dev->oss.read_count = 0; + dev->oss.read_offset = 0; + dsp_buffer_conf(dev,PAGE_SIZE,64); + err = dsp_buffer_init(dev); + if (0 != err) + goto fail2; + + up(&dev->oss.lock); + return 0; + + fail2: + dev->oss.users_dsp--; + fail1: + up(&dev->oss.lock); + return err; +} + +static int dsp_release(struct inode *inode, struct file *file) +{ + struct saa7134_dev *dev = file->private_data; + + down(&dev->oss.lock); + if (dev->oss.recording) + dsp_rec_stop(dev); + dsp_buffer_free(dev); + dev->oss.users_dsp--; + file->private_data = NULL; + up(&dev->oss.lock); + return 0; +} + +static ssize_t dsp_read(struct file *file, char *buffer, + size_t count, loff_t *ppos) +{ + struct saa7134_dev *dev = file->private_data; + DECLARE_WAITQUEUE(wait, current); + int bytes,err,ret = 0; + + add_wait_queue(&dev->oss.wq, &wait); + down(&dev->oss.lock); + while (count > 0) { + /* wait for data if needed */ + if (0 == dev->oss.read_count) { + if (!dev->oss.recording) { + err = dsp_rec_start(dev); + if (err < 0) { + if (0 == ret) + ret = err; + break; + } + } + if (file->f_flags & O_NONBLOCK) { + if (0 == ret) + ret = -EAGAIN; + break; + } + up(&dev->oss.lock); + current->state = TASK_INTERRUPTIBLE; + schedule(); + down(&dev->oss.lock); + if (signal_pending(current)) { + if (0 == ret) + ret = -EINTR; + break; + } + } + + /* copy data to userspace */ + bytes = count; + if (bytes > dev->oss.read_count) + bytes = dev->oss.read_count; + if (bytes > dev->oss.bufsize - dev->oss.read_offset) + bytes = dev->oss.bufsize - dev->oss.read_offset; + if (copy_to_user(buffer + ret, + dev->oss.dma.vmalloc + dev->oss.read_offset, + bytes)) { + if (0 == ret) + ret = -EFAULT; + break; + } + + ret += bytes; + count -= bytes; + dev->oss.read_count -= bytes; + dev->oss.read_offset += bytes; + if (dev->oss.read_offset == dev->oss.bufsize) + dev->oss.read_offset = 0; + } + up(&dev->oss.lock); + remove_wait_queue(&dev->oss.wq, &wait); + current->state = TASK_RUNNING; + return ret; +} + +static ssize_t dsp_write(struct file *file, const char *buffer, + size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +static int dsp_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct saa7134_dev *dev = file->private_data; + int val = 0; + + if (oss_debug > 1) + saa7134_print_ioctl(dev->name,cmd); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + case SNDCTL_DSP_GETCAPS: + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int*)arg)) + return -EFAULT; + /* fall through */ + case SOUND_PCM_READ_RATE: + return put_user(dev->oss.rate, (int*)arg); + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int*)arg)) + return -EFAULT; + down(&dev->oss.lock); + dev->oss.channels = val ? 2 : 1; + if (dev->oss.recording) { + dsp_rec_stop(dev); + dsp_rec_start(dev); + } + up(&dev->oss.lock); + return put_user(dev->oss.channels-1, (int *)arg); + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int*)arg)) + return -EFAULT; + if (val != 1 && val != 2) + return -EINVAL; + down(&dev->oss.lock); + dev->oss.channels = val; + if (dev->oss.recording) { + dsp_rec_stop(dev); + dsp_rec_start(dev); + } + up(&dev->oss.lock); + /* fall through */ + case SOUND_PCM_READ_CHANNELS: + return put_user(dev->oss.channels, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_U8 | AFMT_S8 | + AFMT_U16_LE | AFMT_U16_BE | + AFMT_S16_LE | AFMT_S16_BE, (int*)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt */ + if (get_user(val, (int*)arg)) + return -EFAULT; + switch (val) { + case AFMT_QUERY: + /* nothing to do */ + break; + case AFMT_U8: + case AFMT_S8: + case AFMT_U16_LE: + case AFMT_U16_BE: + case AFMT_S16_LE: + case AFMT_S16_BE: +#ifdef AFMT_S32_LE + case AFMT_S32_LE: + case AFMT_S32_BE: +#endif + down(&dev->oss.lock); + dev->oss.afmt = val; + if (dev->oss.recording) { + dsp_rec_stop(dev); + dsp_rec_start(dev); + } + up(&dev->oss.lock); + return put_user(dev->oss.afmt,(int*)arg); + default: + return -EINVAL; + } + + case SOUND_PCM_READ_BITS: + switch (dev->oss.afmt) { + case AFMT_U8: + case AFMT_S8: + return put_user(8, (int*)arg); + case AFMT_U16_LE: + case AFMT_U16_BE: + case AFMT_S16_LE: + case AFMT_S16_BE: + return put_user(16, (int*)arg); +#ifdef AFMT_S32_LE + case AFMT_S32_LE: + case AFMT_S32_BE: + return put_user(20, (int*)arg); +#endif + default: + return -EINVAL; + } + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_RESET: + down(&dev->oss.lock); + if (dev->oss.recording) + dsp_rec_stop(dev); + up(&dev->oss.lock); + return 0; + case SNDCTL_DSP_GETBLKSIZE: + return put_user(dev->oss.blksize,(int*)arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int*)arg)) + return -EFAULT; + if (dev->oss.recording) + return -EBUSY; + dsp_buffer_free(dev); + dsp_buffer_conf(dev,1 << (val & 0xffff), (arg >> 16) & 0xffff); + dsp_buffer_init(dev); + return 0; + + case SNDCTL_DSP_SYNC: + /* NOP */ + return 0; + + case SNDCTL_DSP_GETISPACE: + { + audio_buf_info info; + info.fragsize = dev->oss.blksize; + info.fragstotal = dev->oss.blocks; + info.bytes = dev->oss.read_count; + info.fragments = info.bytes / info.fragsize; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + default: + return -EINVAL; + } +} + +static unsigned int dsp_poll(struct file *file, struct poll_table_struct *wait) +{ + struct saa7134_dev *dev = file->private_data; + unsigned int mask = 0; + + poll_wait(file, &dev->oss.wq, wait); + + if (0 == dev->oss.read_count) { + down(&dev->oss.lock); + if (!dev->oss.recording) + dsp_rec_start(dev); + up(&dev->oss.lock); + } else + mask |= (POLLIN | POLLRDNORM); + return mask; +} + +struct file_operations saa7134_dsp_fops = { + owner: THIS_MODULE, + open: dsp_open, + release: dsp_release, + read: dsp_read, + write: dsp_write, + ioctl: dsp_ioctl, + poll: dsp_poll, + llseek: no_llseek, +}; + +/* ------------------------------------------------------------------ */ + +static int +mixer_recsrc(struct saa7134_dev *dev, enum saa7134_audio_in src) +{ + static const char *iname[] = { "Oops", "TV", "LINE1", "LINE2" }; + int analog_io,rate; + + dev->oss.count++; + dev->oss.input = src; + dprintk("mixer input = %s\n",iname[dev->oss.input]); + switch (dev->oss.input) { + case TV: + saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, 0xc0, 0xc0); + saa_andorb(SAA7134_SIF_SAMPLE_FREQ, 0x03, 0x00); + break; + case LINE1: + case LINE2: + analog_io = (LINE1 == dev->oss.input) ? 0x00 : 0x08; + rate = (32000 == dev->oss.rate) ? 0x01 : 0x03; + saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x08, analog_io); + saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, 0xc0, 0x80); + saa_andorb(SAA7134_SIF_SAMPLE_FREQ, 0x03, rate); + break; + } + return 0; +} + +static int +mixer_level(struct saa7134_dev *dev, enum saa7134_audio_in src, int level) +{ + switch (src) { + case TV: + /* nothing */ + break; + case LINE1: + saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x10, + (100 == level) ? 0x00 : 0x10); + break; + case LINE2: + saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x20, + (100 == level) ? 0x00 : 0x20); + break; + } + return 0; +} + +static int mixer_open(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + struct saa7134_dev *h,*dev = NULL; + struct list_head *list; + + list_for_each(list,&saa7134_devlist) { + h = list_entry(list, struct saa7134_dev, devlist); + if (h->oss.minor_mixer == minor) + dev = h; + } + if (NULL == dev) + return -ENODEV; + + file->private_data = dev; + return 0; +} + +static int mixer_release(struct inode *inode, struct file *file) +{ + file->private_data = NULL; + return 0; +} + +static int mixer_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct saa7134_dev *dev = file->private_data; + enum saa7134_audio_in input; + int val,ret; + + if (oss_debug > 1) + saa7134_print_ioctl(dev->name,cmd); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + case SOUND_MIXER_INFO: + { + mixer_info info; + memset(&info,0,sizeof(info)); + strncpy(info.id, "TV audio", sizeof(info.id)-1); + strncpy(info.name, dev->name, sizeof(info.name)-1); + info.modify_counter = dev->oss.count; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + case SOUND_OLD_MIXER_INFO: + { + _old_mixer_info info; + memset(&info,0,sizeof(info)); + strncpy(info.id, "TV audio", sizeof(info.id)-1); + strncpy(info.name, dev->name, sizeof(info.name)-1); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + case MIXER_READ(SOUND_MIXER_CAPS): + return put_user(SOUND_CAP_EXCL_INPUT,(int*)arg); + case MIXER_READ(SOUND_MIXER_STEREODEVS): + return put_user(0,(int*)arg); + case MIXER_READ(SOUND_MIXER_RECMASK): + case MIXER_READ(SOUND_MIXER_DEVMASK): + val = SOUND_MASK_LINE1 | SOUND_MASK_LINE2; + if (32000 == dev->oss.rate) + val |= SOUND_MASK_VIDEO; + return put_user(val,(int*)arg); + + case MIXER_WRITE(SOUND_MIXER_RECSRC): + if (get_user(val, (int *)arg)) + return -EFAULT; + input = dev->oss.input; + if (32000 == dev->oss.rate && + val & SOUND_MASK_VIDEO && dev->oss.input != TV) + input = TV; + if (val & SOUND_MASK_LINE1 && dev->oss.input != LINE1) + input = LINE1; + if (val & SOUND_MASK_LINE2 && dev->oss.input != LINE2) + input = LINE2; + if (input != dev->oss.input) + mixer_recsrc(dev,input); + /* fall throuth */ + case MIXER_READ(SOUND_MIXER_RECSRC): + switch (dev->oss.input) { + case TV: ret = SOUND_MASK_VIDEO; break; + case LINE1: ret = SOUND_MASK_LINE1; break; + case LINE2: ret = SOUND_MASK_LINE2; break; + default: ret = 0; + } + return put_user(ret,(int*)arg); + + case MIXER_WRITE(SOUND_MIXER_VIDEO): + case MIXER_READ(SOUND_MIXER_VIDEO): + if (32000 != dev->oss.rate) + return -EINVAL; + return put_user(100 | 100 << 8,(int*)arg); + + case MIXER_WRITE(SOUND_MIXER_LINE1): + if (get_user(val, (int *)arg)) + return -EFAULT; + val &= 0xff; + val = (val <= 50) ? 50 : 100; + dev->oss.line1 = val; + mixer_level(dev,LINE1,dev->oss.line1); + /* fall throuth */ + case MIXER_READ(SOUND_MIXER_LINE1): + return put_user(dev->oss.line1 | dev->oss.line1 << 8, + (int*)arg); + + case MIXER_WRITE(SOUND_MIXER_LINE2): + if (get_user(val, (int *)arg)) + return -EFAULT; + val &= 0xff; + val = (val <= 50) ? 50 : 100; + dev->oss.line2 = val; + mixer_level(dev,LINE2,dev->oss.line2); + /* fall throuth */ + case MIXER_READ(SOUND_MIXER_LINE2): + return put_user(dev->oss.line2 | dev->oss.line2 << 8, + (int*)arg); + + default: + return -EINVAL; + } +} + +struct file_operations saa7134_mixer_fops = { + owner: THIS_MODULE, + open: mixer_open, + release: mixer_release, + ioctl: mixer_ioctl, + llseek: no_llseek, +}; + +/* ------------------------------------------------------------------ */ + +int saa7134_oss_init(struct saa7134_dev *dev) +{ + init_MUTEX(&dev->oss.lock); + init_waitqueue_head(&dev->oss.wq); + dev->oss.line1 = 50; + dev->oss.line2 = 50; + mixer_level(dev,LINE1,dev->oss.line1); + mixer_level(dev,LINE2,dev->oss.line2); + + dev->oss.rate = 32000; + if (oss_rate) + dev->oss.rate = oss_rate; + if (saa7134_boards[dev->board].i2s_rate) + dev->oss.rate = saa7134_boards[dev->board].i2s_rate; + dev->oss.rate = (dev->oss.rate > 40000) ? 48000 : 32000; + + mixer_recsrc(dev, (dev->oss.rate == 32000) ? TV : LINE2); + return 0; +} + +int saa7134_oss_fini(struct saa7134_dev *dev) +{ + /* nothing */ + return 0; +} + +void saa7134_irq_oss_done(struct saa7134_dev *dev, unsigned long status) +{ + int next_blk, reg = 0; + + spin_lock(&dev->slock); + if (-1 == dev->oss.dma_blk) { + dprintk("irq: recording stopped%s\n",""); + goto done; + } + if (0 != (status & 0x0f000000)) + dprintk("irq: lost %ld\n", (status >> 24) & 0x0f); + if (0 == (status & 0x10000000)) { + /* odd */ + if (0 == (dev->oss.dma_blk & 0x01)) + reg = SAA7134_RS_BA1(6); + } else { + /* even */ + if (0 == (dev->oss.dma_blk & 0x00)) + reg = SAA7134_RS_BA2(6); + } + if (0 == reg) { + dprintk("irq: field oops [%s]\n", + (status & 0x10000000) ? "even" : "odd"); + goto done; + } + if (dev->oss.read_count >= dev->oss.blksize * (dev->oss.blocks-2)) { + dprintk("irq: overrun [full=%d/%d]\n",dev->oss.read_count, + dev->oss.bufsize); + dsp_rec_stop(dev); + goto done; + } + + /* next block addr */ + next_blk = (dev->oss.dma_blk + 2) % dev->oss.blocks; + saa_writel(reg,next_blk * dev->oss.blksize); + if (oss_debug > 2) + dprintk("irq: ok, %s, next_blk=%d, addr=%x\n", + (status & 0x10000000) ? "even" : "odd ", next_blk, + next_blk * dev->oss.blksize); + + /* update status & wake waiting readers */ + dev->oss.dma_blk = (dev->oss.dma_blk + 1) % dev->oss.blocks; + dev->oss.read_count += dev->oss.blksize; + wake_up(&dev->oss.wq); + + done: + spin_unlock(&dev->slock); +} + +/* ----------------------------------------------------------- */ +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/media/video/saa7134/saa7134-reg.h linux-2.5-bk/drivers/media/video/saa7134/saa7134-reg.h --- linux-2.5.45/drivers/media/video/saa7134/saa7134-reg.h Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/drivers/media/video/saa7134/saa7134-reg.h Thu Oct 31 22:12:49 2002 @@ -0,0 +1,338 @@ +/* + * philips saa7134 registers + */ + +/* ------------------------------------------------------------------ */ +/* + * PCI ID's + */ +#ifndef PCI_DEVICE_ID_PHILIPS_SAA7130 +# define PCI_DEVICE_ID_PHILIPS_SAA7130 0x7130 +#endif +#ifndef PCI_DEVICE_ID_PHILIPS_SAA7134 +# define PCI_DEVICE_ID_PHILIPS_SAA7134 0x7134 +#endif + +/* ------------------------------------------------------------------ */ +/* + * registers -- 32 bit + */ + +/* DMA channels, n = 0 ... 6 */ +#define SAA7134_RS_BA1(n) ((0x200 >> 2) + 4*n) +#define SAA7134_RS_BA2(n) ((0x204 >> 2) + 4*n) +#define SAA7134_RS_PITCH(n) ((0x208 >> 2) + 4*n) +#define SAA7134_RS_CONTROL(n) ((0x20c >> 2) + 4*n) +#define SAA7134_RS_CONTROL_WSWAP (0x01 << 25) +#define SAA7134_RS_CONTROL_BSWAP (0x01 << 24) +#define SAA7134_RS_CONTROL_BURST_2 (0x01 << 21) +#define SAA7134_RS_CONTROL_BURST_4 (0x02 << 21) +#define SAA7134_RS_CONTROL_BURST_8 (0x03 << 21) +#define SAA7134_RS_CONTROL_BURST_16 (0x04 << 21) +#define SAA7134_RS_CONTROL_BURST_32 (0x05 << 21) +#define SAA7134_RS_CONTROL_BURST_64 (0x06 << 21) +#define SAA7134_RS_CONTROL_BURST_MAX (0x07 << 21) +#define SAA7134_RS_CONTROL_ME (0x01 << 20) +#define SAA7134_FIFO_SIZE (0x2a0 >> 2) +#define SAA7134_THRESHOULD (0x2a4 >> 2) + +/* main control */ +#define SAA7134_MAIN_CTRL (0x2a8 >> 2) +#define SAA7134_MAIN_CTRL_VPLLE (1 << 15) +#define SAA7134_MAIN_CTRL_APLLE (1 << 14) +#define SAA7134_MAIN_CTRL_EXOSC (1 << 13) +#define SAA7134_MAIN_CTRL_EVFE1 (1 << 12) +#define SAA7134_MAIN_CTRL_EVFE2 (1 << 11) +#define SAA7134_MAIN_CTRL_ESFE (1 << 10) +#define SAA7134_MAIN_CTRL_EBADC (1 << 9) +#define SAA7134_MAIN_CTRL_EBDAC (1 << 8) +#define SAA7134_MAIN_CTRL_TE6 (1 << 6) +#define SAA7134_MAIN_CTRL_TE5 (1 << 5) +#define SAA7134_MAIN_CTRL_TE4 (1 << 4) +#define SAA7134_MAIN_CTRL_TE3 (1 << 3) +#define SAA7134_MAIN_CTRL_TE2 (1 << 2) +#define SAA7134_MAIN_CTRL_TE1 (1 << 1) +#define SAA7134_MAIN_CTRL_TE0 (1 << 0) + +/* DMA status */ +#define SAA7134_DMA_STATUS (0x2ac >> 2) + +/* audio / video status */ +#define SAA7134_AV_STATUS (0x2c0 >> 2) +#define SAA7134_AV_STATUS_STEREO (1 << 17) +#define SAA7134_AV_STATUS_DUAL (1 << 16) +#define SAA7134_AV_STATUS_PILOT (1 << 15) +#define SAA7134_AV_STATUS_SMB (1 << 14) +#define SAA7134_AV_STATUS_DMB (1 << 13) +#define SAA7134_AV_STATUS_VDSP (1 << 12) +#define SAA7134_AV_STATUS_IIC_STATUS (3 << 10) +#define SAA7134_AV_STATUS_MVM (7 << 7) +#define SAA7134_AV_STATUS_FIDT (1 << 6) +#define SAA7134_AV_STATUS_INTL (1 << 5) +#define SAA7134_AV_STATUS_RDCAP (1 << 4) +#define SAA7134_AV_STATUS_PWR_ON (1 << 3) +#define SAA7134_AV_STATUS_LOAD_ERR (1 << 2) +#define SAA7134_AV_STATUS_TRIG_ERR (1 << 1) +#define SAA7134_AV_STATUS_CONF_ERR (1 << 0) + +/* interrupt */ +#define SAA7134_IRQ1 (0x2c4 >> 2) +#define SAA7134_IRQ1_INTE_RA3_1 (1 << 25) +#define SAA7134_IRQ1_INTE_RA3_0 (1 << 24) +#define SAA7134_IRQ1_INTE_RA2_3 (1 << 19) +#define SAA7134_IRQ1_INTE_RA2_2 (1 << 18) +#define SAA7134_IRQ1_INTE_RA2_1 (1 << 17) +#define SAA7134_IRQ1_INTE_RA2_0 (1 << 16) +#define SAA7134_IRQ1_INTE_RA1_3 (1 << 11) +#define SAA7134_IRQ1_INTE_RA1_2 (1 << 10) +#define SAA7134_IRQ1_INTE_RA1_1 (1 << 9) +#define SAA7134_IRQ1_INTE_RA1_0 (1 << 8) +#define SAA7134_IRQ1_INTE_RA0_7 (1 << 7) +#define SAA7134_IRQ1_INTE_RA0_6 (1 << 6) +#define SAA7134_IRQ1_INTE_RA0_5 (1 << 5) +#define SAA7134_IRQ1_INTE_RA0_4 (1 << 4) +#define SAA7134_IRQ1_INTE_RA0_3 (1 << 3) +#define SAA7134_IRQ1_INTE_RA0_2 (1 << 2) +#define SAA7134_IRQ1_INTE_RA0_1 (1 << 1) +#define SAA7134_IRQ1_INTE_RA0_0 (1 << 0) +#define SAA7134_IRQ2 (0x2c8 >> 2) +#define SAA7134_IRQ2_INTE_SC2 (1 << 10) +#define SAA7134_IRQ2_INTE_SC1 (1 << 9) +#define SAA7134_IRQ2_INTE_SC0 (1 << 8) +#define SAA7134_IRQ2_INTE_DEC5 (1 << 7) +#define SAA7134_IRQ2_INTE_DEC4 (1 << 6) +#define SAA7134_IRQ2_INTE_DEC3 (1 << 5) +#define SAA7134_IRQ2_INTE_DEC2 (1 << 4) +#define SAA7134_IRQ2_INTE_DEC1 (1 << 3) +#define SAA7134_IRQ2_INTE_DEC0 (1 << 2) +#define SAA7134_IRQ2_INTE_PE (1 << 1) +#define SAA7134_IRQ2_INTE_AR (1 << 0) +#define SAA7134_IRQ_REPORT (0x2cc >> 2) +#define SAA7134_IRQ_REPORT_LOAD_ERR (1 << 13) +#define SAA7134_IRQ_REPORT_CONF_ERR (1 << 12) +#define SAA7134_IRQ_REPORT_TRIG_ERR (1 << 11) +#define SAA7134_IRQ_REPORT_MMC (1 << 10) +#define SAA7134_IRQ_REPORT_FIDT (1 << 9) +#define SAA7134_IRQ_REPORT_INTL (1 << 8) +#define SAA7134_IRQ_REPORT_RDCAP (1 << 7) +#define SAA7134_IRQ_REPORT_PWR_ON (1 << 6) +#define SAA7134_IRQ_REPORT_PE (1 << 5) +#define SAA7134_IRQ_REPORT_AR (1 << 4) +#define SAA7134_IRQ_REPORT_DONE_RA3 (1 << 3) +#define SAA7134_IRQ_REPORT_DONE_RA2 (1 << 2) +#define SAA7134_IRQ_REPORT_DONE_RA1 (1 << 1) +#define SAA7134_IRQ_REPORT_DONE_RA0 (1 << 0) +#define SAA7134_IRQ_STATUS (0x2d0 >> 2) + + +/* ------------------------------------------------------------------ */ +/* + * registers -- 8 bit + */ + +/* video decoder */ +#define SAA7134_INCR_DELAY 0x101 +#define SAA7134_ANALOG_IN_CTRL1 0x102 +#define SAA7134_ANALOG_IN_CTRL2 0x103 +#define SAA7134_ANALOG_IN_CTRL3 0x104 +#define SAA7134_ANALOG_IN_CTRL4 0x105 +#define SAA7134_HSYNC_START 0x106 +#define SAA7134_HSYNC_STOP 0x107 +#define SAA7134_SYNC_CTRL 0x108 +#define SAA7134_LUMA_CTRL 0x109 +#define SAA7134_DEC_LUMA_BRIGHT 0x10a +#define SAA7134_DEC_LUMA_CONTRAST 0x10b +#define SAA7134_DEC_CHROMA_SATURATION 0x10c +#define SAA7134_DEC_CHROMA_HUE 0x10d +#define SAA7134_CHROMA_CTRL1 0x10e +#define SAA7134_CHROMA_GAIN 0x10f +#define SAA7134_CHROMA_CTRL2 0x110 +#define SAA7134_MODE_DELAY_CTRL 0x111 + +#define SAA7134_ANALOG_ADC 0x114 +#define SAA7134_VGATE_START 0x115 +#define SAA7134_VGATE_STOP 0x116 +#define SAA7134_MISC_VGATE_MSB 0x117 +#define SAA7134_RAW_DATA_GAIN 0x118 +#define SAA7134_RAW_DATA_OFFSET 0x119 +#define SAA7134_STATUS_VIDEO1 0x11e +#define SAA7134_STATUS_VIDEO2 0x11f + +/* video scaler */ +#define SAA7134_SOURCE_TIMING1 0x000 +#define SAA7134_SOURCE_TIMING2 0x001 +#define SAA7134_REGION_ENABLE 0x004 +#define SAA7134_SCALER_STATUS0 0x006 +#define SAA7134_SCALER_STATUS1 0x007 +#define SAA7134_START_GREEN 0x00c +#define SAA7134_START_BLUE 0x00d +#define SAA7134_START_RED 0x00e +#define SAA7134_GREEN_PATH(x) (0x010 +x) +#define SAA7134_BLUE_PATH(x) (0x020 +x) +#define SAA7134_RED_PATH(x) (0x030 +x) + +#define TASK_A 0x040 +#define TASK_B 0x080 +#define SAA7134_TASK_CONDITIONS(t) (0x000 +t) +#define SAA7134_FIELD_HANDLING(t) (0x001 +t) +#define SAA7134_DATA_PATH(t) (0x002 +t) +#define SAA7134_VBI_H_START1(t) (0x004 +t) +#define SAA7134_VBI_H_START2(t) (0x005 +t) +#define SAA7134_VBI_H_STOP1(t) (0x006 +t) +#define SAA7134_VBI_H_STOP2(t) (0x007 +t) +#define SAA7134_VBI_V_START1(t) (0x008 +t) +#define SAA7134_VBI_V_START2(t) (0x009 +t) +#define SAA7134_VBI_V_STOP1(t) (0x00a +t) +#define SAA7134_VBI_V_STOP2(t) (0x00b +t) +#define SAA7134_VBI_H_LEN1(t) (0x00c +t) +#define SAA7134_VBI_H_LEN2(t) (0x00d +t) +#define SAA7134_VBI_V_LEN1(t) (0x00e +t) +#define SAA7134_VBI_V_LEN2(t) (0x00f +t) + +#define SAA7134_VIDEO_H_START1(t) (0x014 +t) +#define SAA7134_VIDEO_H_START2(t) (0x015 +t) +#define SAA7134_VIDEO_H_STOP1(t) (0x016 +t) +#define SAA7134_VIDEO_H_STOP2(t) (0x017 +t) +#define SAA7134_VIDEO_V_START1(t) (0x018 +t) +#define SAA7134_VIDEO_V_START2(t) (0x019 +t) +#define SAA7134_VIDEO_V_STOP1(t) (0x01a +t) +#define SAA7134_VIDEO_V_STOP2(t) (0x01b +t) +#define SAA7134_VIDEO_PIXELS1(t) (0x01c +t) +#define SAA7134_VIDEO_PIXELS2(t) (0x01d +t) +#define SAA7134_VIDEO_LINES1(t) (0x01e +t) +#define SAA7134_VIDEO_LINES2(t) (0x01f +t) + +#define SAA7134_H_PRESCALE(t) (0x020 +t) +#define SAA7134_ACC_LENGTH(t) (0x021 +t) +#define SAA7134_LEVEL_CTRL(t) (0x022 +t) +#define SAA7134_FIR_PREFILTER_CTRL(t) (0x023 +t) +#define SAA7134_LUMA_BRIGHT(t) (0x024 +t) +#define SAA7134_LUMA_CONTRAST(t) (0x025 +t) +#define SAA7134_CHROMA_SATURATION(t) (0x026 +t) +#define SAA7134_VBI_H_SCALE_INC1(t) (0x028 +t) +#define SAA7134_VBI_H_SCALE_INC2(t) (0x029 +t) +#define SAA7134_VBI_PHASE_OFFSET_LUMA(t) (0x02a +t) +#define SAA7134_VBI_PHASE_OFFSET_CHROMA(t) (0x02b +t) +#define SAA7134_H_SCALE_INC1(t) (0x02c +t) +#define SAA7134_H_SCALE_INC2(t) (0x02d +t) +#define SAA7134_H_PHASE_OFF_LUMA(t) (0x02e +t) +#define SAA7134_H_PHASE_OFF_CHROMA(t) (0x02f +t) +#define SAA7134_V_SCALE_RATIO1(t) (0x030 +t) +#define SAA7134_V_SCALE_RATIO2(t) (0x031 +t) +#define SAA7134_V_FILTER(t) (0x032 +t) +#define SAA7134_V_PHASE_OFFSET0(t) (0x034 +t) +#define SAA7134_V_PHASE_OFFSET1(t) (0x035 +t) +#define SAA7134_V_PHASE_OFFSET2(t) (0x036 +t) +#define SAA7134_V_PHASE_OFFSET3(t) (0x037 +t) + +/* clipping & dma */ +#define SAA7134_OFMT_VIDEO_A 0x300 +#define SAA7134_OFMT_DATA_A 0x301 +#define SAA7134_OFMT_VIDEO_B 0x302 +#define SAA7134_OFMT_DATA_B 0x303 +#define SAA7134_ALPHA_NOCLIP 0x304 +#define SAA7134_ALPHA_CLIP 0x305 +#define SAA7134_UV_PIXEL 0x308 +#define SAA7134_CLIP_RED 0x309 +#define SAA7134_CLIP_GREEN 0x30a +#define SAA7134_CLIP_BLUE 0x30b + +/* i2c bus */ +#define SAA7134_I2C_ATTR_STATUS 0x180 +#define SAA7134_I2C_DATA 0x181 +#define SAA7134_I2C_CLOCK_SELECT 0x182 +#define SAA7134_I2C_TIMER 0x183 + +/* audio */ +#define SAA7134_NICAM_ADD_DATA1 0x140 +#define SAA7134_NICAM_ADD_DATA2 0x141 +#define SAA7134_NICAM_STATUS 0x142 +#define SAA7134_AUDIO_STATUS 0x143 +#define SAA7134_NICAM_ERROR_COUNT 0x144 +#define SAA7134_IDENT_SIF 0x145 +#define SAA7134_LEVEL_READOUT1 0x146 +#define SAA7134_LEVEL_READOUT2 0x147 +#define SAA7134_NICAM_ERROR_LOW 0x148 +#define SAA7134_NICAM_ERROR_HIGH 0x149 +#define SAA7134_DCXO_IDENT_CTRL 0x14a +#define SAA7134_DEMODULATOR 0x14b +#define SAA7134_AGC_GAIN_SELECT 0x14c +#define SAA7134_CARRIER1_FREQ0 0x150 +#define SAA7134_CARRIER1_FREQ1 0x151 +#define SAA7134_CARRIER1_FREQ2 0x152 +#define SAA7134_CARRIER2_FREQ0 0x154 +#define SAA7134_CARRIER2_FREQ1 0x155 +#define SAA7134_CARRIER2_FREQ2 0x156 +#define SAA7134_NUM_SAMPLES0 0x158 +#define SAA7134_NUM_SAMPLES1 0x159 +#define SAA7134_NUM_SAMPLES2 0x15a +#define SAA7134_AUDIO_FORMAT_CTRL 0x15b +#define SAA7134_MONITOR_SELECT 0x160 +#define SAA7134_FM_DEEMPHASIS 0x161 +#define SAA7134_FM_DEMATRIX 0x162 +#define SAA7134_CHANNEL1_LEVEL 0x163 +#define SAA7134_CHANNEL2_LEVEL 0x164 +#define SAA7134_NICAM_CONFIG 0x165 +#define SAA7134_NICAM_LEVEL_ADJUST 0x166 +#define SAA7134_STEREO_DAC_OUTPUT_SELECT 0x167 +#define SAA7134_I2S_OUTPUT_FORMAT 0x168 +#define SAA7134_I2S_OUTPUT_SELECT 0x169 +#define SAA7134_I2S_OUTPUT_LEVEL 0x16a +#define SAA7134_DSP_OUTPUT_SELECT 0x16b +#define SAA7134_AUDIO_MUTE_CTRL 0x16c +#define SAA7134_SIF_SAMPLE_FREQ 0x16d +#define SAA7134_ANALOG_IO_SELECT 0x16e +#define SAA7134_AUDIO_CLOCK0 0x170 +#define SAA7134_AUDIO_CLOCK1 0x171 +#define SAA7134_AUDIO_CLOCK2 0x172 +#define SAA7134_AUDIO_PLL_CTRL 0x173 +#define SAA7134_AUDIO_CLOCKS_PER_FIELD0 0x174 +#define SAA7134_AUDIO_CLOCKS_PER_FIELD1 0x175 +#define SAA7134_AUDIO_CLOCKS_PER_FIELD2 0x176 + +/* video port output */ +#define SAA7134_VIDEO_PORT_CTRL0 0x190 +#define SAA7134_VIDEO_PORT_CTRL1 0x191 +#define SAA7134_VIDEO_PORT_CTRL2 0x192 +#define SAA7134_VIDEO_PORT_CTRL3 0x193 +#define SAA7134_VIDEO_PORT_CTRL4 0x194 +#define SAA7134_VIDEO_PORT_CTRL5 0x195 +#define SAA7134_VIDEO_PORT_CTRL6 0x196 +#define SAA7134_VIDEO_PORT_CTRL7 0x197 +#define SAA7134_VIDEO_PORT_CTRL8 0x198 + +/* transport stream interface */ +#define SAA7134_TS_PARALLEL 0x1a0 +#define SAA7134_TS_PARALLEL_SERIAL 0x1a1 +#define SAA7134_TS_SERIAL0 0x1a2 +#define SAA7134_TS_SERIAL1 0x1a3 +#define SAA7134_TS_DMA0 0x1a4 +#define SAA7134_TS_DMA1 0x1a5 +#define SAA7134_TS_DMA2 0x1a6 + +/* GPIO Controls */ +#define SAA7134_GPIO_GPRESCAN 0x80 +#define SAA7134_GPIO_27_25 0x0E + +#define SAA7134_GPIO_GPMODE0 0x1B0 +#define SAA7134_GPIO_GPMODE1 0x1B1 +#define SAA7134_GPIO_GPMODE2 0x1B2 +#define SAA7134_GPIO_GPMODE3 0x1B3 +#define SAA7134_GPIO_GPSTATUS0 0x1B4 +#define SAA7134_GPIO_GPSTATUS1 0x1B5 +#define SAA7134_GPIO_GPSTATUS2 0x1B6 +#define SAA7134_GPIO_GPSTATUS3 0x1B7 + +/* I2S output */ +#define SAA7134_I2S_AUDIO_OUTPUT 0x1c0 + +/* test modes */ +#define SAA7134_SPECIAL_MODE 0x1d0 + +/* ------------------------------------------------------------------ */ +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ + diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/media/video/saa7134/saa7134-ts.c linux-2.5-bk/drivers/media/video/saa7134/saa7134-ts.c --- linux-2.5.45/drivers/media/video/saa7134/saa7134-ts.c Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/drivers/media/video/saa7134/saa7134-ts.c Thu Oct 31 22:12:49 2002 @@ -0,0 +1,453 @@ +/* + * device driver for philips saa7134 based TV cards + * video4linux video interface + * + * (c) 2001,02 Gerd Knorr [SuSE Labs] + * + * 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. + */ + +#define __NO_VERSION__ 1 + +#include +#include +#include +#include +#include + +#include "saa7134-reg.h" +#include "saa7134.h" + +/* ------------------------------------------------------------------ */ + +static unsigned int ts_debug = 0; +MODULE_PARM(ts_debug,"i"); +MODULE_PARM_DESC(ts_debug,"enable debug messages [ts]"); + +static unsigned int tsbufs = 4; +MODULE_PARM(tsbufs,"i"); +MODULE_PARM_DESC(tsbufs,"number of ts buffers, range 2-32"); + +#define TS_PACKET_SIZE 188 /* TS packets 188 bytes */ +#define TS_NR_PACKETS 312 + +#define dprintk(fmt, arg...) if (ts_debug) \ + printk(KERN_DEBUG "%s/ts: " fmt, dev->name, ## arg) + +/* ------------------------------------------------------------------ */ + +static int buffer_activate(struct saa7134_dev *dev, + struct saa7134_buf *buf, + struct saa7134_buf *next) +{ + unsigned long control,status; + + dprintk("buffer_activate [%p]\n",buf); + buf->vb.state = STATE_ACTIVE; + + /* dma: setup channel 5 (= TS) */ + control = SAA7134_RS_CONTROL_BURST_16 | + SAA7134_RS_CONTROL_ME | + (buf->pt->dma >> 12); + + status = saa_readl(SAA7134_IRQ_STATUS); + if (0 == (status & 0x100000)) { + saa_writel(SAA7134_RS_BA1(5),saa7134_buffer_base(buf)); + saa_writel(SAA7134_RS_BA2(5),saa7134_buffer_base(next)); + } else { + saa_writel(SAA7134_RS_BA1(5),saa7134_buffer_base(next)); + saa_writel(SAA7134_RS_BA2(5),saa7134_buffer_base(buf)); + } + saa_writel(SAA7134_RS_PITCH(5),TS_PACKET_SIZE); + saa_writel(SAA7134_RS_CONTROL(5),control); + + /* start DMA */ + saa7134_set_dmabits(dev); + + mod_timer(&dev->ts_q.timeout, jiffies+BUFFER_TIMEOUT); + return 0; +} + +static int buffer_prepare(struct file *file, struct videobuf_buffer *vb) +{ + struct saa7134_dev *dev = file->private_data; + struct saa7134_buf *buf = (struct saa7134_buf *)vb; + int lines, llength, size, err; + + llength = TS_PACKET_SIZE; + lines = TS_NR_PACKETS; + + size = lines * llength; + if (0 != buf->vb.baddr && buf->vb.bsize < size) + return -EINVAL; + + if (buf->vb.size != size) { + saa7134_dma_free(dev,buf); + } + + if (STATE_NEEDS_INIT == buf->vb.state) { + buf->vb.width = llength; + buf->vb.height = lines; + buf->vb.size = size; + buf->pt = &dev->ts.pt_ts; + + err = videobuf_iolock(dev->pci,&buf->vb); + if (err) + goto oops; + err = saa7134_pgtable_build(dev->pci,buf->pt, + buf->vb.dma.sglist, + buf->vb.dma.sglen, + saa7134_buffer_startpage(buf)); + if (err) + goto oops; + } + buf->vb.state = STATE_PREPARED; + buf->top_seen = 0; + buf->activate = buffer_activate; + buf->vb.field = V4L2_FIELD_SEQ_TB; + return 0; + + oops: + saa7134_dma_free(dev,buf); + return err; +} + +static int +buffer_setup(struct file *file, int *count, int *size) +{ + *size = TS_PACKET_SIZE * TS_NR_PACKETS; + if (0 == *count) + *count = tsbufs; + *count = saa7134_buffer_count(*size,*count); + return 0; +} + +static void buffer_queue(struct file *file, struct videobuf_buffer *vb) +{ + struct saa7134_dev *dev = file->private_data; + struct saa7134_buf *buf = (struct saa7134_buf *)vb; + + saa7134_buffer_queue(dev,&dev->ts_q,buf); +} + +static void buffer_release(struct file *file, struct videobuf_buffer *vb) +{ + struct saa7134_dev *dev = file->private_data; + struct saa7134_buf *buf = (struct saa7134_buf *)vb; + + saa7134_dma_free(dev,buf); +} + +static struct videobuf_queue_ops ts_qops = { + buf_setup: buffer_setup, + buf_prepare: buffer_prepare, + buf_queue: buffer_queue, + buf_release: buffer_release, +}; + +/* ------------------------------------------------------------------ */ + +static int ts_open(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + struct saa7134_dev *h,*dev = NULL; + struct list_head *list; + int err; + + list_for_each(list,&saa7134_devlist) { + h = list_entry(list, struct saa7134_dev, devlist); + if (h->ts_dev.minor == minor) + dev = h; + } + if (NULL == dev) + return -ENODEV; + + dprintk("open minor=%d\n",minor); + down(&dev->ts.ts.lock); + err = -EBUSY; + if (dev->ts.users) + goto done; + + dev->ts.users++; + file->private_data = dev; + err = 0; + done: + up(&dev->ts.ts.lock); + return err; +} + +static int ts_release(struct inode *inode, struct file *file) +{ + struct saa7134_dev *dev = file->private_data; + + if (dev->ts.ts.streaming) + videobuf_streamoff(file,&dev->ts.ts); + down(&dev->ts.ts.lock); + if (dev->ts.ts.reading) + videobuf_read_stop(file,&dev->ts.ts); + dev->ts.users--; + up(&dev->ts.ts.lock); + return 0; +} + +static ssize_t +ts_read(struct file *file, char *data, size_t count, loff_t *ppos) +{ + struct saa7134_dev *dev = file->private_data; + + return videobuf_read_stream(file, &dev->ts.ts, data, count, ppos, 0); +} + +static unsigned int +ts_poll(struct file *file, struct poll_table_struct *wait) +{ + struct saa7134_dev *dev = file->private_data; + + return videobuf_poll_stream(file, &dev->ts.ts, wait); +} + + +static int +ts_mmap(struct file *file, struct vm_area_struct * vma) +{ + struct saa7134_dev *dev = file->private_data; + + return videobuf_mmap_mapper(vma, &dev->ts.ts); +} + +/* + * This function is _not_ called directly, but from + * video_generic_ioctl (and maybe others). userspace + * copying is done already, arg is a kernel pointer. + */ +static int ts_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct saa7134_dev *dev = file->private_data; + + if (ts_debug > 1) + saa7134_print_ioctl(dev->name,cmd); + switch (cmd) { + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *cap = arg; + + memset(cap,0,sizeof(*cap)); + strcpy(cap->driver, "saa7134"); + strncpy(cap->card, saa7134_boards[dev->board].name, + sizeof(cap->card)); + sprintf(cap->bus_info,"PCI:%s",dev->pci->slot_name); + cap->version = SAA7134_VERSION_CODE; + cap->capabilities = + V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + return 0; + } + + /* --- input switching --------------------------------------- */ + case VIDIOC_ENUMINPUT: + { + struct v4l2_input *i = arg; + + if (i->index != 0) + return -EINVAL; + i->type = V4L2_INPUT_TYPE_CAMERA; + strcpy(i->name,"CCIR656"); + return 0; + } + case VIDIOC_G_INPUT: + { + int *i = arg; + *i = 0; + return 0; + } + case VIDIOC_S_INPUT: + { + int *i = arg; + + if (*i != 0) + return -EINVAL; + return 0; + } + /* --- capture ioctls ---------------------------------------- */ + + case VIDIOC_ENUM_FMT: + { + struct v4l2_fmtdesc *f = arg; + int index; + + index = f->index; + if (index != 0) + return -EINVAL; + + memset(f,0,sizeof(*f)); + f->index = index; + strncpy(f->description, "MPEG TS", 31); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->pixelformat = V4L2_PIX_FMT_MPEG; + return 0; + } + + case VIDIOC_G_FMT: + { + struct v4l2_format *f = arg; + + memset(f,0,sizeof(*f)); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + /* FIXME: translate subsampling type EMPRESS into + * width/height: */ + f->fmt.pix.width = 720; /* D1 */ + f->fmt.pix.height = 576; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + f->fmt.pix.sizeimage = TS_PACKET_SIZE*TS_NR_PACKETS; + return 0; + } + + case VIDIOC_S_FMT: + { + struct v4l2_format *f = arg; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + /* + FIXME: translate and round width/height into EMPRESS + subsample type: + + type | PAL | NTSC + --------------------------- + SIF | 352x288 | 352x240 + 1/2 D1 | 352x576 | 352x480 + 2/3 D1 | 480x576 | 480x480 + D1 | 720x576 | 720x480 + */ + + f->fmt.pix.width = 720; /* D1 */ + f->fmt.pix.height = 576; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + f->fmt.pix.sizeimage = TS_PACKET_SIZE*TS_NR_PACKETS; + } + + case VIDIOC_REQBUFS: + return videobuf_reqbufs(file,&dev->ts.ts,arg); + + case VIDIOC_QUERYBUF: + return videobuf_querybuf(&dev->ts.ts,arg); + + case VIDIOC_QBUF: + return videobuf_qbuf(file,&dev->ts.ts,arg); + + case VIDIOC_DQBUF: + return videobuf_dqbuf(file,&dev->ts.ts,arg); + + case VIDIOC_STREAMON: + return videobuf_streamon(file,&dev->ts.ts); + + case VIDIOC_STREAMOFF: + return videobuf_streamoff(file,&dev->ts.ts); + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static int ts_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, ts_do_ioctl); +} + + +static struct file_operations ts_fops = +{ + owner: THIS_MODULE, + open: ts_open, + release: ts_release, + read: ts_read, + poll: ts_poll, + mmap: ts_mmap, + ioctl: ts_ioctl, + llseek: no_llseek, +}; + + +/* ----------------------------------------------------------- */ +/* exported stuff */ + +struct video_device saa7134_ts_template = +{ + name: "saa7134-ts", + type: 0 /* FIXME */, + type2: 0 /* FIXME */, + hardware: 0, + fops: &ts_fops, + minor: -1, +}; + +int saa7134_ts_init(struct saa7134_dev *dev) +{ + /* sanitycheck insmod options */ + if (tsbufs < 2) + tsbufs = 2; + if (tsbufs > VIDEO_MAX_FRAME) + tsbufs = VIDEO_MAX_FRAME; + + INIT_LIST_HEAD(&dev->ts_q.queue); + dev->ts_q.timeout.function = saa7134_buffer_timeout; + dev->ts_q.timeout.data = (unsigned long)(&dev->ts_q); + dev->ts_q.dev = dev; + videobuf_queue_init(&dev->ts.ts, &ts_qops, dev->pci, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + sizeof(struct saa7134_buf)); + saa7134_pgtable_alloc(dev->pci,&dev->ts.pt_ts); + + /* init TS hw */ + saa_writeb(SAA7134_TS_SERIAL1, 0x00); /* deactivate TS softreset */ + saa_writeb(SAA7134_TS_PARALLEL, 0xec); /* TSSOP high active, TSVAL high active, TSLOCK ignored */ + saa_writeb(SAA7134_TS_PARALLEL_SERIAL, (TS_PACKET_SIZE-1)); + saa_writeb(SAA7134_TS_DMA0, ((TS_NR_PACKETS-1)&0xff)); + saa_writeb(SAA7134_TS_DMA1, (((TS_NR_PACKETS-1)>>8)&0xff)); + saa_writeb(SAA7134_TS_DMA2, ((((TS_NR_PACKETS-1)>>16)&0x3f) | 0x00)); /* TSNOPIT=0, TSCOLAP=0 */ + + return 0; +} + +int saa7134_ts_fini(struct saa7134_dev *dev) +{ + /* nothing */ + saa7134_pgtable_free(dev->pci,&dev->ts.pt_ts); + return 0; +} + + +void saa7134_irq_ts_done(struct saa7134_dev *dev, unsigned long status) +{ + spin_lock(&dev->slock); + if (dev->ts_q.curr) { + saa7134_buffer_finish(dev,&dev->ts_q,STATE_DONE); + } + saa7134_buffer_next(dev,&dev->ts_q); + spin_unlock(&dev->slock); +} + +/* ----------------------------------------------------------- */ +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/media/video/saa7134/saa7134-tvaudio.c linux-2.5-bk/drivers/media/video/saa7134/saa7134-tvaudio.c --- linux-2.5.45/drivers/media/video/saa7134/saa7134-tvaudio.c Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/drivers/media/video/saa7134/saa7134-tvaudio.c Thu Oct 31 22:12:49 2002 @@ -0,0 +1,552 @@ +/* + * device driver for philips saa7134 based TV cards + * tv audio decoder (fm stereo, nicam, ...) + * + * (c) 2001,02 Gerd Knorr [SuSE Labs] + * + * 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. + */ + +#define __NO_VERSION__ 1 + +#include +#include +#include +#include +#include +#include +#include + +#include "saa7134-reg.h" +#include "saa7134.h" + +/* ------------------------------------------------------------------ */ + +static unsigned int audio_debug = 0; +MODULE_PARM(audio_debug,"i"); +MODULE_PARM_DESC(audio_debug,"enable debug messages [tv audio]"); + +#define dprintk(fmt, arg...) if (audio_debug) \ + printk(KERN_DEBUG "%s/audio: " fmt, dev->name, ## arg) + +#define print_regb(reg) printk("%s: reg 0x%03x [%-16s]: 0x%02x\n", \ + dev->name,(SAA7134_##reg),(#reg),saa_readb((SAA7134_##reg))) + +/* ------------------------------------------------------------------ */ + +static struct saa7134_tvaudio tvaudio[] = { + { + name: "PAL-B/G FM-stereo", + std: V4L2_STD_PAL, + mode: TVAUDIO_FM_BG_STEREO, + carr1: 5500, + carr2: 5742, + },{ + name: "PAL-D/K1 FM-stereo", + std: V4L2_STD_PAL, + carr1: 6500, + carr2: 6258, + mode: TVAUDIO_FM_BG_STEREO, + },{ + name: "PAL-D/K2 FM-stereo", + std: V4L2_STD_PAL, + carr1: 6500, + carr2: 6742, + mode: TVAUDIO_FM_BG_STEREO, + },{ + name: "PAL-D/K3 FM-stereo", + std: V4L2_STD_PAL, + carr1: 6500, + carr2: 5742, + mode: TVAUDIO_FM_BG_STEREO, + },{ + name: "PAL-B/G NICAM", + std: V4L2_STD_PAL, + carr1: 5500, + carr2: 5850, + mode: TVAUDIO_NICAM_FM, + },{ + name: "PAL-I NICAM", + std: V4L2_STD_PAL, + carr1: 6000, + carr2: 6552, + mode: TVAUDIO_NICAM_FM, + },{ + name: "PAL-D/K NICAM", + std: V4L2_STD_PAL, + carr1: 6500, + carr2: 5850, + mode: TVAUDIO_NICAM_FM, + },{ + name: "SECAM-L NICAM", + std: V4L2_STD_SECAM, + carr1: 6500, + carr2: 5850, + mode: TVAUDIO_NICAM_AM, + },{ + name: "NTSC-M", + std: V4L2_STD_NTSC, + carr1: 4500, + carr2: -1, + mode: TVAUDIO_FM_MONO, + },{ + name: "NTSC-A2 FM-stereo", + std: V4L2_STD_NTSC, + carr1: 4500, + carr2: 4724, + mode: TVAUDIO_FM_K_STEREO, + } +}; +#define TVAUDIO (sizeof(tvaudio)/sizeof(struct saa7134_tvaudio)) + +/* ------------------------------------------------------------------ */ + +static void tvaudio_init(struct saa7134_dev *dev) +{ + int clock = saa7134_boards[dev->board].audio_clock; + + /* init all audio registers */ + saa_writeb(SAA7134_AUDIO_PLL_CTRL, 0x00); + + saa_writeb(SAA7134_AUDIO_CLOCK0, clock & 0xff); + saa_writeb(SAA7134_AUDIO_CLOCK1, (clock >> 8) & 0xff); + saa_writeb(SAA7134_AUDIO_CLOCK2, (clock >> 16) & 0xff); + saa_writeb(SAA7134_AUDIO_PLL_CTRL, 0x01); + + saa_writeb(SAA7134_NICAM_ERROR_LOW, 0x14); + saa_writeb(SAA7134_NICAM_ERROR_HIGH, 0x50); + saa_writeb(SAA7134_MONITOR_SELECT, 0xa0); + saa_writeb(SAA7134_FM_DEMATRIX, 0x80); +} + +static __u32 tvaudio_carr2reg(__u32 carrier) +{ + __u64 a = carrier; + + a <<= 24; + do_div(a,12288); + return a; +} + +static void tvaudio_setcarrier(struct saa7134_dev *dev, + int primary, int secondary) +{ + if (-1 == secondary) + secondary = primary; + saa_writel(SAA7134_CARRIER1_FREQ0 >> 2, tvaudio_carr2reg(primary)); + saa_writel(SAA7134_CARRIER2_FREQ0 >> 2, tvaudio_carr2reg(secondary)); +} + +static void saa7134_tvaudio_do_mute_input(struct saa7134_dev *dev) +{ + int mute; + struct saa7134_input *in; + int reg = 0; + int mask; + + /* look what is to do ... */ + in = dev->input; + mute = (dev->ctl_mute || dev->automute); + if (!card_has_audio(dev) && card(dev).mute.name) { + /* 7130 - we'll mute using some unconnected audio input */ + if (mute) + in = &card(dev).mute; + } + if (dev->hw_mute == mute && + dev->hw_input == in) + return; + +#if 1 + dprintk("ctl_mute=%d automute=%d input=%s => mute=%d input=%s\n", + dev->ctl_mute,dev->automute,dev->input->name,mute,in->name); +#endif + dev->hw_mute = mute; + dev->hw_input = in; + + if (card_has_audio(dev)) + /* 7134 mute */ + saa_writeb(SAA7134_AUDIO_MUTE_CTRL, mute ? 0xff : 0xbb); + + /* switch internal audio mux */ + switch (in->amux) { + case TV: reg = 0x02; break; + case LINE1: reg = 0x00; break; + case LINE2: reg = 0x01; break; + } + saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x07, reg); + + /* switch gpio-connected external audio mux */ + if (0 == card(dev).gpiomask) + return; + mask = card(dev).gpiomask; + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, mask, mask); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, mask, in->gpio); + saa7134_track_gpio(dev,in->name); +} + +void saa7134_tvaudio_setmute(struct saa7134_dev *dev) +{ + saa7134_tvaudio_do_mute_input(dev); +} + +void saa7134_tvaudio_setinput(struct saa7134_dev *dev, + struct saa7134_input *in) +{ + dev->input = in; + saa7134_tvaudio_do_mute_input(dev); +} + +void saa7134_tvaudio_setvolume(struct saa7134_dev *dev, int level) +{ + saa_writeb(SAA7134_CHANNEL1_LEVEL, level & 0x1f); + saa_writeb(SAA7134_CHANNEL2_LEVEL, level & 0x1f); + saa_writeb(SAA7134_NICAM_LEVEL_ADJUST, level & 0x1f); +} + +static void tvaudio_setmode(struct saa7134_dev *dev, + struct saa7134_tvaudio *audio, + char *note) +{ + if (note) + dprintk("tvaudio_setmode: %s %s [%d.%03d/%d.%03d MHz]\n", + note,audio->name, + audio->carr1 / 1000, audio->carr1 % 1000, + audio->carr2 / 1000, audio->carr2 % 1000); + + if (dev->tvnorm->id == V4L2_STD_NTSC) { + saa_writeb(SAA7134_AUDIO_CLOCKS_PER_FIELD0, 0xde); + saa_writeb(SAA7134_AUDIO_CLOCKS_PER_FIELD1, 0x15); + saa_writeb(SAA7134_AUDIO_CLOCKS_PER_FIELD2, 0x02); + } else { + saa_writeb(SAA7134_AUDIO_CLOCKS_PER_FIELD0, 0x00); + saa_writeb(SAA7134_AUDIO_CLOCKS_PER_FIELD1, 0x80); + saa_writeb(SAA7134_AUDIO_CLOCKS_PER_FIELD2, 0x02); + } + tvaudio_setcarrier(dev,audio->carr1,audio->carr2); + + switch (audio->mode) { + case TVAUDIO_FM_MONO: + case TVAUDIO_FM_BG_STEREO: + saa_writeb(SAA7134_DEMODULATOR, 0x00); + saa_writeb(SAA7134_DCXO_IDENT_CTRL, 0x00); + saa_writeb(SAA7134_FM_DEEMPHASIS, 0x22); + saa_writeb(SAA7134_FM_DEMATRIX, 0x80); + saa_writeb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0xa0); + break; + case TVAUDIO_FM_K_STEREO: + saa_writeb(SAA7134_DEMODULATOR, 0x00); + saa_writeb(SAA7134_DCXO_IDENT_CTRL, 0x01); + saa_writeb(SAA7134_FM_DEEMPHASIS, 0x22); + saa_writeb(SAA7134_FM_DEMATRIX, 0x80); + saa_writeb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0xa0); + break; + case TVAUDIO_NICAM_FM: + saa_writeb(SAA7134_DEMODULATOR, 0x10); + saa_writeb(SAA7134_DCXO_IDENT_CTRL, 0x00); + saa_writeb(SAA7134_FM_DEEMPHASIS, 0x44); + saa_writeb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0xa1); + break; + case TVAUDIO_NICAM_AM: + saa_writeb(SAA7134_DEMODULATOR, 0x12); + saa_writeb(SAA7134_DCXO_IDENT_CTRL, 0x00); + saa_writeb(SAA7134_FM_DEEMPHASIS, 0x44); + saa_writeb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0xa1); + break; + case TVAUDIO_FM_SAT_STEREO: + /* not implemented (yet) */ + break; + } + saa_writel(0x174 >> 2, 0x0001e000); /* FIXME */ +} + +int saa7134_tvaudio_getstereo(struct saa7134_dev *dev, + struct saa7134_tvaudio *audio) +{ + __u32 idp,nicam; + int retval = -1; + + switch (audio->mode) { + case TVAUDIO_FM_MONO: + return V4L2_TUNER_SUB_MONO; + case TVAUDIO_FM_K_STEREO: + case TVAUDIO_FM_BG_STEREO: + idp = (saa_readb(SAA7134_IDENT_SIF) & 0xe0) >> 5; + if (0x03 == (idp & 0x03)) + retval = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + else if (0x05 == (idp & 0x05)) + retval = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; + else if (0x01 == (idp & 0x01)) + retval = V4L2_TUNER_SUB_MONO; + break; + case TVAUDIO_FM_SAT_STEREO: + /* not implemented (yet) */ + break; + case TVAUDIO_NICAM_FM: + case TVAUDIO_NICAM_AM: + nicam = saa_readb(SAA7134_NICAM_STATUS); + switch (nicam & 0x0b) { + case 0x08: + retval = V4L2_TUNER_SUB_MONO; + break; + case 0x09: + retval = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + break; + case 0x0a: + retval = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; + break; + } + break; + } + if (retval != -1) + dprintk("found audio subchannels:%s%s%s%s\n", + (retval & V4L2_TUNER_SUB_MONO) ? " mono" : "", + (retval & V4L2_TUNER_SUB_STEREO) ? " stereo" : "", + (retval & V4L2_TUNER_SUB_LANG1) ? " lang1" : "", + (retval & V4L2_TUNER_SUB_LANG2) ? " lang2" : ""); + return retval; +} + +static int tvaudio_sleep(struct saa7134_dev *dev, int timeout) +{ + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&dev->thread.wq, &wait); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(timeout); + remove_wait_queue(&dev->thread.wq, &wait); + return dev->thread.scan1 != dev->thread.scan2; +} + +static int tvaudio_checkcarrier(struct saa7134_dev *dev, int carrier) +{ + __s32 left,right,value; + + tvaudio_setcarrier(dev,carrier-100,carrier-100); + if (tvaudio_sleep(dev,HZ/10)) + return -1; + left = saa_readl(SAA7134_LEVEL_READOUT1 >> 2); + if (tvaudio_sleep(dev,HZ/10)) + return -1; + left = saa_readl(SAA7134_LEVEL_READOUT1 >> 2); + + tvaudio_setcarrier(dev,carrier+100,carrier+100); + if (tvaudio_sleep(dev,HZ/10)) + return -1; + right = saa_readl(SAA7134_LEVEL_READOUT1 >> 2); + if (tvaudio_sleep(dev,HZ/10)) + return -1; + right = saa_readl(SAA7134_LEVEL_READOUT1 >> 2); + + left >>= 16; + right >>= 16; + value = left > right ? left - right : right - left; + dprintk("scanning %d.%03d MHz => dc is %5d [%d/%d]\n", + carrier/1000,carrier%1000,value,left,right); + return value; +} + +static void sifdebug_dump_regs(struct saa7134_dev *dev) +{ + print_regb(AUDIO_STATUS); + print_regb(IDENT_SIF); + print_regb(LEVEL_READOUT1); + print_regb(LEVEL_READOUT2); + print_regb(DCXO_IDENT_CTRL); + print_regb(DEMODULATOR); + print_regb(AGC_GAIN_SELECT); + print_regb(MONITOR_SELECT); + print_regb(FM_DEEMPHASIS); + print_regb(FM_DEMATRIX); + print_regb(SIF_SAMPLE_FREQ); + print_regb(ANALOG_IO_SELECT); +} + +static int tvaudio_thread(void *data) +{ +#define MAX_SCAN 4 + static const int carr_pal[MAX_SCAN] = { 5500, 6000, 6500 }; + static const int carr_ntsc[MAX_SCAN] = { 4500 }; + static const int carr_secam[MAX_SCAN] = { 6500 }; + static const int carr_default[MAX_SCAN] = { 4500, 5500, 6000, 6500 }; + struct saa7134_dev *dev = data; + const int *carr_scan; + int carr_vals[4]; + int i,max,carrier,audio; + + lock_kernel(); + daemonize(); + sigfillset(¤t->blocked); + sprintf(current->comm, "%s", dev->name); + dev->thread.task = current; + unlock_kernel(); + if (dev->thread.notify != NULL) + up(dev->thread.notify); + + for (;;) { + if (dev->thread.exit || signal_pending(current)) + goto done; + interruptible_sleep_on(&dev->thread.wq); + if (dev->thread.exit || signal_pending(current)) + goto done; + + restart: + dev->thread.scan1 = dev->thread.scan2; + dprintk("tvaudio thread scan start [%d]\n",dev->thread.scan1); + dev->tvaudio = NULL; + tvaudio_init(dev); + dev->automute = 1; + saa7134_tvaudio_setmute(dev); + + /* give the tuner some time */ + if (tvaudio_sleep(dev,HZ/2)) + goto restart; + + /* find the main carrier */ + carr_scan = carr_default; + if (dev->tvnorm->id & V4L2_STD_PAL) + carr_scan = carr_pal; + if (dev->tvnorm->id & V4L2_STD_NTSC) + carr_scan = carr_ntsc; + if (dev->tvnorm->id & V4L2_STD_SECAM) + carr_scan = carr_secam; + saa_writeb(SAA7134_MONITOR_SELECT,0x00); + tvaudio_setmode(dev,&tvaudio[0],NULL); + for (i = 0; i < MAX_SCAN; i++) { + if (!carr_scan[i]) + continue; + carr_vals[i] = tvaudio_checkcarrier(dev,carr_scan[i]); + if (dev->thread.scan1 != dev->thread.scan2) + goto restart; + } + for (carrier = 0, max = 0, i = 0; i < MAX_SCAN; i++) { + if (!carr_scan[i]) + continue; + if (max < carr_vals[i]) { + max = carr_vals[i]; + carrier = carr_scan[i]; + } + } + if (0 == carrier) { + /* Oops: autoscan didn't work for some reason :-/ */ + printk("%s/audio: oops: audio carrier scan failed\n", + dev->name); + sifdebug_dump_regs(dev); + } else { + dprintk("found %s main sound carrier @ %d.%03d MHz\n", + dev->tvnorm->name, + carrier/1000,carrier%1000); + } + tvaudio_setcarrier(dev,carrier,carrier); + dev->automute = 0; + saa7134_tvaudio_setmute(dev); + + /* find the exact tv audio norm */ + for (audio = -1, i = 0; i < TVAUDIO; i++) { + if (dev->tvnorm->id != -1 && + dev->tvnorm->id != tvaudio[i].std) + continue; + if (tvaudio[i].carr1 != carrier) + continue; + + if (-1 == audio) + audio = i; + tvaudio_setmode(dev,&tvaudio[i],"trying"); + if (tvaudio_sleep(dev,HZ)) + goto restart; + if (-1 != saa7134_tvaudio_getstereo(dev,&tvaudio[i])) { + audio = i; + break; + } + } + if (-1 == audio) + continue; + tvaudio_setmode(dev,&tvaudio[audio],"using"); + dev->tvaudio = &tvaudio[audio]; + +#if 1 + if (tvaudio_sleep(dev,3*HZ)) + goto restart; + saa7134_tvaudio_getstereo(dev,&tvaudio[i]); +#endif + } + + done: + dev->thread.task = NULL; + if(dev->thread.notify != NULL) + up(dev->thread.notify); + return 0; +} + +/* ------------------------------------------------------------------ */ + +int saa7134_tvaudio_init(struct saa7134_dev *dev) +{ + DECLARE_MUTEX_LOCKED(sem); + + /* enable I2S audio output */ + if (saa7134_boards[dev->board].i2s_rate) { + int rate = (32000 == saa7134_boards[dev->board].i2s_rate) ? 0x01 : 0x03; + + /* set rate */ + saa_andorb(SAA7134_SIF_SAMPLE_FREQ, 0x03, rate); + + /* enable I2S output */ + saa_writeb(SAA7134_DSP_OUTPUT_SELECT, 0x80); + saa_writeb(SAA7134_I2S_OUTPUT_SELECT, 0x80); + saa_writeb(SAA7134_I2S_OUTPUT_FORMAT, 0x01); + saa_writeb(SAA7134_I2S_OUTPUT_LEVEL, 0x00); + saa_writeb(SAA7134_I2S_AUDIO_OUTPUT, 0x01); + } + + /* start tvaudio thread */ + init_waitqueue_head(&dev->thread.wq); + dev->thread.notify = &sem; + kernel_thread(tvaudio_thread,dev,0); + down(&sem); + dev->thread.notify = NULL; + wake_up_interruptible(&dev->thread.wq); + + return 0; +} + +int saa7134_tvaudio_fini(struct saa7134_dev *dev) +{ + DECLARE_MUTEX_LOCKED(sem); + + /* shutdown tvaudio thread */ + if (dev->thread.task) { + dev->thread.notify = &sem; + dev->thread.exit = 1; + wake_up_interruptible(&dev->thread.wq); + down(&sem); + dev->thread.notify = NULL; + } + saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x07, 0x00); /* LINE1 */ + return 0; +} + +int saa7134_tvaudio_do_scan(struct saa7134_dev *dev) +{ + dev->thread.scan2++; + wake_up_interruptible(&dev->thread.wq); + return 0; +} + +/* ----------------------------------------------------------- */ +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/media/video/saa7134/saa7134-vbi.c linux-2.5-bk/drivers/media/video/saa7134/saa7134-vbi.c --- linux-2.5.45/drivers/media/video/saa7134/saa7134-vbi.c Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/drivers/media/video/saa7134/saa7134-vbi.c Thu Oct 31 22:12:49 2002 @@ -0,0 +1,265 @@ +/* + * device driver for philips saa7134 based TV cards + * video4linux video interface + * + * (c) 2001,02 Gerd Knorr [SuSE Labs] + * + * 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. + */ + +#define __NO_VERSION__ 1 + +#include +#include +#include +#include +#include + +#include "saa7134-reg.h" +#include "saa7134.h" + +/* ------------------------------------------------------------------ */ + +static unsigned int vbi_debug = 0; +MODULE_PARM(vbi_debug,"i"); +MODULE_PARM_DESC(vbi_debug,"enable debug messages [vbi]"); + +static unsigned int vbibufs = 4; +MODULE_PARM(vbibufs,"i"); +MODULE_PARM_DESC(vbibufs,"number of vbi buffers, range 2-32"); + +#define dprintk(fmt, arg...) if (vbi_debug) \ + printk(KERN_DEBUG "%s/vbi: " fmt, dev->name, ## arg) + +/* ------------------------------------------------------------------ */ + +#define VBI_LINE_COUNT 16 +#define VBI_LINE_LENGTH 2048 +#define VBI_SCALE 0x200 + +static void task_init(struct saa7134_dev *dev, struct saa7134_buf *buf, + int task) +{ + struct saa7134_tvnorm *norm = dev->tvnorm; + + /* setup video scaler */ + saa_writeb(SAA7134_VBI_H_START1(task), norm->h_start & 0xff); + saa_writeb(SAA7134_VBI_H_START2(task), norm->h_start >> 8); + saa_writeb(SAA7134_VBI_H_STOP1(task), norm->h_stop & 0xff); + saa_writeb(SAA7134_VBI_H_STOP2(task), norm->h_stop >> 8); + saa_writeb(SAA7134_VBI_V_START1(task), norm->vbi_v_start & 0xff); + saa_writeb(SAA7134_VBI_V_START2(task), norm->vbi_v_start >> 8); + saa_writeb(SAA7134_VBI_V_STOP1(task), norm->vbi_v_stop & 0xff); + saa_writeb(SAA7134_VBI_V_STOP2(task), norm->vbi_v_stop >> 8); + + saa_writeb(SAA7134_VBI_H_SCALE_INC1(task), VBI_SCALE & 0xff); + saa_writeb(SAA7134_VBI_H_SCALE_INC2(task), VBI_SCALE >> 8); + saa_writeb(SAA7134_VBI_PHASE_OFFSET_LUMA(task), 0x00); + saa_writeb(SAA7134_VBI_PHASE_OFFSET_CHROMA(task), 0x00); + + saa_writeb(SAA7134_VBI_H_LEN1(task), buf->vb.width & 0xff); + saa_writeb(SAA7134_VBI_H_LEN2(task), buf->vb.width >> 8); + saa_writeb(SAA7134_VBI_V_LEN1(task), buf->vb.height & 0xff); + saa_writeb(SAA7134_VBI_V_LEN2(task), buf->vb.height >> 8); + + saa_andorb(SAA7134_DATA_PATH(task), 0xc0, 0x00); +} + +/* ------------------------------------------------------------------ */ + +static int buffer_activate(struct saa7134_dev *dev, + struct saa7134_buf *buf, + struct saa7134_buf *next) +{ + unsigned long control,base; + + dprintk("buffer_activate [%p]\n",buf); + buf->vb.state = STATE_ACTIVE; + + task_init(dev,buf,TASK_A); + task_init(dev,buf,TASK_B); + saa_writeb(SAA7134_OFMT_DATA_A, 0x06); + saa_writeb(SAA7134_OFMT_DATA_B, 0x06); + + /* DMA: setup channel 2+3 (= VBI Task A+B) */ + base = saa7134_buffer_base(buf); + control = SAA7134_RS_CONTROL_BURST_16 | + SAA7134_RS_CONTROL_ME | + (buf->pt->dma >> 12); + saa_writel(SAA7134_RS_BA1(2),base); + saa_writel(SAA7134_RS_BA2(2),base + buf->vb.size/2); + saa_writel(SAA7134_RS_PITCH(2),buf->vb.width); + saa_writel(SAA7134_RS_CONTROL(2),control); + saa_writel(SAA7134_RS_BA1(3),base); + saa_writel(SAA7134_RS_BA2(3),base + buf->vb.size/2); + saa_writel(SAA7134_RS_PITCH(3),buf->vb.width); + saa_writel(SAA7134_RS_CONTROL(3),control); + + /* start DMA */ + saa7134_set_dmabits(dev); + mod_timer(&dev->vbi_q.timeout, jiffies+BUFFER_TIMEOUT); + + return 0; +} + +static int buffer_prepare(struct file *file, struct videobuf_buffer *vb) +{ + struct saa7134_fh *fh = file->private_data; + struct saa7134_dev *dev = fh->dev; + struct saa7134_buf *buf = (struct saa7134_buf *)vb; + struct saa7134_tvnorm *norm = dev->tvnorm; + int lines, llength, size, err; + + lines = norm->vbi_v_stop - norm->vbi_v_start +1; + if (lines > VBI_LINE_COUNT) + lines = VBI_LINE_COUNT; +#if 1 + llength = VBI_LINE_LENGTH; +#else + llength = (norm->h_stop - norm->h_start +1) * 2; + if (llength > VBI_LINE_LENGTH) + llength = VBI_LINE_LENGTH; +#endif + size = lines * llength * 2; + if (0 != buf->vb.baddr && buf->vb.bsize < size) + return -EINVAL; + + if (buf->vb.size != size) + saa7134_dma_free(dev,buf); + + if (STATE_NEEDS_INIT == buf->vb.state) { + buf->vb.width = llength; + buf->vb.height = lines; + buf->vb.size = size; + buf->pt = &fh->pt_vbi; + + err = videobuf_iolock(dev->pci,&buf->vb); + if (err) + goto oops; + err = saa7134_pgtable_build(dev->pci,buf->pt, + buf->vb.dma.sglist, + buf->vb.dma.sglen, + saa7134_buffer_startpage(buf)); + if (err) + goto oops; + } + buf->vb.state = STATE_PREPARED; + buf->top_seen = 0; + buf->activate = buffer_activate; + buf->vb.field = V4L2_FIELD_SEQ_TB; + return 0; + + oops: + saa7134_dma_free(dev,buf); + return err; +} + +static int +buffer_setup(struct file *file, int *count, int *size) +{ + struct saa7134_fh *fh = file->private_data; + struct saa7134_dev *dev = fh->dev; + int llength,lines; + + lines = dev->tvnorm->vbi_v_stop - dev->tvnorm->vbi_v_start +1; +#if 1 + llength = VBI_LINE_LENGTH; +#else + llength = (norm->h_stop - norm->h_start +1) * 2; + if (llength > VBI_LINE_LENGTH) + llength = VBI_LINE_LENGTH; +#endif + *size = lines * llength * 2; + if (0 == *count) + *count = vbibufs; + *count = saa7134_buffer_count(*size,*count); + return 0; +} + +static void buffer_queue(struct file *file, struct videobuf_buffer *vb) +{ + struct saa7134_fh *fh = file->private_data; + struct saa7134_dev *dev = fh->dev; + struct saa7134_buf *buf = (struct saa7134_buf *)vb; + + saa7134_buffer_queue(dev,&dev->vbi_q,buf); +} + +static void buffer_release(struct file *file, struct videobuf_buffer *vb) +{ + struct saa7134_fh *fh = file->private_data; + struct saa7134_dev *dev = fh->dev; + struct saa7134_buf *buf = (struct saa7134_buf *)vb; + + saa7134_dma_free(dev,buf); +} + +struct videobuf_queue_ops saa7134_vbi_qops = { + buf_setup: buffer_setup, + buf_prepare: buffer_prepare, + buf_queue: buffer_queue, + buf_release: buffer_release, +}; + +/* ------------------------------------------------------------------ */ + +int saa7134_vbi_init(struct saa7134_dev *dev) +{ + INIT_LIST_HEAD(&dev->vbi_q.queue); + dev->vbi_q.timeout.function = saa7134_buffer_timeout; + dev->vbi_q.timeout.data = (unsigned long)(&dev->vbi_q); + dev->vbi_q.dev = dev; + + if (vbibufs < 2) + vbibufs = 2; + if (vbibufs > VIDEO_MAX_FRAME) + vbibufs = VIDEO_MAX_FRAME; + return 0; +} + +int saa7134_vbi_fini(struct saa7134_dev *dev) +{ + /* nothing */ + return 0; +} + +void saa7134_irq_vbi_done(struct saa7134_dev *dev, unsigned long status) +{ + spin_lock(&dev->slock); + if (dev->vbi_q.curr) { + dev->vbi_fieldcount++; + /* make sure we have seen both fields */ + if ((status & 0x10) == 0x10) { + dev->vbi_q.curr->top_seen = 1; + goto done; + } + if (!dev->vbi_q.curr->top_seen) + goto done; + + dev->vbi_q.curr->vb.field_count = dev->vbi_fieldcount; + saa7134_buffer_finish(dev,&dev->vbi_q,STATE_DONE); + } + saa7134_buffer_next(dev,&dev->vbi_q); + + done: + spin_unlock(&dev->slock); +} + +/* ----------------------------------------------------------- */ +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/media/video/saa7134/saa7134-video.c linux-2.5-bk/drivers/media/video/saa7134/saa7134-video.c --- linux-2.5.45/drivers/media/video/saa7134/saa7134-video.c Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/drivers/media/video/saa7134/saa7134-video.c Thu Oct 31 22:12:49 2002 @@ -0,0 +1,2035 @@ +/* + * device driver for philips saa7134 based TV cards + * video4linux video interface + * + * (c) 2001,02 Gerd Knorr [SuSE Labs] + * + * 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. + */ + +#define __NO_VERSION__ 1 + +#include +#include +#include +#include +#include + +#include "saa7134-reg.h" +#include "saa7134.h" +#include "tuner.h" +#include "audiochip.h" + +/* ------------------------------------------------------------------ */ + +static unsigned int video_debug = 0; +static unsigned int gbuffers = 8; +static unsigned int gbufsize = 768*576*4; +static unsigned int gbufsize_max = 768*576*4; +MODULE_PARM(video_debug,"i"); +MODULE_PARM_DESC(video_debug,"enable debug messages [video]"); +MODULE_PARM(gbuffers,"i"); +MODULE_PARM_DESC(gbuffers,"number of capture buffers, range 2-32"); + +#define dprintk(fmt, arg...) if (video_debug) \ + printk(KERN_DEBUG "%s/video: " fmt, dev->name, ## arg) + +/* ------------------------------------------------------------------ */ +/* data structs for video */ + +static int video_out[][9] = { + [CCIR656] = { 0x00, 0xb1, 0x00, 0xa1, 0x00, 0x04, 0x06, 0x00, 0x00 }, +}; + +static struct saa7134_format formats[] = { + { + name: "8 bpp gray", + fourcc: V4L2_PIX_FMT_GREY, + depth: 8, + pm: 0x06, + },{ + name: "15 bpp RGB, le", + fourcc: V4L2_PIX_FMT_RGB555, + depth: 16, + pm: 0x13 | 0x80, + },{ + name: "15 bpp RGB, be", + fourcc: V4L2_PIX_FMT_RGB555X, + depth: 16, + pm: 0x13 | 0x80, + bswap: 1, + },{ + name: "16 bpp RGB, le", + fourcc: V4L2_PIX_FMT_RGB565, + depth: 16, + pm: 0x10 | 0x80, + },{ + name: "16 bpp RGB, be", + fourcc: V4L2_PIX_FMT_RGB565X, + depth: 16, + pm: 0x10 | 0x80, + bswap: 1, + },{ + name: "24 bpp RGB, le", + fourcc: V4L2_PIX_FMT_BGR24, + depth: 24, + pm: 0x11, + },{ + name: "32 bpp RGB, le", + fourcc: V4L2_PIX_FMT_BGR32, + depth: 32, + pm: 0x12, + },{ + name: "32 bpp RGB, be", + fourcc: V4L2_PIX_FMT_RGB32, + depth: 32, + pm: 0x12, + bswap: 1, + wswap: 1, + },{ + name: "4:2:2 packed, YUYV", + fourcc: V4L2_PIX_FMT_YUYV, + depth: 16, + pm: 0x00, + bswap: 1, + yuv: 1, + },{ + name: "4:2:2 packed, UYVY", + fourcc: V4L2_PIX_FMT_UYVY, + depth: 16, + pm: 0x00, + yuv: 1, + },{ + name: "4:2:2 planar, Y-Cb-Cr", + fourcc: V4L2_PIX_FMT_YUV422P, + depth: 16, + pm: 0x09, + yuv: 1, + planar: 1, + hshift: 1, + vshift: 0, + },{ + name: "4:2:0 planar, Y-Cb-Cr", + fourcc: V4L2_PIX_FMT_YUV420, + depth: 12, + pm: 0x0a, + yuv: 1, + planar: 1, + hshift: 1, + vshift: 1, + } +}; +#define FORMATS (sizeof(formats)/sizeof(struct saa7134_format)) + + +static struct saa7134_tvnorm tvnorms[] = { + { + name: "PAL-BGHI", + id: V4L2_STD_PAL, + width: 720, + height: 576, + + sync_control: 0x18, + luma_control: 0x40, + chroma_ctrl1: 0x81, + chroma_gain: 0x2a, + chroma_ctrl2: 0x06, + + h_start: 0, + h_stop: 719, + video_v_start: 24, + video_v_stop: 311, + vbi_v_start: 7-3, /* FIXME */ + vbi_v_stop: 22-3, + },{ + name: "NTSC-M", + id: V4L2_STD_NTSC, + width: 720, + height: 480, + + sync_control: 0x59, + luma_control: 0x40, + chroma_ctrl1: 0x89, + chroma_gain: 0x2a, + chroma_ctrl2: 0x0e, + + h_start: 0, + h_stop: 719, + video_v_start: 22, + video_v_stop: 22+240, + vbi_v_start: 10, /* FIXME */ + vbi_v_stop: 21, /* FIXME */ + },{ + name: "SECAM", + id: V4L2_STD_SECAM, + width: 720, + height: 576, + + sync_control: 0x58, + luma_control: 0x1b, + chroma_ctrl1: 0xd1, + chroma_gain: 0x80, + chroma_ctrl2: 0x00, + + h_start: 0, + h_stop: 719, + video_v_start: 24, + video_v_stop: 311, + vbi_v_start: 7, + vbi_v_stop: 22, + },{ + name: "AUTO", + id: -1, + width: 768, + height: 576, + + sync_control: 0x98, + luma_control: 0x40, + chroma_ctrl1: 0x8b, + chroma_gain: 0x00, + chroma_ctrl2: 0x00, + + h_start: 0, + h_stop: 719, + video_v_start: 24, + video_v_stop: 311, + vbi_v_start: 7, + vbi_v_stop: 22, + } +}; +#define TVNORMS (sizeof(tvnorms)/sizeof(struct saa7134_tvnorm)) + + +#define V4L2_CID_PRIVATE_INVERT (V4L2_CID_PRIVATE_BASE + 0) +#define V4L2_CID_PRIVATE_Y_ODD (V4L2_CID_PRIVATE_BASE + 1) +#define V4L2_CID_PRIVATE_Y_EVEN (V4L2_CID_PRIVATE_BASE + 2) +#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 3) + +static const struct v4l2_queryctrl no_ctrl = { + name: "42", + flags: V4L2_CTRL_FLAG_DISABLED, +}; +static const struct v4l2_queryctrl video_ctrls[] = { + /* --- video --- */ + { + id: V4L2_CID_BRIGHTNESS, + name: "Brightness", + minimum: 0, + maximum: 255, + step: 1, + default_value: 128, + type: V4L2_CTRL_TYPE_INTEGER, + },{ + id: V4L2_CID_CONTRAST, + name: "Contrast", + minimum: 0, + maximum: 127, + step: 1, + default_value: 68, + type: V4L2_CTRL_TYPE_INTEGER, + },{ + id: V4L2_CID_SATURATION, + name: "Saturation", + minimum: 0, + maximum: 127, + step: 1, + default_value: 64, + type: V4L2_CTRL_TYPE_INTEGER, + },{ + id: V4L2_CID_HUE, + name: "Hue", + minimum: -128, + maximum: 127, + step: 1, + default_value: 0, + type: V4L2_CTRL_TYPE_INTEGER, + },{ + id: V4L2_CID_VFLIP, + name: "vertical flip", + minimum: 0, + maximum: 1, + type: V4L2_CTRL_TYPE_BOOLEAN, + }, + /* --- audio --- */ + { + id: V4L2_CID_AUDIO_MUTE, + name: "Mute", + minimum: 0, + maximum: 1, + type: V4L2_CTRL_TYPE_BOOLEAN, + },{ + id: V4L2_CID_AUDIO_VOLUME, + name: "Volume", + minimum: -15, + maximum: 15, + step: 1, + default_value: 0, + type: V4L2_CTRL_TYPE_INTEGER, + }, + /* --- private --- */ + { + id: V4L2_CID_PRIVATE_INVERT, + name: "Invert", + minimum: 0, + maximum: 1, + type: V4L2_CTRL_TYPE_BOOLEAN, + },{ + id: V4L2_CID_PRIVATE_Y_ODD, + name: "y offset odd field", + minimum: 0, + maximum: 128, + default_value: 0, + type: V4L2_CTRL_TYPE_INTEGER, + },{ + id: V4L2_CID_PRIVATE_Y_EVEN, + name: "y offset even field", + minimum: 0, + maximum: 128, + default_value: 0, + type: V4L2_CTRL_TYPE_INTEGER, + } +}; +const int CTRLS = (sizeof(video_ctrls)/sizeof(struct v4l2_queryctrl)); + +static const struct v4l2_queryctrl* ctrl_by_id(int id) +{ + int i; + + for (i = 0; i < CTRLS; i++) + if (video_ctrls[i].id == id) + return video_ctrls+i; + return NULL; +} + +static struct saa7134_format* format_by_fourcc(int fourcc) +{ + int i; + + for (i = 0; i < FORMATS; i++) + if (formats[i].fourcc == fourcc) + return formats+i; + return NULL; +} + +/* ----------------------------------------------------------------------- */ +/* resource management */ + +static int res_get(struct saa7134_dev *dev, struct saa7134_fh *fh, int bit) +{ + if (fh->resources & bit) + /* have it already allocated */ + return 1; + + /* is it free? */ + down(&dev->lock); + if (dev->resources & bit) { + /* no, someone else uses it */ + up(&dev->lock); + return 0; + } + /* it's free, grab it */ + fh->resources |= bit; + dev->resources |= bit; + dprintk("res: get %d\n",bit); + up(&dev->lock); + return 1; +} + +static +int res_check(struct saa7134_fh *fh, int bit) +{ + return (fh->resources & bit); +} + +static +int res_locked(struct saa7134_dev *dev, int bit) +{ + return (dev->resources & bit); +} + +static +void res_free(struct saa7134_dev *dev, struct saa7134_fh *fh, int bits) +{ + if ((fh->resources & bits) != bits) + BUG(); + + down(&dev->lock); + fh->resources &= ~bits; + dev->resources &= ~bits; + dprintk("res: put %d\n",bits); + up(&dev->lock); +} + +/* ------------------------------------------------------------------ */ + +static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm) +{ + struct video_channel c; + int luma_control,mux; + + dprintk("set tv norm = %s\n",norm->name); + dev->tvnorm = norm; + + mux = card_in(dev,dev->ctl_input).vmux; + luma_control = norm->luma_control; + if (mux > 5) + luma_control |= 0x80; /* svideo */ + + /* setup video decoder */ + saa_writeb(SAA7134_INCR_DELAY, 0x08); + saa_writeb(SAA7134_ANALOG_IN_CTRL1, 0xc0 | mux); + saa_writeb(SAA7134_ANALOG_IN_CTRL2, 0x00); + + saa_writeb(SAA7134_ANALOG_IN_CTRL3, 0x90); + saa_writeb(SAA7134_ANALOG_IN_CTRL4, 0x90); + saa_writeb(SAA7134_HSYNC_START, 0xeb); + saa_writeb(SAA7134_HSYNC_STOP, 0xe0); + + saa_writeb(SAA7134_SYNC_CTRL, norm->sync_control); + saa_writeb(SAA7134_LUMA_CTRL, luma_control); + saa_writeb(SAA7134_DEC_LUMA_BRIGHT, dev->ctl_bright); + saa_writeb(SAA7134_DEC_LUMA_CONTRAST, dev->ctl_contrast); + + saa_writeb(SAA7134_DEC_CHROMA_SATURATION, dev->ctl_saturation); + saa_writeb(SAA7134_DEC_CHROMA_HUE, dev->ctl_hue); + saa_writeb(SAA7134_CHROMA_CTRL1, norm->chroma_ctrl1); + saa_writeb(SAA7134_CHROMA_GAIN, norm->chroma_gain); + + saa_writeb(SAA7134_CHROMA_CTRL2, norm->chroma_ctrl2); + saa_writeb(SAA7134_MODE_DELAY_CTRL, 0x00); + + saa_writeb(SAA7134_ANALOG_ADC, 0x01); + saa_writeb(SAA7134_VGATE_START, 0x11); + saa_writeb(SAA7134_VGATE_STOP, 0xfe); + saa_writeb(SAA7134_MISC_VGATE_MSB, 0x18); /* FIXME */ + saa_writeb(SAA7134_RAW_DATA_GAIN, 0x40); + saa_writeb(SAA7134_RAW_DATA_OFFSET, 0x80); + + /* pass down info to the i2c chips (v4l1) */ + memset(&c,0,sizeof(c)); + c.channel = dev->ctl_input; + c.norm = VIDEO_MODE_PAL; + if (norm->id & V4L2_STD_NTSC) + c.norm = VIDEO_MODE_NTSC; + if (norm->id & V4L2_STD_SECAM) + c.norm = VIDEO_MODE_SECAM; + saa7134_i2c_call_clients(dev,VIDIOCSCHAN,&c); +} + +static void video_mux(struct saa7134_dev *dev, int input) +{ + dprintk("video input = %d [%s]\n",input,card_in(dev,input).name); + dev->ctl_input = input; + set_tvnorm(dev,dev->tvnorm); + saa7134_tvaudio_setinput(dev,&card_in(dev,input)); +} + +static void set_h_prescale(struct saa7134_dev *dev, int task, int prescale) +{ + static const struct { + int xpsc; + int xacl; + int xc2_1; + int xdcg; + int vpfy; + } vals[] = { + /* XPSC XACL XC2_1 XDCG VPFY */ + { 1, 0, 0, 0, 0 }, + { 2, 2, 1, 2, 2 }, + { 3, 4, 1, 3, 2 }, + { 4, 8, 1, 4, 2 }, + { 5, 8, 1, 4, 2 }, + { 6, 8, 1, 4, 3 }, + { 7, 8, 1, 4, 3 }, + { 8, 15, 0, 4, 3 }, + { 9, 15, 0, 4, 3 }, + { 10, 16, 1, 5, 3 }, + }; + static const int count = sizeof(vals)/sizeof(vals[0]); + int i; + + for (i = 0; i < count; i++) + if (vals[i].xpsc == prescale) + break; + if (i == count) + return; + + saa_writeb(SAA7134_H_PRESCALE(task), vals[i].xpsc); + saa_writeb(SAA7134_ACC_LENGTH(task), vals[i].xacl); + saa_writeb(SAA7134_LEVEL_CTRL(task), + (vals[i].xc2_1 << 3) | (vals[i].xdcg)); + saa_andorb(SAA7134_FIR_PREFILTER_CTRL(task), 0x0f, + (vals[i].vpfy << 2) | vals[i].vpfy); +} + +static void set_v_scale(struct saa7134_dev *dev, int task, int yscale) +{ + int val,mirror; + + saa_writeb(SAA7134_V_SCALE_RATIO1(task), yscale & 0xff); + saa_writeb(SAA7134_V_SCALE_RATIO2(task), yscale >> 8); + + mirror = (dev->ctl_mirror) ? 0x02 : 0x00; + if (yscale < 2048) { + /* LPI */ + dprintk("yscale LPI yscale=%d\n",yscale); + saa_writeb(SAA7134_V_FILTER(task), 0x00 | mirror); + saa_writeb(SAA7134_LUMA_CONTRAST(task), 0x40); + saa_writeb(SAA7134_CHROMA_SATURATION(task), 0x40); + } else { + /* ACM */ + val = 0x40 * 1024 / yscale; + dprintk("yscale ACM yscale=%d val=0x%x\n",yscale,val); + saa_writeb(SAA7134_V_FILTER(task), 0x01 | mirror); + saa_writeb(SAA7134_LUMA_CONTRAST(task), val); + saa_writeb(SAA7134_CHROMA_SATURATION(task), val); + } + saa_writeb(SAA7134_LUMA_BRIGHT(task), 0x80); +} + +static void set_size(struct saa7134_dev *dev, int task, + int width, int height, int interlace) +{ + struct saa7134_tvnorm *norm = dev->tvnorm; + int prescale,xscale,yscale,y_even,y_odd; + int div = interlace ? 2 : 1; + + /* setup video scaler */ + saa_writeb(SAA7134_VIDEO_H_START1(task), norm->h_start & 0xff); + saa_writeb(SAA7134_VIDEO_H_START2(task), norm->h_start >> 8); + saa_writeb(SAA7134_VIDEO_H_STOP1(task), norm->h_stop & 0xff); + saa_writeb(SAA7134_VIDEO_H_STOP2(task), norm->h_stop >> 8); + saa_writeb(SAA7134_VIDEO_V_START1(task), norm->video_v_start & 0xff); + saa_writeb(SAA7134_VIDEO_V_START2(task), norm->video_v_start >> 8); + saa_writeb(SAA7134_VIDEO_V_STOP1(task), norm->video_v_stop & 0xff); + saa_writeb(SAA7134_VIDEO_V_STOP2(task), norm->video_v_stop >> 8); + + prescale = norm->width / width; + if (0 == prescale) + prescale = 1; + xscale = 1024 * norm->width / prescale / width; + yscale = 512 * div * norm->height / height; + dprintk("prescale=%d xscale=%d yscale=%d\n",prescale,xscale,yscale); + set_h_prescale(dev,task,prescale); + saa_writeb(SAA7134_H_SCALE_INC1(task), xscale & 0xff); + saa_writeb(SAA7134_H_SCALE_INC2(task), xscale >> 8); + set_v_scale(dev,task,yscale); + + saa_writeb(SAA7134_VIDEO_PIXELS1(task), width & 0xff); + saa_writeb(SAA7134_VIDEO_PIXELS2(task), width >> 8); + saa_writeb(SAA7134_VIDEO_LINES1(task), height/div & 0xff); + saa_writeb(SAA7134_VIDEO_LINES2(task), height/div >> 8); + + /* deinterlace y offsets */ + if (interlace) { + y_odd = dev->ctl_y_odd; + y_even = dev->ctl_y_even + yscale / 32; + saa_writeb(SAA7134_V_PHASE_OFFSET0(task), y_odd); + saa_writeb(SAA7134_V_PHASE_OFFSET1(task), y_even); + saa_writeb(SAA7134_V_PHASE_OFFSET2(task), y_odd); + saa_writeb(SAA7134_V_PHASE_OFFSET3(task), y_even); + } else { + y_odd = dev->ctl_y_odd; + y_even = dev->ctl_y_even + yscale / 64; + saa_writeb(SAA7134_V_PHASE_OFFSET0(task), y_odd); + saa_writeb(SAA7134_V_PHASE_OFFSET1(task), y_even); + saa_writeb(SAA7134_V_PHASE_OFFSET2(task), y_odd); + saa_writeb(SAA7134_V_PHASE_OFFSET3(task), y_even); + } +} + +/* ------------------------------------------------------------------ */ + +struct cliplist { + __u16 position; + __u8 enable; + __u8 disable; +}; + +static void sort_cliplist(struct cliplist *cl, int entries) +{ + struct cliplist swap; + int i,j,n; + + for (i = entries-2; i >= 0; i--) { + for (n = 0, j = 0; j <= i; j++) { + if (cl[j].position > cl[j+1].position) { + swap = cl[j]; + cl[j] = cl[j+1]; + cl[j+1] = swap; + n++; + } + } + if (0 == n) + break; + } +} + +static void set_cliplist(struct saa7134_dev *dev, int reg, + struct cliplist *cl, int entries, char *name) +{ + __u8 winbits = 0; + int i; + + for (i = 0; i < entries; i++) { + winbits |= cl[i].enable; + winbits &= ~cl[i].disable; + if (i < 15 && cl[i].position == cl[i+1].position) + continue; + saa_writeb(reg + 0, winbits); + saa_writeb(reg + 2, cl[i].position & 0xff); + saa_writeb(reg + 3, cl[i].position >> 8); + dprintk("clip: %s winbits=%02x pos=%d\n", + name,winbits,cl[i].position); + reg += 8; + } + for (; reg < 0x400; reg += 8) { + saa_writeb(reg+ 0, 0); + saa_writeb(reg + 1, 0); + saa_writeb(reg + 2, 0); + saa_writeb(reg + 3, 0); + } +} + +static int clip_range(int val) +{ + if (val < 0) + val = 0; + return val; +} + +static int setup_clipping(struct saa7134_dev *dev, struct v4l2_clip *clips, + int nclips, int interlace) +{ + struct cliplist col[16], row[16]; + int cols, rows, i; + int div = interlace ? 2 : 1; + + memset(col,0,sizeof(col)); cols = 0; + memset(row,0,sizeof(row)); rows = 0; + for (i = 0; i < nclips && i < 8; i++) { + col[cols].position = clip_range(clips[i].c.left); + col[cols].enable = (1 << i); + cols++; + col[cols].position = clip_range(clips[i].c.left+clips[i].c.width); + col[cols].disable = (1 << i); + cols++; + row[rows].position = clip_range(clips[i].c.top / div); + row[rows].enable = (1 << i); + rows++; + row[rows].position = clip_range((clips[i].c.top + clips[i].c.height) + / div); + row[rows].disable = (1 << i); + rows++; + } + sort_cliplist(col,cols); + sort_cliplist(row,rows); + set_cliplist(dev,0x380,col,cols,"cols"); + set_cliplist(dev,0x384,row,rows,"rows"); + return 0; +} + +static int verify_preview(struct saa7134_dev *dev, struct v4l2_window *win) +{ + enum v4l2_field field; + int maxw, maxh; + + if (NULL == dev->ovbuf.base) + return -EINVAL; + if (NULL == dev->ovfmt) + return -EINVAL; + if (win->w.width < 48 || win->w.height < 32) + return -EINVAL; + if (win->clipcount > 2048) + return -EINVAL; + + field = win->field; + maxw = dev->tvnorm->width; + maxh = dev->tvnorm->height; + + if (V4L2_FIELD_ANY == field) { + field = (win->w.height > maxh/2) + ? V4L2_FIELD_INTERLACED + : V4L2_FIELD_TOP; + } + switch (field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + maxh = maxh / 2; + break; + case V4L2_FIELD_INTERLACED: + break; + default: + return -EINVAL; + } + + win->field = field; + if (win->w.width > maxw) + win->w.width = maxw; + if (win->w.height > maxh) + win->w.height = maxh; + return 0; +} + +static int start_preview(struct saa7134_dev *dev, struct saa7134_fh *fh) +{ + unsigned long base,control,bpl; + int err; + + err = verify_preview(dev,&fh->win); + if (0 != err) + return err; + + dprintk("start_preview %dx%d+%d+%d %s field=%s\n", + fh->win.w.width,fh->win.w.height, + fh->win.w.left,fh->win.w.top, + dev->ovfmt->name,v4l2_field_names[dev->ovfield]); + + /* setup window + clipping */ + set_size(dev,TASK_B,fh->win.w.width,fh->win.w.height, + V4L2_FIELD_HAS_BOTH(dev->ovfield)); + setup_clipping(dev,fh->clips,fh->nclips, + V4L2_FIELD_HAS_BOTH(dev->ovfield)); + if (dev->ovfmt->yuv) + saa_andorb(SAA7134_DATA_PATH(TASK_B), 0x3f, 0x03); + else + saa_andorb(SAA7134_DATA_PATH(TASK_B), 0x3f, 0x01); + saa_writeb(SAA7134_OFMT_VIDEO_B, dev->ovfmt->pm | 0x20); + + /* dma: setup channel 1 (= Video Task B) */ + base = (unsigned long)dev->ovbuf.base; + base += dev->ovbuf.fmt.bytesperline * fh->win.w.top; + base += dev->ovfmt->depth/8 * fh->win.w.left; + bpl = dev->ovbuf.fmt.bytesperline; + control = SAA7134_RS_CONTROL_BURST_16; + if (dev->ovfmt->bswap) + control |= SAA7134_RS_CONTROL_BSWAP; + if (dev->ovfmt->wswap) + control |= SAA7134_RS_CONTROL_WSWAP; + if (V4L2_FIELD_HAS_BOTH(dev->ovfield)) { + saa_writel(SAA7134_RS_BA1(1),base); + saa_writel(SAA7134_RS_BA2(1),base+bpl); + saa_writel(SAA7134_RS_PITCH(1),bpl*2); + saa_writel(SAA7134_RS_CONTROL(1),control); + } else { + saa_writel(SAA7134_RS_BA1(1),base); + saa_writel(SAA7134_RS_BA2(1),base); + saa_writel(SAA7134_RS_PITCH(1),bpl); + saa_writel(SAA7134_RS_CONTROL(1),control); + } + + /* start dma */ + dev->ovenable = 1; + saa7134_set_dmabits(dev); + + return 0; +} + +static int stop_preview(struct saa7134_dev *dev, struct saa7134_fh *fh) +{ + dev->ovenable = 0; + saa7134_set_dmabits(dev); + return 0; +} + +/* ------------------------------------------------------------------ */ + +static int buffer_activate(struct saa7134_dev *dev, + struct saa7134_buf *buf, + struct saa7134_buf *next) +{ + unsigned long base,control,bpl; + unsigned long bpl_uv,lines_uv,base2,base3; /* planar */ + + dprintk("buffer_activate buf=%p\n",buf); + buf->vb.state = STATE_ACTIVE; + + set_size(dev,TASK_A,buf->vb.width,buf->vb.height, + V4L2_FIELD_HAS_BOTH(buf->vb.field)); + if (buf->fmt->yuv) + saa_andorb(SAA7134_DATA_PATH(TASK_A), 0x3f, 0x03); + else + saa_andorb(SAA7134_DATA_PATH(TASK_A), 0x3f, 0x01); + saa_writeb(SAA7134_OFMT_VIDEO_A, buf->fmt->pm); + + /* DMA: setup channel 0 (= Video Task A0) */ + base = saa7134_buffer_base(buf); + if (buf->fmt->planar) + bpl = buf->vb.width; + else + bpl = (buf->vb.width * buf->fmt->depth) / 8; + control = SAA7134_RS_CONTROL_BURST_16 | + SAA7134_RS_CONTROL_ME | + (buf->pt->dma >> 12); + if (buf->fmt->bswap) + control |= SAA7134_RS_CONTROL_BSWAP; + if (buf->fmt->wswap) + control |= SAA7134_RS_CONTROL_WSWAP; + if (V4L2_FIELD_HAS_BOTH(buf->vb.field)) { + /* interlaced */ + saa_writel(SAA7134_RS_BA1(0),base); + saa_writel(SAA7134_RS_BA2(0),base+bpl); + saa_writel(SAA7134_RS_PITCH(0),bpl*2); + } else { + /* non-interlaced */ + saa_writel(SAA7134_RS_BA1(0),base); + saa_writel(SAA7134_RS_BA2(0),base); + saa_writel(SAA7134_RS_PITCH(0),bpl); + } + saa_writel(SAA7134_RS_CONTROL(0),control); + + if (buf->fmt->planar) { + /* DMA: setup channel 4+5 (= planar task A) */ + bpl_uv = bpl >> buf->fmt->hshift; + lines_uv = buf->vb.height >> buf->fmt->vshift; + base2 = base + bpl * buf->vb.height; + base3 = base2 + bpl_uv * lines_uv; + dprintk("uv: bpl=%ld lines=%ld base2/3=%ld/%ld\n", + bpl_uv,lines_uv,base2,base3); + if (V4L2_FIELD_HAS_BOTH(buf->vb.field)) { + /* interlaced */ + saa_writel(SAA7134_RS_BA1(4),base2); + saa_writel(SAA7134_RS_BA2(4),base2+bpl_uv); + saa_writel(SAA7134_RS_PITCH(4),bpl_uv*2); + saa_writel(SAA7134_RS_BA1(5),base3); + saa_writel(SAA7134_RS_BA2(5),base3+bpl_uv); + saa_writel(SAA7134_RS_PITCH(5),bpl_uv*2); + } else { + /* non-interlaced */ + saa_writel(SAA7134_RS_BA1(4),base2); + saa_writel(SAA7134_RS_BA2(4),base2); + saa_writel(SAA7134_RS_PITCH(4),bpl_uv); + saa_writel(SAA7134_RS_BA1(5),base3); + saa_writel(SAA7134_RS_BA2(5),base3); + saa_writel(SAA7134_RS_PITCH(5),bpl_uv); + } + saa_writel(SAA7134_RS_CONTROL(4),control); + saa_writel(SAA7134_RS_CONTROL(5),control); + } + + /* start DMA */ + saa7134_set_dmabits(dev); + mod_timer(&dev->video_q.timeout, jiffies+BUFFER_TIMEOUT); + return 0; +} + +static int buffer_prepare(struct file *file, struct videobuf_buffer *vb) +{ + struct saa7134_fh *fh = file->private_data; + struct saa7134_dev *dev = fh->dev; + struct saa7134_buf *buf = (struct saa7134_buf *)vb; + int size,err; + + /* sanity checks */ + if (NULL == fh->fmt) + return -EINVAL; + if (fh->width < 48 || + fh->height < 32 || + fh->width > dev->tvnorm->width || + fh->height > dev->tvnorm->height) + return -EINVAL; + size = (fh->width * fh->height * fh->fmt->depth) >> 3; + if (0 != buf->vb.baddr && buf->vb.bsize < size) + return -EINVAL; + + dprintk("buffer_prepare [size=%dx%d,bytes=%d,fields=%s,%s]\n", + fh->width,fh->height,size,v4l2_field_names[fh->field], + fh->fmt->name); + if (buf->vb.width != fh->width || + buf->vb.height != fh->height || + buf->vb.size != size || + buf->vb.field != fh->field || + buf->fmt != fh->fmt) { + saa7134_dma_free(dev,buf); + } + + if (STATE_NEEDS_INIT == buf->vb.state) { + buf->vb.width = fh->width; + buf->vb.height = fh->height; + buf->vb.size = size; + buf->fmt = fh->fmt; + buf->vb.field = fh->field; + buf->pt = &fh->pt_cap; + + err = videobuf_iolock(dev->pci,&buf->vb); + if (err) + goto oops; + err = saa7134_pgtable_build(dev->pci,buf->pt, + buf->vb.dma.sglist, + buf->vb.dma.sglen, + saa7134_buffer_startpage(buf)); + if (err) + goto oops; + } + buf->vb.state = STATE_PREPARED; + buf->top_seen = 0; + buf->activate = buffer_activate; + return 0; + + oops: + saa7134_dma_free(dev,buf); + return err; +} + +static int +buffer_setup(struct file *file, int *count, int *size) +{ + struct saa7134_fh *fh = file->private_data; + + *size = fh->fmt->depth * fh->width * fh->height >> 3; + if (0 == *count) + *count = gbuffers; + *count = saa7134_buffer_count(*size,*count); + return 0; +} + +static void buffer_queue(struct file *file, struct videobuf_buffer *vb) +{ + struct saa7134_fh *fh = file->private_data; + struct saa7134_buf *buf = (struct saa7134_buf *)vb; + + saa7134_buffer_queue(fh->dev,&fh->dev->video_q,buf); +} + +static void buffer_release(struct file *file, struct videobuf_buffer *vb) +{ + struct saa7134_fh *fh = file->private_data; + struct saa7134_buf *buf = (struct saa7134_buf *)vb; + + saa7134_dma_free(fh->dev,buf); +} + +static struct videobuf_queue_ops video_qops = { + buf_setup: buffer_setup, + buf_prepare: buffer_prepare, + buf_queue: buffer_queue, + buf_release: buffer_release, +}; + +/* ------------------------------------------------------------------ */ + +static int get_control(struct saa7134_dev *dev, struct v4l2_control *c) +{ + const struct v4l2_queryctrl* ctrl; + + ctrl = ctrl_by_id(c->id); + if (NULL == ctrl) + return -EINVAL; + switch (c->id) { + case V4L2_CID_BRIGHTNESS: + c->value = dev->ctl_bright; + break; + case V4L2_CID_HUE: + c->value = dev->ctl_hue; + break; + case V4L2_CID_CONTRAST: + c->value = dev->ctl_contrast; + break; + case V4L2_CID_SATURATION: + c->value = dev->ctl_saturation; + break; + case V4L2_CID_AUDIO_MUTE: + c->value = dev->ctl_mute; + break; + case V4L2_CID_AUDIO_VOLUME: + c->value = dev->ctl_volume; + break; + case V4L2_CID_PRIVATE_INVERT: + c->value = dev->ctl_invert; + break; + case V4L2_CID_VFLIP: + c->value = dev->ctl_mirror; + break; + case V4L2_CID_PRIVATE_Y_EVEN: + c->value = dev->ctl_y_even; + break; + case V4L2_CID_PRIVATE_Y_ODD: + c->value = dev->ctl_y_odd; + break; + default: + return -EINVAL; + } + return 0; +} + +static int set_control(struct saa7134_dev *dev, struct saa7134_fh *fh, + struct v4l2_control *c) +{ + const struct v4l2_queryctrl* ctrl; + unsigned long flags; + int restart_overlay = 0; + + ctrl = ctrl_by_id(c->id); + if (NULL == ctrl) + return -EINVAL; + dprintk("set_control name=%s val=%d\n",ctrl->name,c->value); + switch (ctrl->type) { + case V4L2_CTRL_TYPE_BOOLEAN: + case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_INTEGER: + if (c->value < ctrl->minimum) + c->value = ctrl->minimum; + if (c->value > ctrl->maximum) + c->value = ctrl->maximum; + break; + default: + /* nothing */; + }; + switch (c->id) { + case V4L2_CID_BRIGHTNESS: + dev->ctl_bright = c->value; + saa_writeb(SAA7134_DEC_LUMA_BRIGHT, dev->ctl_bright); + break; + case V4L2_CID_HUE: + dev->ctl_hue = c->value; + saa_writeb(SAA7134_DEC_CHROMA_HUE, dev->ctl_hue); + break; + case V4L2_CID_CONTRAST: + dev->ctl_contrast = c->value; + saa_writeb(SAA7134_DEC_LUMA_CONTRAST, + dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast); + break; + case V4L2_CID_SATURATION: + dev->ctl_saturation = c->value; + saa_writeb(SAA7134_DEC_CHROMA_SATURATION, + dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation); + break; + case V4L2_CID_AUDIO_MUTE: + dev->ctl_mute = c->value; + saa7134_tvaudio_setmute(dev); + break; + case V4L2_CID_AUDIO_VOLUME: + dev->ctl_volume = c->value; + saa7134_tvaudio_setvolume(dev,dev->ctl_volume); + break; + case V4L2_CID_PRIVATE_INVERT: + dev->ctl_invert = c->value; + saa_writeb(SAA7134_DEC_LUMA_CONTRAST, + dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast); + saa_writeb(SAA7134_DEC_CHROMA_SATURATION, + dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation); + break; + case V4L2_CID_VFLIP: + dev->ctl_mirror = c->value; + restart_overlay = 1; + break; + case V4L2_CID_PRIVATE_Y_EVEN: + dev->ctl_y_even = c->value; + restart_overlay = 1; + break; + case V4L2_CID_PRIVATE_Y_ODD: + dev->ctl_y_odd = c->value; + restart_overlay = 1; + break; + default: + return -EINVAL; + } + if (restart_overlay && res_check(fh, RESOURCE_OVERLAY)) { + spin_lock_irqsave(&dev->slock,flags); + stop_preview(dev,fh); + start_preview(dev,fh); + spin_unlock_irqrestore(&dev->slock,flags); + } + return 0; +} + +/* ------------------------------------------------------------------ */ + +static struct videobuf_queue* saa7134_queue(struct saa7134_fh *fh) +{ + struct videobuf_queue* q = NULL; + + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + q = &fh->cap; + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + q = &fh->vbi; + break; + default: + BUG(); + } + return q; +} + +static int saa7134_resource(struct saa7134_fh *fh) +{ + int res = 0; + + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + res = RESOURCE_VIDEO; + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + res = RESOURCE_VBI; + break; + default: + BUG(); + } + return res; +} + +static int video_open(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + struct saa7134_dev *h,*dev = NULL; + struct saa7134_fh *fh; + struct list_head *list; + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + int radio = 0; + + list_for_each(list,&saa7134_devlist) { + h = list_entry(list, struct saa7134_dev, devlist); + if (h->video_dev.minor == minor) + dev = h; + if (h->radio_dev.minor == minor) { + radio = 1; + dev = h; + } + if (h->vbi_dev.minor == minor) { + type = V4L2_BUF_TYPE_VBI_CAPTURE; + dev = h; + } + } + if (NULL == dev) + return -ENODEV; + + dprintk("open minor=%d radio=%d type=%s\n",minor,radio, + v4l2_type_names[type]); + + /* allocate + initialize per filehandle data */ + fh = kmalloc(sizeof(*fh),GFP_KERNEL); + if (NULL == fh) + return -ENOMEM; + memset(fh,0,sizeof(*fh)); + file->private_data = fh; + fh->dev = dev; + fh->radio = radio; + fh->type = type; + fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24); + fh->width = 320; + fh->height = 240; + + videobuf_queue_init(&fh->cap, &video_qops, + dev->pci, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + sizeof(struct saa7134_buf)); + init_MUTEX(&fh->cap.lock); + saa7134_pgtable_alloc(dev->pci,&fh->pt_cap); + + videobuf_queue_init(&fh->vbi, &saa7134_vbi_qops, + dev->pci, &dev->slock, + V4L2_BUF_TYPE_VBI_CAPTURE, + sizeof(struct saa7134_buf)); + init_MUTEX(&fh->vbi.lock); + saa7134_pgtable_alloc(dev->pci,&fh->pt_vbi); + + if (fh->radio) { + /* switch to radio mode */ + saa7134_tvaudio_setinput(dev,&card(dev).radio); + saa7134_i2c_call_clients(dev,AUDC_SET_RADIO,NULL); + } else { + /* switch to video/vbi mode */ + set_tvnorm(dev,dev->tvnorm); + video_mux(dev,dev->ctl_input); + } + return 0; +} + +static ssize_t +video_read(struct file *file, char *data, size_t count, loff_t *ppos) +{ + struct saa7134_fh *fh = file->private_data; + + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return videobuf_read_one(file, saa7134_queue(fh), + data, count, ppos); + case V4L2_BUF_TYPE_VBI_CAPTURE: + return videobuf_read_stream(file, saa7134_queue(fh), + data, count, ppos, 1); + break; + default: + BUG(); + return 0; + } +} + +static unsigned int +video_poll(struct file *file, struct poll_table_struct *wait) +{ + struct saa7134_fh *fh = file->private_data; + struct videobuf_buffer *buf = NULL; + + if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) + return videobuf_poll_stream(file, &fh->vbi, wait); + + if (res_check(fh,RESOURCE_VIDEO)) { + if (!list_empty(&fh->cap.stream)) + buf = list_entry(fh->cap.stream.next, struct videobuf_buffer, stream); + } else { + down(&fh->cap.lock); + if (-1 == fh->cap.read_off) { + /* need to capture a new frame */ + if (res_locked(fh->dev,RESOURCE_VIDEO)) { + up(&fh->cap.lock); + return POLLERR; + } + if (0 != fh->cap.ops->buf_prepare(file,fh->cap.read_buf)) { + up(&fh->cap.lock); + return POLLERR; + } + fh->cap.ops->buf_queue(file,fh->cap.read_buf); + fh->cap.read_off = 0; + } + up(&fh->cap.lock); + buf = fh->cap.read_buf; + } + + if (!buf) + return POLLERR; + + poll_wait(file, &buf->done, wait); + if (buf->state == STATE_DONE || + buf->state == STATE_ERROR) + return POLLIN|POLLRDNORM; + return 0; +} + +static int video_release(struct inode *inode, struct file *file) +{ + struct saa7134_fh *fh = file->private_data; + struct saa7134_dev *dev = fh->dev; + unsigned long flags; + + if (res_check(fh, RESOURCE_OVERLAY)) { + spin_lock_irqsave(&dev->slock,flags); + stop_preview(dev,fh); + spin_unlock_irqrestore(&dev->slock,flags); + res_free(dev,fh,RESOURCE_OVERLAY); + } + if (res_check(fh, RESOURCE_VIDEO)) { + videobuf_queue_cancel(file,&fh->cap); + res_free(dev,fh,RESOURCE_VIDEO); + } + if (fh->cap.read_buf) { + buffer_release(file,fh->cap.read_buf); + kfree(fh->cap.read_buf); + } + if (fh->vbi.streaming) + videobuf_streamoff(file,&fh->vbi); + if (fh->vbi.reading) + videobuf_read_stop(file,&fh->vbi); + + saa7134_pgtable_free(dev->pci,&fh->pt_cap); + saa7134_pgtable_free(dev->pci,&fh->pt_vbi); + + file->private_data = NULL; + kfree(fh); + return 0; +} + +static int +video_mmap(struct file *file, struct vm_area_struct * vma) +{ + struct saa7134_fh *fh = file->private_data; + + return videobuf_mmap_mapper(vma,saa7134_queue(fh)); +} + +/* ------------------------------------------------------------------ */ + +int saa7134_g_fmt(struct saa7134_dev *dev, struct saa7134_fh *fh, + struct v4l2_format *f) +{ + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + memset(&f->fmt.pix,0,sizeof(f->fmt.pix)); + f->fmt.pix.width = fh->width; + f->fmt.pix.height = fh->height; + f->fmt.pix.pixelformat = fh->fmt->fourcc; + f->fmt.pix.sizeimage = + (fh->width*fh->height*fh->fmt->depth)/8; + return 0; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + f->fmt.win = fh->win; + return 0; + case V4L2_BUF_TYPE_VBI_CAPTURE: + { + struct saa7134_tvnorm *norm = fh->dev->tvnorm; + + f->fmt.vbi.sampling_rate = 6750000 * 4; + f->fmt.vbi.samples_per_line = 2048 /* VBI_LINE_LENGTH */; + f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + f->fmt.vbi.offset = 64 * 4; + f->fmt.vbi.start[0] = norm->vbi_v_start; + f->fmt.vbi.count[0] = norm->vbi_v_stop - norm->vbi_v_start +1; + f->fmt.vbi.start[1] = norm->video_v_stop + norm->vbi_v_start + 1; + f->fmt.vbi.count[1] = f->fmt.vbi.count[0]; + f->fmt.vbi.flags = 0; /* VBI_UNSYNC VBI_INTERLACED */; + return 0; + } + default: + return -EINVAL; + } +} + +int saa7134_try_fmt(struct saa7134_dev *dev, struct saa7134_fh *fh, + struct v4l2_format *f) +{ + int err; + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + { + struct saa7134_format *fmt; + enum v4l2_field field; + int maxw, maxh; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (NULL == fmt) + return -EINVAL; + + field = f->fmt.pix.field; + maxw = dev->tvnorm->width; + maxh = dev->tvnorm->height; + + if (V4L2_FIELD_ANY == field) { + field = (f->fmt.pix.height > maxh/2) + ? V4L2_FIELD_INTERLACED + : V4L2_FIELD_BOTTOM; + } + switch (field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + maxh = maxh / 2; + break; + case V4L2_FIELD_INTERLACED: + break; + default: + return -EINVAL; + } + + f->fmt.pix.field = field; + if (f->fmt.pix.width > maxw) + f->fmt.pix.width = maxw; + if (f->fmt.pix.height > maxh) + f->fmt.pix.height = maxh; + f->fmt.pix.sizeimage = + (fh->width * fh->height * fmt->depth)/8; + + return 0; + } + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + err = verify_preview(dev,&f->fmt.win); + if (0 != err) + return err; + return 0; + default: + return -EINVAL; + } +} + +int saa7134_s_fmt(struct saa7134_dev *dev, struct saa7134_fh *fh, + struct v4l2_format *f) +{ + unsigned long flags; + int err; + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + err = saa7134_try_fmt(dev,fh,f); + if (0 != err) + return err; + + fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + fh->width = f->fmt.pix.width; + fh->height = f->fmt.pix.height; + fh->field = f->fmt.pix.field; + return 0; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + err = verify_preview(dev,&f->fmt.win); + if (0 != err) + return err; + + down(&dev->lock); + fh->win = f->fmt.win; + fh->nclips = f->fmt.win.clipcount; + if (fh->nclips > 8) + fh->nclips = 8; + if (copy_from_user(fh->clips,f->fmt.win.clips, + sizeof(struct v4l2_clip)*fh->nclips)) { + up(&dev->lock); + return -EFAULT; + } + + if (res_check(fh, RESOURCE_OVERLAY)) { + spin_lock_irqsave(&dev->slock,flags); + stop_preview(dev,fh); + start_preview(dev,fh); + spin_unlock_irqrestore(&dev->slock,flags); + } + up(&dev->lock); + return 0; + break; + default: + return -EINVAL; + } +} + +/* + * This function is _not_ called directly, but from + * video_generic_ioctl (and maybe others). userspace + * copying is done already, arg is a kernel pointer. + */ +static int video_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct saa7134_fh *fh = file->private_data; + struct saa7134_dev *dev = fh->dev; + unsigned long flags; + int err; + + if (video_debug > 1) + saa7134_print_ioctl(dev->name,cmd); + switch (cmd) { + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *cap = arg; + + memset(cap,0,sizeof(*cap)); + strcpy(cap->driver, "saa7134"); + strncpy(cap->card, saa7134_boards[dev->board].name, + sizeof(cap->card)); + sprintf(cap->bus_info,"PCI:%s",dev->pci->slot_name); + cap->version = SAA7134_VERSION_CODE; + cap->capabilities = + V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_VIDEO_OVERLAY | + V4L2_CAP_VBI_CAPTURE | + V4L2_CAP_TUNER | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + return 0; + } + + /* --- tv standards ------------------------------------------ */ + case VIDIOC_ENUMSTD: + { + struct v4l2_standard *e = arg; + + if (e->index < 0 || e->index >= TVNORMS) + return -EINVAL; + err = v4l2_video_std_construct(e, tvnorms[e->index].id, + tvnorms[e->index].name); + if (err < 0) + return err; + return 0; + } + case VIDIOC_G_STD: + { + v4l2_std_id *id = arg; + + *id = dev->tvnorm->id; + return 0; + } + case VIDIOC_S_STD: + { + v4l2_std_id *id = arg; + int i; + + for(i = 0; i < TVNORMS; i++) + if (*id & tvnorms[i].id) + break; + if (i == TVNORMS) + return -EINVAL; + + down(&dev->lock); + if (res_check(fh, RESOURCE_OVERLAY)) { + spin_lock_irqsave(&dev->slock,flags); + stop_preview(dev,fh); + set_tvnorm(dev,&tvnorms[i]); + start_preview(dev,fh); + spin_unlock_irqrestore(&dev->slock,flags); + } else + set_tvnorm(dev,&tvnorms[i]); + up(&dev->lock); + return 0; + } + + /* --- input switching --------------------------------------- */ + case VIDIOC_ENUMINPUT: + { + struct v4l2_input *i = arg; + + if (i->index >= SAA7134_INPUT_MAX) + return -EINVAL; + if (NULL == card_in(dev,i->index).name) + return -EINVAL; + i->type = V4L2_INPUT_TYPE_CAMERA; + strcpy(i->name,card_in(dev,i->index).name); + if (card_in(dev,i->index).tv) + i->type = V4L2_INPUT_TYPE_TUNER; + return 0; + } + case VIDIOC_G_INPUT: + { + int *i = arg; + *i = dev->ctl_input; + return 0; + } + case VIDIOC_S_INPUT: + { + int *i = arg; + + if (*i < 0 || *i >= SAA7134_INPUT_MAX) + return -EINVAL; + if (NULL == card_in(dev,*i).name) + return -EINVAL; + down(&dev->lock); + video_mux(dev,*i); + up(&dev->lock); + return 0; + } + + /* --- tuner ioctls ------------------------------------------ */ + case VIDIOC_G_TUNER: + { + struct v4l2_tuner *t = arg; + int n; + + memset(t,0,sizeof(*t)); + for (n = 0; n < SAA7134_INPUT_MAX; n++) + if (card_in(dev,n).tv) + break; + if (NULL != card_in(dev,n).name) { + strcpy(t->name, "Television"); + t->capability = V4L2_TUNER_CAP_NORM | + V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1 | + V4L2_TUNER_CAP_LANG2; + t->rangehigh = 0xffffffffUL; + if (dev->tvaudio) { + t->rxsubchans = saa7134_tvaudio_getstereo + (dev,dev->tvaudio); + } else { + t->rxsubchans = V4L2_TUNER_SUB_MONO; + } +#if 1 + /* fill audmode -- FIXME: allow manual switching */ + t->audmode = V4L2_TUNER_MODE_MONO; + if (t->rxsubchans & V4L2_TUNER_SUB_STEREO) + t->audmode = V4L2_TUNER_MODE_STEREO; + else if (t->rxsubchans & V4L2_TUNER_SUB_LANG1) + t->audmode = V4L2_TUNER_MODE_LANG1; + else if (t->rxsubchans & V4L2_TUNER_SUB_LANG2) + t->audmode = V4L2_TUNER_MODE_LANG2; +#endif + } + if (0 != (saa_readb(SAA7134_STATUS_VIDEO1) & 0x03)) + t->signal = 0xffff; + return 0; + } + case VIDIOC_S_TUNER: + { +#if 0 + struct v4l2_tuner *t = arg; +#endif + return 0; + } + case VIDIOC_G_FREQUENCY: + { + struct v4l2_frequency *f = arg; + + memset(f,0,sizeof(*f)); + f->type = V4L2_TUNER_ANALOG_TV; + f->frequency = dev->ctl_freq; + return 0; + } + case VIDIOC_S_FREQUENCY: + { + struct v4l2_frequency *f = arg; + + if (0 != f->tuner) + return -EINVAL; + if (V4L2_TUNER_ANALOG_TV != f->type) + return -EINVAL; + down(&dev->lock); + dev->ctl_freq = f->frequency; + saa7134_i2c_call_clients(dev,VIDIOCSFREQ,&dev->ctl_freq); + up(&dev->lock); + return 0; + } + + /* --- control ioctls ---------------------------------------- */ + case VIDIOC_QUERYCTRL: + { + const struct v4l2_queryctrl *ctrl; + struct v4l2_queryctrl *c = arg; + + if ((c->id < V4L2_CID_BASE || + c->id >= V4L2_CID_LASTP1) && + (c->id < V4L2_CID_PRIVATE_BASE || + c->id >= V4L2_CID_PRIVATE_LASTP1)) + return -EINVAL; + ctrl = ctrl_by_id(c->id); + *c = (NULL != ctrl) ? *ctrl : no_ctrl; + return 0; + } + case VIDIOC_G_CTRL: + return get_control(dev,arg); + case VIDIOC_S_CTRL: + { + down(&dev->lock); + err = set_control(dev,fh,arg); + up(&dev->lock); + return err; + } + case VIDIOC_G_AUDIO: + { + struct v4l2_audio *a = arg; + + memset(a,0,sizeof(*a)); + strcpy(a->name,"audio"); + return 0; + } + case VIDIOC_S_AUDIO: + return 0; + case VIDIOC_G_PARM: + { + struct v4l2_captureparm *parm = arg; + memset(parm,0,sizeof(*parm)); + return 0; + } + + /* --- preview ioctls ---------------------------------------- */ + case VIDIOC_ENUM_FMT: + { + struct v4l2_fmtdesc *f = arg; + int index; + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + index = f->index; + if (index < 0 || index >= FORMATS) + return -EINVAL; + if (f->type == V4L2_BUF_TYPE_VIDEO_OVERLAY && + formats[index].planar) + return -EINVAL; + memset(f,0,sizeof(*f)); + f->index = index; + strncpy(f->description,formats[index].name,31); + f->pixelformat = formats[index].fourcc; + break; + default: + return -EINVAL; + } + return 0; + } + case VIDIOC_G_FBUF: + { + struct v4l2_framebuffer *fb = arg; + + *fb = dev->ovbuf; + fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; + return 0; + } + case VIDIOC_S_FBUF: + { + struct v4l2_framebuffer *fb = arg; + struct saa7134_format *fmt; + + if(!capable(CAP_SYS_ADMIN) && + !capable(CAP_SYS_RAWIO)) + return -EPERM; + + /* check args */ + fmt = format_by_fourcc(fb->fmt.pixelformat); + if (NULL == fmt) + return -EINVAL; + + /* ok, accept it */ + dev->ovbuf = *fb; + dev->ovfmt = fmt; + if (0 == dev->ovbuf.fmt.bytesperline) + dev->ovbuf.fmt.bytesperline = + dev->ovbuf.fmt.width*fmt->depth/8; + return 0; + } + case VIDIOC_OVERLAY: + { + int *on = arg; + + if (*on) { + if (!res_get(dev,fh,RESOURCE_OVERLAY)) + return -EBUSY; + spin_lock_irqsave(&dev->slock,flags); + start_preview(dev,fh); + spin_unlock_irqrestore(&dev->slock,flags); + } + if (!*on) { + if (!res_check(fh, RESOURCE_OVERLAY)) + return -EINVAL; + spin_lock_irqsave(&dev->slock,flags); + stop_preview(dev,fh); + spin_unlock_irqrestore(&dev->slock,flags); + res_free(dev,fh,RESOURCE_OVERLAY); + } + return 0; + } + + /* --- capture ioctls ---------------------------------------- */ + case VIDIOC_G_FMT: + { + struct v4l2_format *f = arg; + return saa7134_g_fmt(dev,fh,f); + } + case VIDIOC_S_FMT: + { + struct v4l2_format *f = arg; + return saa7134_s_fmt(dev,fh,f); + } + case VIDIOC_TRY_FMT: + { + struct v4l2_format *f = arg; + return saa7134_try_fmt(dev,fh,f); + } + + case VIDIOCGMBUF: + { + struct video_mbuf *mbuf = arg; + struct videobuf_queue *q; + int i; + + q = saa7134_queue(fh); + down(&q->lock); + err = videobuf_mmap_setup(file,q,gbuffers,gbufsize); + if (err < 0) { + up(&q->lock); + return err; + } + memset(mbuf,0,sizeof(*mbuf)); + mbuf->frames = gbuffers; + mbuf->size = gbuffers * gbufsize; + for (i = 0; i < gbuffers; i++) + mbuf->offsets[i] = i * gbufsize; + up(&q->lock); + return 0; + } + case VIDIOC_REQBUFS: + return videobuf_reqbufs(file,saa7134_queue(fh),arg); + + case VIDIOC_QUERYBUF: + return videobuf_querybuf(saa7134_queue(fh),arg); + + case VIDIOC_QBUF: + return videobuf_qbuf(file,saa7134_queue(fh),arg); + + case VIDIOC_DQBUF: + return videobuf_dqbuf(file,saa7134_queue(fh),arg); + + case VIDIOC_STREAMON: + { + int res = saa7134_resource(fh); + + if (!res_get(dev,fh,res)) + return -EBUSY; + return videobuf_streamon(file,saa7134_queue(fh)); + } + case VIDIOC_STREAMOFF: + { + int res = saa7134_resource(fh); + err = videobuf_streamoff(file,saa7134_queue(fh)); + res_free(dev,fh,res); + return 0; + } + + default: + return v4l_compat_translate_ioctl(inode,file,cmd,arg, + video_do_ioctl); + } + return 0; +} + +static int video_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, video_do_ioctl); +} + +static int radio_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct saa7134_fh *fh = file->private_data; + struct saa7134_dev *dev = fh->dev; + + if (video_debug > 1) + saa7134_print_ioctl(dev->name,cmd); + switch (cmd) { + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *cap = arg; + + memset(cap,0,sizeof(*cap)); + strcpy(cap->driver, "saa7134"); + strncpy(cap->card, saa7134_boards[dev->board].name, + sizeof(cap->card)); + sprintf(cap->bus_info,"PCI:%s",dev->pci->slot_name); + cap->version = SAA7134_VERSION_CODE; + cap->capabilities = V4L2_CAP_TUNER; + return 0; + } + case VIDIOC_G_TUNER: + { + struct v4l2_tuner *t = arg; + struct video_tuner vt; + + memset(t,0,sizeof(*t)); + strcpy(t->name, "Radio"); + t->rangelow = (int)(65*16); + t->rangehigh = (int)(108*16); + + memset(&vt,0,sizeof(vt)); + saa7134_i2c_call_clients(dev,VIDIOCGTUNER,&vt); + t->signal = vt.signal; + return 0; + } + case VIDIOC_ENUMINPUT: + { + struct v4l2_input *i = arg; + + if (i->index != 0) + return -EINVAL; + strcpy(i->name,"Radio"); + i->type = V4L2_INPUT_TYPE_TUNER; + return 0; + } + case VIDIOC_G_INPUT: + { + int *i = arg; + *i = 0; + return 0; + } + case VIDIOC_G_AUDIO: + { + struct v4l2_audio *a = arg; + + memset(a,0,sizeof(*a)); + strcpy(a->name,"Radio"); + return 0; + } + case VIDIOC_S_AUDIO: + case VIDIOC_S_TUNER: + case VIDIOC_S_INPUT: + return 0; + + case VIDIOC_QUERYCTRL: + { + const struct v4l2_queryctrl *ctrl; + struct v4l2_queryctrl *c = arg; + + if (c->id < V4L2_CID_BASE || + c->id >= V4L2_CID_LASTP1) + return -EINVAL; + if (c->id == V4L2_CID_AUDIO_MUTE) { + ctrl = ctrl_by_id(c->id); + *c = *ctrl; + } else + *c = no_ctrl; + return 0; + } + + case VIDIOC_G_CTRL: + case VIDIOC_S_CTRL: + case VIDIOC_G_FREQUENCY: + case VIDIOC_S_FREQUENCY: + return video_do_ioctl(inode,file,cmd,arg); + + default: + return v4l_compat_translate_ioctl(inode,file,cmd,arg, + radio_do_ioctl); + } + return 0; +} + +static int radio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, radio_do_ioctl); +} + +static struct file_operations video_fops = +{ + owner: THIS_MODULE, + open: video_open, + release: video_release, + read: video_read, + poll: video_poll, + mmap: video_mmap, + ioctl: video_ioctl, + llseek: no_llseek, +}; + +static struct file_operations radio_fops = +{ + owner: THIS_MODULE, + open: video_open, + release: video_release, + ioctl: radio_ioctl, + llseek: no_llseek, +}; + +/* ----------------------------------------------------------- */ +/* exported stuff */ + +struct video_device saa7134_video_template = +{ + name: "saa7134-video", + type: VID_TYPE_CAPTURE|VID_TYPE_TUNER|VID_TYPE_OVERLAY| + VID_TYPE_CLIPPING|VID_TYPE_SCALES, + hardware: 0, + fops: &video_fops, + minor: -1, +}; + +struct video_device saa7134_vbi_template = +{ + name: "saa7134-vbi", + type: VID_TYPE_TUNER|VID_TYPE_TELETEXT, + hardware: 0, + fops: &video_fops, + minor: -1, +}; + +struct video_device saa7134_radio_template = +{ + name: "saa7134-radio", + type: VID_TYPE_TUNER, + hardware: 0, + fops: &radio_fops, + minor: -1, +}; + +int saa7134_video_init(struct saa7134_dev *dev) +{ + /* sanitycheck insmod options */ + if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME) + gbuffers = 2; + if (gbufsize < 0 || gbufsize > gbufsize_max) + gbufsize = gbufsize_max; + gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK; + + /* put some sensible defaults into the data structures ... */ + dev->ctl_bright = ctrl_by_id(V4L2_CID_BRIGHTNESS)->default_value; + dev->ctl_contrast = ctrl_by_id(V4L2_CID_CONTRAST)->default_value; + dev->ctl_hue = ctrl_by_id(V4L2_CID_HUE)->default_value; + dev->ctl_saturation = ctrl_by_id(V4L2_CID_SATURATION)->default_value; + dev->ctl_volume = ctrl_by_id(V4L2_CID_AUDIO_VOLUME)->default_value; + + dev->ctl_invert = 0; + dev->ctl_mute = 1; + dev->automute = 0; + + INIT_LIST_HEAD(&dev->video_q.queue); + dev->video_q.timeout.function = saa7134_buffer_timeout; + dev->video_q.timeout.data = (unsigned long)(&dev->video_q); + dev->video_q.dev = dev; + + /* init video hw */ + set_tvnorm(dev,&tvnorms[0]); + video_mux(dev,0); + saa7134_tvaudio_setmute(dev); + saa7134_tvaudio_setvolume(dev,dev->ctl_volume); + + if (saa7134_boards[dev->board].video_out) { + /* enable video output */ + int vo = saa7134_boards[dev->board].video_out; + saa_writeb(SAA7134_VIDEO_PORT_CTRL0, video_out[vo][0]); + saa_writeb(SAA7134_VIDEO_PORT_CTRL1, video_out[vo][1]); + saa_writeb(SAA7134_VIDEO_PORT_CTRL2, video_out[vo][2]); + saa_writeb(SAA7134_VIDEO_PORT_CTRL3, video_out[vo][3]); + saa_writeb(SAA7134_VIDEO_PORT_CTRL4, video_out[vo][4]); + saa_writeb(SAA7134_VIDEO_PORT_CTRL5, video_out[vo][5]); + saa_writeb(SAA7134_VIDEO_PORT_CTRL6, video_out[vo][6]); + saa_writeb(SAA7134_VIDEO_PORT_CTRL7, video_out[vo][7]); + saa_writeb(SAA7134_VIDEO_PORT_CTRL8, video_out[vo][8]); + } + + return 0; +} + +int saa7134_video_fini(struct saa7134_dev *dev) +{ + /* nothing */ + return 0; +} + +void saa7134_irq_video_intl(struct saa7134_dev *dev) +{ + static const char *st[] = { + "no signal", "found NTSC", "found PAL", "found SECAM" }; + int norm; + + norm = saa_readb(SAA7134_STATUS_VIDEO1) & 0x03; + printk("%s/video: DCSDT: %s\n",dev->name,st[norm]); + + if (0 != norm) { + /* wake up tvaudio audio carrier scan thread */ + saa7134_tvaudio_do_scan(dev); + } else { + /* no video signal -> mute audio */ + dev->automute = 1; + saa7134_tvaudio_setmute(dev); + } +} + +void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status) +{ + enum v4l2_field field; + + spin_lock(&dev->slock); + if (dev->video_q.curr) { + field = dev->video_q.curr->vb.field; + + if (V4L2_FIELD_HAS_BOTH(field)) { + /* make sure we have seen both fields */ + if ((status & 0x10) == 0x10) { + dev->video_q.curr->top_seen = 1; + goto done; + } + if (!dev->video_q.curr->top_seen) + goto done; + } else if (field == V4L2_FIELD_TOP) { + if ((status & 0x10) != 0x10) + goto done; + } else if (field == V4L2_FIELD_BOTTOM) { + if ((status & 0x10) != 0x00) + goto done; + } + saa7134_buffer_finish(dev,&dev->video_q,STATE_DONE); + } + saa7134_buffer_next(dev,&dev->video_q); + + done: + spin_unlock(&dev->slock); +} + +/* ----------------------------------------------------------- */ +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/media/video/saa7134/saa7134.h linux-2.5-bk/drivers/media/video/saa7134/saa7134.h --- linux-2.5.45/drivers/media/video/saa7134/saa7134.h Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/drivers/media/video/saa7134/saa7134.h Thu Oct 31 22:12:49 2002 @@ -0,0 +1,473 @@ +/* + * v4l2 device driver for philips saa7134 based TV cards + * + * (c) 2001,02 Gerd Knorr + * + * 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. + */ + +#include +#include +#include +#include +#include "video-buf.h" + +#define SAA7134_VERSION_CODE KERNEL_VERSION(0,2,1) + +#ifndef TRUE +# define TRUE (1==1) +#endif +#ifndef FALSE +# define FALSE (1==0) +#endif + +/* 2.4 / 2.5 driver compatibility stuff */ + +/* ----------------------------------------------------------- */ +/* enums */ + +enum saa7134_tvaudio_mode { + TVAUDIO_FM_MONO = 1, + TVAUDIO_FM_BG_STEREO = 2, + TVAUDIO_FM_SAT_STEREO = 3, + TVAUDIO_FM_K_STEREO = 4, + TVAUDIO_NICAM_AM = 5, + TVAUDIO_NICAM_FM = 6, +}; + +enum saa7134_audio_in { + TV = 1, + LINE1 = 2, + LINE2 = 3, +}; + +enum saa7134_video_out { + CCIR656 = 1, +}; + +/* ----------------------------------------------------------- */ +/* static data */ + +struct saa7134_tvnorm { + char *name; + v4l2_std_id id; + int width; + int height; + + /* video decoder */ + int sync_control; + int luma_control; + int chroma_ctrl1; + int chroma_gain; + int chroma_ctrl2; + + /* video scaler */ + int h_start; + int h_stop; + int video_v_start; + int video_v_stop; + int vbi_v_start; + int vbi_v_stop; +}; + +struct saa7134_tvaudio { + char *name; + int std; + enum saa7134_tvaudio_mode mode; + int carr1; + int carr2; +}; + +struct saa7134_format { + char *name; + int fourcc; + int depth; + int pm; + int vshift; /* vertical downsampling (for planar yuv) */ + int hshift; /* horizontal downsampling (for planar yuv) */ + int bswap:1; + int wswap:1; + int yuv:1; + int planar:1; +}; + +/* ----------------------------------------------------------- */ +/* card configuration */ + +#define SAA7134_BOARD_NOAUTO -1 +#define SAA7134_BOARD_UNKNOWN 0 +#define SAA7134_BOARD_PROTEUS_PRO 1 +#define SAA7134_BOARD_FLYVIDEO3000 2 +#define SAA7134_BOARD_FLYVIDEO2000 3 +#define SAA7134_BOARD_EMPRESS 4 +#define SAA7134_BOARD_MONSTERTV 5 +#define SAA7134_BOARD_MD9717 6 +#define SAA7134_BOARD_TVSTATION_RDS 7 +#define SAA7134_BOARD_CINERGY400 8 +#define SAA7134_BOARD_MD5044 9 + +#define SAA7134_INPUT_MAX 8 + +struct saa7134_input { + char *name; + int vmux; + enum saa7134_audio_in amux; + int gpio; + int tv:1; +}; + +struct saa7134_board { + char *name; + int audio_clock; + + /* input switching */ + int gpiomask; + struct saa7134_input inputs[SAA7134_INPUT_MAX]; + struct saa7134_input radio; + struct saa7134_input mute; + + /* peripheral I/O */ + int i2s_rate; + int has_ts; + enum saa7134_video_out video_out; + + /* i2c chip info */ + int tuner_type; + int need_tda9887:1; +}; + +#define card_has_audio(dev) (dev->pci->device == PCI_DEVICE_ID_PHILIPS_SAA7134) +#define card_has_radio(dev) (NULL != saa7134_boards[dev->board].radio.name) +#define card_has_ts(dev) (saa7134_boards[dev->board].has_ts) +#define card(dev) (saa7134_boards[dev->board]) +#define card_in(dev,n) (saa7134_boards[dev->board].inputs[n]) + +/* ----------------------------------------------------------- */ +/* device / file handle status */ + +#define RESOURCE_OVERLAY 1 +#define RESOURCE_VIDEO 2 +#define RESOURCE_VBI 3 + +#define INTERLACE_AUTO 0 +#define INTERLACE_ON 1 +#define INTERLACE_OFF 2 + +#define BUFFER_TIMEOUT (HZ/2) /* 0.5 seconds */ + +struct saa7134_dev; +struct saa7134_dma; + +/* saa7134 page table */ +struct saa7134_pgtable { + unsigned int size; + u32 *cpu; + dma_addr_t dma; +}; + +/* tvaudio thread status */ +struct saa7134_thread { + struct task_struct *task; + wait_queue_head_t wq; + struct semaphore *notify; + int exit; + int scan1; + int scan2; +}; + +/* buffer for one video/vbi/ts frame */ +struct saa7134_buf { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + + /* saa7134 specific */ + struct saa7134_format *fmt; + int top_seen; + int (*activate)(struct saa7134_dev *dev, + struct saa7134_buf *buf, + struct saa7134_buf *next); + + /* page tables */ + struct saa7134_pgtable *pt; +}; + +struct saa7134_dmaqueue { + struct saa7134_dev *dev; + struct saa7134_buf *curr; + struct list_head queue; + struct timer_list timeout; +}; + +/* video filehandle status */ +struct saa7134_fh { + struct saa7134_dev *dev; + int radio; + enum v4l2_buf_type type; + + struct v4l2_window win; + struct v4l2_clip clips[8]; + int nclips; + int resources; + + /* video capture */ + struct saa7134_format *fmt; + int width,height; + enum v4l2_field field; + struct videobuf_queue cap; + struct saa7134_pgtable pt_cap; + + /* vbi capture */ + struct videobuf_queue vbi; + struct saa7134_pgtable pt_vbi; +}; + +/* TS status */ +struct saa7134_ts { + int users; + + /* TS capture */ + struct videobuf_queue ts; + struct saa7134_pgtable pt_ts; +}; + +/* oss dsp status */ +struct saa7134_oss { + struct semaphore lock; + int minor_mixer; + int minor_dsp; + int users_dsp; + + /* mixer */ + enum saa7134_audio_in input; + int count; + int line1; + int line2; + + /* dsp */ + int afmt; + int rate; + int channels; + int recording; + int blocks; + int blksize; + int bufsize; + struct saa7134_pgtable pt; + struct videobuf_dmabuf dma; + wait_queue_head_t wq; + int dma_blk; + int read_offset; + int read_count; +}; + +/* global device status */ +struct saa7134_dev { + struct list_head devlist; + struct semaphore lock; + spinlock_t slock; + + /* various device info */ + int resources; + struct video_device video_dev; + struct video_device ts_dev; + struct video_device radio_dev; + struct video_device vbi_dev; + struct saa7134_oss oss; + struct saa7134_ts ts; + + /* pci i/o */ + char name[32]; + struct pci_dev *pci; + unsigned char pci_rev,pci_lat; + __u32 *lmmio; + __u8 *bmmio; + + /* config info */ + int board; + int tuner_type; + + /* i2c i/o */ + struct i2c_adapter i2c_adap; + struct i2c_client i2c_client; + unsigned char eedata[64]; + + /* video overlay */ + struct v4l2_framebuffer ovbuf; + struct saa7134_format *ovfmt; + int ovenable; + enum v4l2_field ovfield; + + /* video+ts+vbi capture */ + struct saa7134_dmaqueue video_q; + struct saa7134_dmaqueue ts_q; + struct saa7134_dmaqueue vbi_q; + int vbi_fieldcount; + + /* various v4l controls */ + struct saa7134_tvnorm *tvnorm; /* video */ + struct saa7134_tvaudio *tvaudio; + int ctl_input; + int ctl_bright; + int ctl_contrast; + int ctl_hue; + int ctl_saturation; + int ctl_freq; + int ctl_mute; /* audio */ + int ctl_volume; + int ctl_invert; /* private */ + int ctl_mirror; + int ctl_y_odd; + int ctl_y_even; + + /* other global state info */ + int automute; + struct saa7134_thread thread; + struct saa7134_input *input; + struct saa7134_input *hw_input; + int hw_mute; +}; + +/* ----------------------------------------------------------- */ + +#define saa_readl(reg) readl(dev->lmmio + (reg)) +#define saa_writel(reg,value) writel((value), dev->lmmio + (reg)); +#define saa_andorl(reg,mask,value) \ + writel((readl(dev->lmmio+(reg)) & ~(mask)) |\ + ((value) & (mask)), dev->lmmio+(reg)) +#define saa_setl(reg,bit) saa_andorl((reg),(bit),(bit)) +#define saa_clearl(reg,bit) saa_andorl((reg),(bit),0) + +#define saa_readb(reg) readb(dev->bmmio + (reg)) +#define saa_writeb(reg,value) writeb((value), dev->bmmio + (reg)); +#define saa_andorb(reg,mask,value) \ + writeb((readb(dev->bmmio+(reg)) & ~(mask)) |\ + ((value) & (mask)), dev->bmmio+(reg)) +#define saa_setb(reg,bit) saa_andorb((reg),(bit),(bit)) +#define saa_clearb(reg,bit) saa_andorb((reg),(bit),0) + + +/* ----------------------------------------------------------- */ +/* saa7134-core.c */ + +extern struct list_head saa7134_devlist; +extern int saa7134_devcount; + +void saa7134_print_ioctl(char *name, unsigned int cmd); +void saa7134_track_gpio(struct saa7134_dev *dev, char *msg); + +#define SAA7134_PGTABLE_SIZE 4096 + +int saa7134_pgtable_alloc(struct pci_dev *pci, struct saa7134_pgtable *pt); +int saa7134_pgtable_build(struct pci_dev *pci, struct saa7134_pgtable *pt, + struct scatterlist *list, int length, + int startpage); +void saa7134_pgtable_free(struct pci_dev *pci, struct saa7134_pgtable *pt); + +int saa7134_buffer_count(int size, int count); +int saa7134_buffer_startpage(struct saa7134_buf *buf); +unsigned long saa7134_buffer_base(struct saa7134_buf *buf); + +int saa7134_buffer_queue(struct saa7134_dev *dev, struct saa7134_dmaqueue *q, + struct saa7134_buf *buf); +void saa7134_buffer_finish(struct saa7134_dev *dev, struct saa7134_dmaqueue *q, + int state); +void saa7134_buffer_next(struct saa7134_dev *dev, struct saa7134_dmaqueue *q); +void saa7134_buffer_timeout(unsigned long data); +void saa7134_dma_free(struct saa7134_dev *dev,struct saa7134_buf *buf); + +int saa7134_set_dmabits(struct saa7134_dev *dev); + +/* ----------------------------------------------------------- */ +/* saa7134-cards.c */ + +extern struct saa7134_board saa7134_boards[]; +extern const int saa7134_bcount; +extern struct pci_device_id __devinitdata saa7134_pci_tbl[]; + + +/* ----------------------------------------------------------- */ +/* saa7134-i2c.c */ + +int saa7134_i2c_register(struct saa7134_dev *dev); +int saa7134_i2c_unregister(struct saa7134_dev *dev); +void saa7134_i2c_call_clients(struct saa7134_dev *dev, + unsigned int cmd, void *arg); + + +/* ----------------------------------------------------------- */ +/* saa7134-video.c */ + +extern struct video_device saa7134_video_template; +extern struct video_device saa7134_radio_template; + +int saa7134_common_ioctl(struct saa7134_dev *dev, + unsigned int cmd, void *arg); + +int saa7134_video_init(struct saa7134_dev *dev); +int saa7134_video_fini(struct saa7134_dev *dev); +void saa7134_irq_video_intl(struct saa7134_dev *dev); +void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status); + + +/* ----------------------------------------------------------- */ +/* saa7134-ts.c */ + +extern struct video_device saa7134_ts_template; +int saa7134_ts_init(struct saa7134_dev *dev); +int saa7134_ts_fini(struct saa7134_dev *dev); +void saa7134_irq_ts_done(struct saa7134_dev *dev, unsigned long status); + + +/* ----------------------------------------------------------- */ +/* saa7134-vbi.c */ + +extern struct videobuf_queue_ops saa7134_vbi_qops; +extern struct video_device saa7134_vbi_template; + +int saa7134_vbi_init(struct saa7134_dev *dev); +int saa7134_vbi_fini(struct saa7134_dev *dev); +void saa7134_irq_vbi_done(struct saa7134_dev *dev, unsigned long status); + + +/* ----------------------------------------------------------- */ +/* saa7134-tvaudio.c */ + +void saa7134_tvaudio_setmute(struct saa7134_dev *dev); +void saa7134_tvaudio_setinput(struct saa7134_dev *dev, + struct saa7134_input *in); +void saa7134_tvaudio_setvolume(struct saa7134_dev *dev, int level); +int saa7134_tvaudio_getstereo(struct saa7134_dev *dev, + struct saa7134_tvaudio *audio); + +int saa7134_tvaudio_init(struct saa7134_dev *dev); +int saa7134_tvaudio_fini(struct saa7134_dev *dev); +int saa7134_tvaudio_do_scan(struct saa7134_dev *dev); + + +/* ----------------------------------------------------------- */ +/* saa7134-oss.c */ + +extern struct file_operations saa7134_dsp_fops; +extern struct file_operations saa7134_mixer_fops; + +int saa7134_oss_init(struct saa7134_dev *dev); +int saa7134_oss_fini(struct saa7134_dev *dev); +void saa7134_irq_oss_done(struct saa7134_dev *dev, unsigned long status); + + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/media/video/tuner.c linux-2.5-bk/drivers/media/video/tuner.c --- linux-2.5.45/drivers/media/video/tuner.c Wed Oct 30 16:43:39 2002 +++ linux-2.5-bk/drivers/media/video/tuner.c Thu Oct 31 22:12:49 2002 @@ -101,10 +101,16 @@ /* system switching for Philips FI1216MF MK2 from datasheet "1996 Jul 09", + standard BG L L' + picture carrier 38.90 38.90 33.95 + colour 34.47 34.37 38.38 + sound 1 33.40 32.40 40.45 + sound 2 33.16 - - + NICAM 33.05 33.05 39.80 */ #define PHILIPS_MF_SET_BG 0x01 /* Bit 2 must be zero, Bit 3 is system output */ -#define PHILIPS_MF_SET_PAL_L 0x03 -#define PHILIPS_MF_SET_PAL_L2 0x02 +#define PHILIPS_MF_SET_PAL_L 0x03 // France +#define PHILIPS_MF_SET_PAL_L2 0x02 // L' /* ---------------------------------------------------------------------- */ @@ -121,7 +127,9 @@ unsigned char VHF_H; unsigned char UHF; unsigned char config; - unsigned short IFPCoff; /* 622.4=16*38.90 MHz PAL, 732=16*45.75 NTSC */ + unsigned short IFPCoff; /* 622.4=16*38.90 MHz PAL, + 732 =16*45.75 NTSCi, + 940 =58.75 NTSC-Japan */ }; /* @@ -132,16 +140,16 @@ static struct tunertype tuners[] = { { "Temic PAL (4002 FH5)", TEMIC, PAL, 16*140.25,16*463.25,0x02,0x04,0x01,0x8e,623}, - { "Philips PAL_I", Philips, PAL_I, + { "Philips PAL_I (FI1246 and compatibles)", Philips, PAL_I, 16*140.25,16*463.25,0xa0,0x90,0x30,0x8e,623}, - { "Philips NTSC", Philips, NTSC, + { "Philips NTSC (FI1236 and compatibles)", Philips, NTSC, 16*157.25,16*451.25,0xA0,0x90,0x30,0x8e,732}, - { "Philips SECAM", Philips, SECAM, + { "Philips (SECAM+PAL_BG) (FI1216MF, FM1216MF, FR1216MF)", Philips, SECAM, 16*168.25,16*447.25,0xA7,0x97,0x37,0x8e,623}, { "NoTuner", NoTuner, NOTUNER, 0,0,0x00,0x00,0x00,0x00,0x00}, - { "Philips PAL", Philips, PAL, + { "Philips PAL_BG (FI1216 and compatibles)", Philips, PAL, 16*168.25,16*447.25,0xA0,0x90,0x30,0x8e,623}, { "Temic NTSC (4032 FY5)", TEMIC, NTSC, 16*157.25,16*463.25,0x02,0x04,0x01,0x8e,732}, @@ -181,7 +189,7 @@ 16*158.00, 16*453.00, 0xa0,0x90,0x30,0x8e,732}, { "Temic PAL/SECAM multi (4046 FM5)", TEMIC, PAL, 16*169.00, 16*454.00, 0xa0,0x90,0x30,0x8e,623}, - { "Philips PAL_DK", Philips, PAL, + { "Philips PAL_DK (FI1256 and compatibles)", Philips, PAL, 16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,623}, { "Philips PAL/SECAM multi (FQ1216ME)", Philips, PAL, @@ -200,7 +208,7 @@ { "Temic PAL* auto + FM (4009 FN5)", TEMIC, PAL, 16*141.00, 16*464.00, 0xa0,0x90,0x30,0x8e,623}, { "SHARP NTSC_JP (2U5JF5540)", SHARP, NTSC, /* 940=16*58.75 NTSC@Japan */ - 16*137.25,16*317.25,0x01,0x02,0x08,0x8e,940}, + 16*137.25,16*317.25,0x01,0x02,0x08,0x8e,732 }, // Corrected to NTSC=732 (was:940) { "Samsung PAL TCPM9091PD27", Samsung, PAL, /* from sourceforge v3tv */ 16*169,16*464,0xA0,0x90,0x30,0x8e,623}, @@ -215,8 +223,10 @@ 16*158.00, 16*453.00, 0xa0,0x90,0x30,0x8e,732}, { "LG PAL (newer TAPC series)", LGINNOTEK, PAL, 16*170.00, 16*450.00, 0x01,0x02,0x08,0x8e,623}, - { "Philips PAL/SECAM multi (FM1216ME)", Philips, PAL, + { "Philips PAL/SECAM multi (FM1216ME MK3)", Philips, PAL, 16*160.00,16*442.00,0x01,0x02,0x04,0x8e,623 }, + { "LG NTSC (newer TAPC series)", LGINNOTEK, NTSC, + 16*170.00, 16*450.00, 0x01,0x02,0x08,0x8e,732}, }; #define TUNERS (sizeof(tuners)/sizeof(struct tunertype)) @@ -295,6 +305,13 @@ } printk("\n "); } + // Look for MT2032 id: + // part= 0x04(MT2032), 0x06(MT2030), 0x07(MT2040) + if((buf[0x11] != 0x4d) || (buf[0x12] != 0x54) || (buf[0x13] != 0x04)) { + printk("not a MT2032.\n"); + return 0; + } + // Initialize Registers per spec. buf[1]=2; // Index to register 2 @@ -621,7 +638,7 @@ /* tv norm specific stuff for multi-norm tuners */ switch (t->type) { - case TUNER_PHILIPS_SECAM: + case TUNER_PHILIPS_SECAM: // FI1216MF /* 0x01 -> ??? no change ??? */ /* 0x02 -> PAL BDGHI / SECAM L */ /* 0x04 -> ??? PAL others / SECAM others ??? */ @@ -751,7 +768,7 @@ buffer[1] = div & 0xff; buffer[2] = tun->config; switch (t->type) { - case TUNER_PHILIPS_FM1216ME: + case TUNER_PHILIPS_FM1216ME_MK3: buffer[3] = 0x19; break; default: @@ -857,12 +874,14 @@ /* --- configuration --- */ case TUNER_SET_TYPE: - if (t->type != -1) + if (t->type != -1) { + printk("tuner: type already set\n"); return 0; + } if (*iarg < 0 || *iarg >= TUNERS) return 0; t->type = *iarg; - dprintk("tuner: type set to %d (%s)\n", + printk("tuner: type set to %d (%s)\n", t->type,tuners[t->type].name); strncpy(client->name, tuners[t->type].name, sizeof(client->name)); if (t->type == TUNER_MT2032) @@ -965,7 +984,7 @@ }; static struct i2c_client client_template = { - name: "(unset)", + name: "(tuner unset)", flags: I2C_CLIENT_ALLOW_USE, driver: &driver, }; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/media/video/tuner.h linux-2.5-bk/drivers/media/video/tuner.h --- linux-2.5.45/drivers/media/video/tuner.h Wed Oct 30 16:43:08 2002 +++ linux-2.5-bk/drivers/media/video/tuner.h Thu Oct 31 22:12:49 2002 @@ -62,7 +62,9 @@ #define TUNER_TEMIC_4012FY5 35 /* 4012 FY5 (3X 0971, 1099)*/ #define TUNER_TEMIC_4136FY5 36 /* 4136 FY5 (3X 7708, 7746)*/ #define TUNER_LG_PAL_NEW_TAPC 37 -#define TUNER_PHILIPS_FM1216ME 38 +#define TUNER_PHILIPS_FM1216ME_MK3 38 +#define TUNER_LG_NTSC_NEW_TAPC 39 + @@ -84,7 +86,9 @@ #define TUNER_SET_TYPE _IOW('t',1,int) /* set tuner type */ #define TUNER_SET_TVFREQ _IOW('t',2,int) /* set tv freq */ -#define TUNER_SET_RADIOFREQ _IOW('t',3,int) /* set radio freq */ -#define TUNER_SET_MODE _IOW('t',4,int) /* set tuner mode */ +#if 0 /* obsolete */ +# define TUNER_SET_RADIOFREQ _IOW('t',3,int) /* set radio freq */ +# define TUNER_SET_MODE _IOW('t',4,int) /* set tuner mode */ +#endif #endif diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/media/video/v4l2-common.c linux-2.5-bk/drivers/media/video/v4l2-common.c --- linux-2.5.45/drivers/media/video/v4l2-common.c Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/drivers/media/video/v4l2-common.c Thu Oct 31 22:12:49 2002 @@ -0,0 +1,211 @@ +/* + * Video for Linux Two + * + * A generic video device interface for the LINUX operating system + * using a set of device structures/vectors for low level operations. + * + * This file replaces the videodev.c file that comes with the + * regular kernel distribution. + * + * 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. + * + * Author: Bill Dirks + * based on code by Alan Cox, + * + */ + +/* + * Video capture interface for Linux + * + * A generic video device interface for the LINUX operating system + * using a set of device structures/vectors for low level operations. + * + * 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. + * + * Author: Alan Cox, + * + * Fixes: + */ + +/* + * Video4linux 1/2 integration by Justin Schoeman + * + * 2.4 PROCFS support ported from 2.4 kernels by + * Iñaki García Etxebarria + * Makefile fix by "W. Michael Petullo" + * 2.4 devfs support ported from 2.4 kernels by + * Dan Merillat + * Added Gerd Knorrs v4l1 enhancements (Justin Schoeman) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_KMOD +#include +#endif + +#if defined(CONFIG_UST) || defined(CONFIG_UST_MODULE) +#include +#endif + +#include + +MODULE_AUTHOR("Bill Dirks, Justin Schoeman, Gerd Knorr"); +MODULE_DESCRIPTION("misc helper functions for v4l2 device drivers"); +MODULE_LICENSE("GPL"); + +/* + * + * V 4 L 2 D R I V E R H E L P E R A P I + * + */ + +/* + * Video Standard Operations (contributed by Michael Schimek) + */ + +/* This is the recommended method to deal with the framerate fields. More + sophisticated drivers will access the fields directly. */ +unsigned int +v4l2_video_std_fps(struct v4l2_standard *vs) +{ + if (vs->frameperiod.numerator > 0) + return (((vs->frameperiod.denominator << 8) / + vs->frameperiod.numerator) + + (1 << 7)) / (1 << 8); + return 0; +} + +/* Fill in the fields of a v4l2_standard structure according to the + 'id' and 'transmission' parameters. Returns negative on error. */ +int v4l2_video_std_construct(struct v4l2_standard *vs, + int id, char *name) +{ + memset(vs, 0, sizeof(struct v4l2_standard)); + + vs->id = id; + if (id & (V4L2_STD_NTSC | V4L2_STD_PAL_M)) { + vs->frameperiod.numerator = 1001; + vs->frameperiod.denominator = 30000; + vs->framelines = 525; + } else { + vs->frameperiod.numerator = 1; + vs->frameperiod.denominator = 25; + vs->framelines = 625; + } + strncpy(vs->name,name,sizeof(vs->name)); + return 0; +} + + +/* ----------------------------------------------------------------- */ + +char *v4l2_field_names[] = { + [V4L2_FIELD_ANY] = "any", + [V4L2_FIELD_NONE] = "none", + [V4L2_FIELD_TOP] = "top", + [V4L2_FIELD_BOTTOM] = "bottom", + [V4L2_FIELD_INTERLACED] = "interlaced", + [V4L2_FIELD_SEQ_TB] = "seq-tb", + [V4L2_FIELD_SEQ_BT] = "seq-bt", + [V4L2_FIELD_ALTERNATE] = "alternate", +}; + +char *v4l2_type_names[] = { + [V4L2_BUF_TYPE_VIDEO_CAPTURE] = "video-cap", + [V4L2_BUF_TYPE_VIDEO_OVERLAY] = "video-over", + [V4L2_BUF_TYPE_VIDEO_OUTPUT] = "video-out", + [V4L2_BUF_TYPE_VBI_CAPTURE] = "vbi-cap", + [V4L2_BUF_TYPE_VBI_OUTPUT] = "vbi-out", +}; + +char *v4l2_ioctl_names[256] = { +#if __GNUC__ >= 3 + [0 ... 255] = "UNKNOWN", +#endif + [_IOC_NR(VIDIOC_QUERYCAP)] = "VIDIOC_QUERYCAP", + [_IOC_NR(VIDIOC_RESERVED)] = "VIDIOC_RESERVED", + [_IOC_NR(VIDIOC_ENUM_FMT)] = "VIDIOC_ENUM_FMT", + [_IOC_NR(VIDIOC_G_FMT)] = "VIDIOC_G_FMT", + [_IOC_NR(VIDIOC_S_FMT)] = "VIDIOC_S_FMT", +#if 0 + [_IOC_NR(VIDIOC_G_COMP)] = "VIDIOC_G_COMP", + [_IOC_NR(VIDIOC_S_COMP)] = "VIDIOC_S_COMP", +#endif + [_IOC_NR(VIDIOC_REQBUFS)] = "VIDIOC_REQBUFS", + [_IOC_NR(VIDIOC_QUERYBUF)] = "VIDIOC_QUERYBUF", + [_IOC_NR(VIDIOC_G_FBUF)] = "VIDIOC_G_FBUF", + [_IOC_NR(VIDIOC_S_FBUF)] = "VIDIOC_S_FBUF", + [_IOC_NR(VIDIOC_OVERLAY)] = "VIDIOC_OVERLAY", + [_IOC_NR(VIDIOC_QBUF)] = "VIDIOC_QBUF", + [_IOC_NR(VIDIOC_DQBUF)] = "VIDIOC_DQBUF", + [_IOC_NR(VIDIOC_STREAMON)] = "VIDIOC_STREAMON", + [_IOC_NR(VIDIOC_STREAMOFF)] = "VIDIOC_STREAMOFF", + [_IOC_NR(VIDIOC_G_PARM)] = "VIDIOC_G_PARM", + [_IOC_NR(VIDIOC_S_PARM)] = "VIDIOC_S_PARM", + [_IOC_NR(VIDIOC_G_STD)] = "VIDIOC_G_STD", + [_IOC_NR(VIDIOC_S_STD)] = "VIDIOC_S_STD", + [_IOC_NR(VIDIOC_ENUMSTD)] = "VIDIOC_ENUMSTD", + [_IOC_NR(VIDIOC_ENUMINPUT)] = "VIDIOC_ENUMINPUT", + [_IOC_NR(VIDIOC_G_CTRL)] = "VIDIOC_G_CTRL", + [_IOC_NR(VIDIOC_S_CTRL)] = "VIDIOC_S_CTRL", + [_IOC_NR(VIDIOC_G_TUNER)] = "VIDIOC_G_TUNER", + [_IOC_NR(VIDIOC_S_TUNER)] = "VIDIOC_S_TUNER", + [_IOC_NR(VIDIOC_G_AUDIO)] = "VIDIOC_G_AUDIO", + [_IOC_NR(VIDIOC_S_AUDIO)] = "VIDIOC_S_AUDIO", + [_IOC_NR(VIDIOC_QUERYCTRL)] = "VIDIOC_QUERYCTRL", + [_IOC_NR(VIDIOC_QUERYMENU)] = "VIDIOC_QUERYMENU", + [_IOC_NR(VIDIOC_G_INPUT)] = "VIDIOC_G_INPUT", + [_IOC_NR(VIDIOC_S_INPUT)] = "VIDIOC_S_INPUT", + [_IOC_NR(VIDIOC_G_OUTPUT)] = "VIDIOC_G_OUTPUT", + [_IOC_NR(VIDIOC_S_OUTPUT)] = "VIDIOC_S_OUTPUT", + [_IOC_NR(VIDIOC_ENUMOUTPUT)] = "VIDIOC_ENUMOUTPUT", + [_IOC_NR(VIDIOC_G_AUDOUT)] = "VIDIOC_G_AUDOUT", + [_IOC_NR(VIDIOC_S_AUDOUT)] = "VIDIOC_S_AUDOUT", + [_IOC_NR(VIDIOC_G_MODULATOR)] = "VIDIOC_G_MODULATOR", + [_IOC_NR(VIDIOC_S_MODULATOR)] = "VIDIOC_S_MODULATOR", + [_IOC_NR(VIDIOC_G_FREQUENCY)] = "VIDIOC_G_FREQUENCY", + [_IOC_NR(VIDIOC_S_FREQUENCY)] = "VIDIOC_S_FREQUENCY", + [_IOC_NR(VIDIOC_CROPCAP)] = "VIDIOC_CROPCAP", + [_IOC_NR(VIDIOC_G_CROP)] = "VIDIOC_G_CROP", + [_IOC_NR(VIDIOC_S_CROP)] = "VIDIOC_S_CROP", + [_IOC_NR(VIDIOC_G_JPEGCOMP)] = "VIDIOC_G_JPEGCOMP", + [_IOC_NR(VIDIOC_S_JPEGCOMP)] = "VIDIOC_S_JPEGCOMP", + [_IOC_NR(VIDIOC_QUERYSTD)] = "VIDIOC_QUERYSTD", + [_IOC_NR(VIDIOC_TRY_FMT)] = "VIDIOC_TRY_FMT", +}; + +/* ----------------------------------------------------------------- */ + +EXPORT_SYMBOL(v4l2_video_std_fps); +EXPORT_SYMBOL(v4l2_video_std_construct); + +EXPORT_SYMBOL(v4l2_field_names); +EXPORT_SYMBOL(v4l2_type_names); +EXPORT_SYMBOL(v4l2_ioctl_names); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/media/video/video-buf.c linux-2.5-bk/drivers/media/video/video-buf.c --- linux-2.5.45/drivers/media/video/video-buf.c Wed Oct 30 16:42:56 2002 +++ linux-2.5-bk/drivers/media/video/video-buf.c Thu Oct 31 22:12:49 2002 @@ -26,6 +26,11 @@ #include #include +#ifndef TryLockPage +# include "linux/page-flags.h" +# define TryLockPage TestSetPageLocked +#endif + #include "video-buf.h" static int debug = 0; @@ -65,12 +70,12 @@ return NULL; } -struct scatterlist * +struct scatterlist* videobuf_pages_to_sg(struct page **pages, int nr_pages, int offset) { struct scatterlist *sglist; int i = 0; - + if (NULL == pages[0]) return NULL; sglist = kmalloc(sizeof(*sglist) * nr_pages, GFP_KERNEL); @@ -80,21 +85,27 @@ if (PageHighMem(pages[0])) /* DMA to highmem pages might not work */ - goto err; + goto highmem; sglist[0].page = pages[0]; sglist[0].offset = offset; sglist[0].length = PAGE_SIZE - offset; for (i = 1; i < nr_pages; i++) { if (NULL == pages[i]) - goto err; + goto nopage; if (PageHighMem(pages[i])) - goto err; + goto highmem; sglist[i].page = pages[i]; sglist[i].length = PAGE_SIZE; } return sglist; - err: + nopage: + dprintk(2,"sgl: oops - no page\n"); + kfree(sglist); + return NULL; + + highmem: + dprintk(2,"sgl: oops - highmem page\n"); kfree(sglist); return NULL; } @@ -103,14 +114,18 @@ { int i; + dprintk(2,"lock start ...\n"); for (i = 0; i < nr_pages; i++) - if (TestSetPageLocked(pages[i])) + if (TryLockPage(pages[i])) goto err; + dprintk(2,"lock ok\n"); return 0; err: + dprintk(2,"lock failed, unlock ...\n"); while (i > 0) unlock_page(pages[--i]); + dprintk(2,"lock quit\n"); return -EINVAL; } @@ -118,8 +133,10 @@ { int i; + dprintk(2,"unlock start ...\n"); for (i = 0; i < nr_pages; i++) unlock_page(pages[i]); + dprintk(2,"unlock ok\n"); return 0; } @@ -128,6 +145,7 @@ int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction, unsigned long data, unsigned long size) { + unsigned long first,last; int err, rw = 0; dma->direction = direction; @@ -137,25 +155,35 @@ default: BUG(); } - dma->offset = data & PAGE_MASK; - dma->nr_pages = ((((data+size) & ~PAGE_MASK) - - (data & ~PAGE_MASK)) >> PAGE_SHIFT) +1; - dma->pages = kmalloc(dma->nr_pages * sizeof(struct page*), - GFP_KERNEL); + first = (data & PAGE_MASK) >> PAGE_SHIFT; + last = ((data+size-1) & PAGE_MASK) >> PAGE_SHIFT; + dma->offset = data & ~PAGE_MASK; + dma->nr_pages = last-first+1; + dma->pages = kmalloc(dma->nr_pages * sizeof(struct page*), + GFP_KERNEL); if (NULL == dma->pages) return -ENOMEM; + dprintk(1,"init user [0x%lx+0x%lx => %d pages]\n", + data,size,dma->nr_pages); + down_read(¤t->mm->mmap_sem); err = get_user_pages(current,current->mm, - data, dma->nr_pages, - rw == READ, 0, /* don't force */ + data & PAGE_MASK, dma->nr_pages, + rw == READ, 1, /* force */ dma->pages, NULL); up_read(¤t->mm->mmap_sem); - return err; + if (err != dma->nr_pages) { + dma->nr_pages = (err >= 0) ? err : 0; + dprintk(1,"get_user_pages: err=%d [%d]\n",err,dma->nr_pages); + return err < 0 ? err : -EINVAL; + } + return 0; } int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, int nr_pages) { + dprintk(1,"init kernel [%d pages]\n",nr_pages); dma->direction = direction; dma->vmalloc = vmalloc_32(nr_pages << PAGE_SHIFT); if (NULL == dma->vmalloc) { @@ -176,13 +204,14 @@ if (dma->pages) { if (0 != (err = videobuf_lock(dma->pages, dma->nr_pages))) { - dprintk(1,"videobuf_lock_pages: %d\n",err); + dprintk(1,"videobuf_lock: %d\n",err); return err; } dma->sglist = videobuf_pages_to_sg(dma->pages, dma->nr_pages, dma->offset); + if (NULL == dma->sglist) + videobuf_unlock(dma->pages, dma->nr_pages); } - if (dma->vmalloc) { dma->sglist = videobuf_vmalloc_to_sg (dma->vmalloc,dma->nr_pages); @@ -215,7 +244,7 @@ dma->sglist = NULL; dma->sglen = 0; if (dma->pages) - videobuf_lock(dma->pages, dma->nr_pages); + videobuf_unlock(dma->pages, dma->nr_pages); return 0; } @@ -231,7 +260,6 @@ kfree(dma->pages); dma->pages = NULL; } - if (dma->vmalloc) { vfree(dma->vmalloc); dma->vmalloc = NULL; @@ -286,16 +314,12 @@ if (0 == vb->baddr) { /* no userspace addr -- kernel bounce buffer */ pages = PAGE_ALIGN(vb->size) >> PAGE_SHIFT; - dprintk(1,"kernel buf size=%ld (%d pages)\n", - vb->size,pages); err = videobuf_dma_init_kernel(&vb->dma,PCI_DMA_FROMDEVICE, pages); if (0 != err) return err; } else { /* dma directly to userspace */ - dprintk(1,"user buf addr=%08lx size=%ld\n", - vb->baddr,vb->bsize); err = videobuf_dma_init_user(&vb->dma,PCI_DMA_FROMDEVICE, vb->baddr,vb->bsize); if (0 != err) @@ -314,7 +338,7 @@ struct videobuf_queue_ops *ops, struct pci_dev *pci, spinlock_t *irqlock, - int type, + enum v4l2_buf_type type, int msize) { memset(q,0,sizeof(*q)); @@ -329,6 +353,30 @@ INIT_LIST_HEAD(&q->stream); } +int +videobuf_queue_is_busy(struct videobuf_queue *q) +{ + int i; + + if (q->reading) + return 1; + if (q->streaming) + return 1; + if (q->read_buf) + return 1; + for (i = 0; i < VIDEO_MAX_FRAME; i++) { + if (NULL == q->bufs[i]) + continue; + if (q->bufs[i]->map) + return 1; + if (q->bufs[i]->state == STATE_QUEUED) + return 1; + if (q->bufs[i]->state == STATE_ACTIVE) + return 1; + } + return 0; +} + void videobuf_queue_cancel(struct file *file, struct videobuf_queue *q) { @@ -358,15 +406,15 @@ /* --------------------------------------------------------------------- */ -#ifdef HAVE_V4L2 void -videobuf_status(struct v4l2_buffer *b, struct videobuf_buffer *vb, int type) +videobuf_status(struct v4l2_buffer *b, struct videobuf_buffer *vb, + enum v4l2_buf_type type) { - b->index = vb->i; - b->type = type; - b->offset = vb->boff; - b->length = vb->bsize; - b->flags = 0; + b->index = vb->i; + b->type = type; + b->m.offset = vb->boff; + b->length = vb->bsize; + b->flags = 0; if (vb->map) b->flags |= V4L2_BUF_FLAG_MAPPED; switch (vb->state) { @@ -384,12 +432,7 @@ /* nothing */ break; } - if (!(vb->field & VBUF_FIELD_INTER)) { - if (vb->field & VBUF_FIELD_ODD) - b->flags |= V4L2_BUF_FLAG_TOPFIELD; - if (vb->field & VBUF_FIELD_EVEN) - b->flags |= V4L2_BUF_FLAG_BOTFIELD; - } + b->field = vb->field; b->timestamp = vb->ts; b->bytesused = vb->size; b->sequence = vb->field_count >> 1; @@ -401,7 +444,7 @@ { int size,count,retval; - if ((req->type & V4L2_BUF_TYPE_field) != q->type) + if (req->type != q->type) return -EINVAL; if (req->count < 1) return -EINVAL; @@ -428,11 +471,11 @@ int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b) { - if ((b->type & V4L2_BUF_TYPE_field) != q->type) + if (unlikely(b->type != q->type)) return -EINVAL; - if (b->index < 0 || b->index >= VIDEO_MAX_FRAME) + if (unlikely(b->index < 0 || b->index >= VIDEO_MAX_FRAME)) return -EINVAL; - if (NULL == q->bufs[b->index]) + if (unlikely(NULL == q->bufs[b->index])) return -EINVAL; videobuf_status(b,q->bufs[b->index],q->type); return 0; @@ -444,7 +487,6 @@ { struct videobuf_buffer *buf; unsigned long flags; - int field = 0; int retval; down(&q->lock); @@ -452,7 +494,7 @@ if (q->reading) goto done; retval = -EINVAL; - if ((b->type & V4L2_BUF_TYPE_field) != q->type) + if (b->type != q->type) goto done; if (b->index < 0 || b->index >= VIDEO_MAX_FRAME) goto done; @@ -465,11 +507,7 @@ buf->state == STATE_ACTIVE) goto done; - if (b->flags & V4L2_BUF_FLAG_TOPFIELD) - field |= VBUF_FIELD_ODD; - if (b->flags & V4L2_BUF_FLAG_BOTFIELD) - field |= VBUF_FIELD_EVEN; - retval = q->ops->buf_prepare(file,buf,field); + retval = q->ops->buf_prepare(file,buf); if (0 != retval) goto done; @@ -498,7 +536,7 @@ if (q->reading) goto done; retval = -EINVAL; - if ((b->type & V4L2_BUF_TYPE_field) != q->type) + if (b->type != q->type) goto done; if (list_empty(&q->stream)) goto done; @@ -526,7 +564,6 @@ up(&q->lock); return retval; } -#endif /* HAVE_V4L2 */ int videobuf_streamon(struct file *file, struct videobuf_queue *q) { @@ -586,7 +623,7 @@ q->read_buf->baddr = (unsigned long)data; q->read_buf->bsize = count; - retval = q->ops->buf_prepare(file,q->read_buf,0); + retval = q->ops->buf_prepare(file,q->read_buf); if (0 != retval) goto done; @@ -631,7 +668,7 @@ q->read_buf = videobuf_alloc(q->msize); if (NULL == q->read_buf) goto done; - retval = q->ops->buf_prepare(file,q->read_buf,0); + retval = q->ops->buf_prepare(file,q->read_buf); if (0 != retval) goto done; q->ops->buf_queue(file,q->read_buf); @@ -683,7 +720,7 @@ if (err) return err; for (i = 0; i < count; i++) { - err = q->ops->buf_prepare(file,q->bufs[i],0); + err = q->ops->buf_prepare(file,q->bufs[i]); if (err) return err; list_add_tail(&q->bufs[i]->stream, &q->stream); @@ -1008,6 +1045,8 @@ /* --------------------------------------------------------------------- */ EXPORT_SYMBOL_GPL(videobuf_vmalloc_to_sg); +EXPORT_SYMBOL_GPL(videobuf_lock); +EXPORT_SYMBOL_GPL(videobuf_unlock); EXPORT_SYMBOL_GPL(videobuf_dma_init_user); EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel); @@ -1022,14 +1061,13 @@ EXPORT_SYMBOL_GPL(videobuf_queue_init); EXPORT_SYMBOL_GPL(videobuf_queue_cancel); +EXPORT_SYMBOL_GPL(videobuf_queue_is_busy); -#ifdef HAVE_V4L2 EXPORT_SYMBOL_GPL(videobuf_status); EXPORT_SYMBOL_GPL(videobuf_reqbufs); EXPORT_SYMBOL_GPL(videobuf_querybuf); EXPORT_SYMBOL_GPL(videobuf_qbuf); EXPORT_SYMBOL_GPL(videobuf_dqbuf); -#endif EXPORT_SYMBOL_GPL(videobuf_streamon); EXPORT_SYMBOL_GPL(videobuf_streamoff); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/media/video/video-buf.h linux-2.5-bk/drivers/media/video/video-buf.h --- linux-2.5.45/drivers/media/video/video-buf.h Wed Oct 30 16:42:57 2002 +++ linux-2.5-bk/drivers/media/video/video-buf.h Thu Oct 31 22:12:49 2002 @@ -28,12 +28,14 @@ struct scatterlist* videobuf_vmalloc_to_sg(unsigned char *virt, int nr_pages); /* - * Return a scatterlist for a an array of userpages (NULL on errors). Memory - * for the scatterlist is allocated using kmalloc. The caller must - * free the memory. + * Return a scatterlist for a an array of userpages (NULL on errors). + * Memory for the scatterlist is allocated using kmalloc. The caller + * must free the memory. */ -struct scatterlist *videobuf_pages_to_sg(struct page **pages, int nr_pages, +struct scatterlist* videobuf_pages_to_sg(struct page **pages, int nr_pages, int offset); +int videobuf_lock(struct page **pages, int nr_pages); +int videobuf_unlock(struct page **pages, int nr_pages); /* --------------------------------------------------------------------- */ @@ -58,8 +60,8 @@ struct videobuf_dmabuf { /* for userland buffer */ - struct page **pages; int offset; + struct page **pages; /* for kernel buffers */ void *vmalloc; @@ -115,10 +117,6 @@ struct videobuf_queue *q; }; -#define VBUF_FIELD_EVEN 1 -#define VBUF_FIELD_ODD 2 -#define VBUF_FIELD_INTER 4 - enum videobuf_state { STATE_NEEDS_INIT = 0, STATE_PREPARED = 1, @@ -136,30 +134,27 @@ int width; int height; long size; - int field; + enum v4l2_field field; enum videobuf_state state; struct videobuf_dmabuf dma; struct list_head stream; /* QBUF/DQBUF list */ /* for mmap'ed buffers */ - unsigned long boff; /* buffer offset (mmap) */ - unsigned long bsize; /* buffer size */ - unsigned long baddr; /* buffer addr (userland ptr!) */ + off_t boff; /* buffer offset (mmap) */ + size_t bsize; /* buffer size */ + unsigned long baddr; /* buffer addr (userland ptr!) */ struct videobuf_mapping *map; /* touched by irq handler */ struct list_head queue; wait_queue_head_t done; int field_count; -#ifdef HAVE_V4L2 - stamp_t ts; -#endif + struct timeval ts; }; struct videobuf_queue_ops { int (*buf_setup)(struct file *file, int *count, int *size); - int (*buf_prepare)(struct file *file,struct videobuf_buffer *vb, - int field); + int (*buf_prepare)(struct file *file,struct videobuf_buffer *vb); void (*buf_queue)(struct file *file,struct videobuf_buffer *vb); void (*buf_release)(struct file *file,struct videobuf_buffer *vb); }; @@ -169,7 +164,7 @@ spinlock_t *irqlock; struct pci_dev *pci; - int type; + enum v4l2_buf_type type; int msize; struct videobuf_buffer *bufs[VIDEO_MAX_FRAME]; struct videobuf_queue_ops *ops; @@ -191,12 +186,12 @@ void videobuf_queue_init(struct videobuf_queue *q, struct videobuf_queue_ops *ops, struct pci_dev *pci, spinlock_t *irqlock, - int type, int msize); + enum v4l2_buf_type type, int msize); +int videobuf_queue_is_busy(struct videobuf_queue *q); void videobuf_queue_cancel(struct file *file, struct videobuf_queue *q); -#ifdef HAVE_V4L2 void videobuf_status(struct v4l2_buffer *b, struct videobuf_buffer *vb, - int type); + enum v4l2_buf_type type); int videobuf_reqbufs(struct file *file, struct videobuf_queue *q, struct v4l2_requestbuffers *req); int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b); @@ -204,7 +199,6 @@ struct v4l2_buffer *b); int videobuf_dqbuf(struct file *file, struct videobuf_queue *q, struct v4l2_buffer *b); -#endif int videobuf_streamon(struct file *file, struct videobuf_queue *q); int videobuf_streamoff(struct file *file, struct videobuf_queue *q); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/drivers/oprofile/buffer_sync.c linux-2.5-bk/drivers/oprofile/buffer_sync.c --- linux-2.5.45/drivers/oprofile/buffer_sync.c Wed Oct 30 16:42:21 2002 +++ linux-2.5-bk/drivers/oprofile/buffer_sync.c Thu Oct 31 22:12:52 2002 @@ -118,13 +118,13 @@ * because we cannot reach this code without at least one * dcookie user still being registered (namely, the reader * of the event buffer). */ -static inline u32 fast_get_dcookie(struct dentry * dentry, +static inline unsigned long fast_get_dcookie(struct dentry * dentry, struct vfsmount * vfsmnt) { - u32 cookie; + unsigned long cookie; if (dentry->d_cookie) - return (u32)dentry; + return (unsigned long)dentry; get_dcookie(dentry, vfsmnt, &cookie); return cookie; } @@ -135,9 +135,9 @@ * not strictly necessary but allows oprofile to associate * shared-library samples with particular applications */ -static u32 get_exec_dcookie(struct mm_struct * mm) +static unsigned long get_exec_dcookie(struct mm_struct * mm) { - u32 cookie = 0; + unsigned long cookie = 0; struct vm_area_struct * vma; if (!mm) @@ -163,9 +163,9 @@ * sure to do this lookup before a mm->mmap modification happens so * we don't lose track. */ -static u32 lookup_dcookie(struct mm_struct * mm, unsigned long addr, off_t * offset) +static unsigned long lookup_dcookie(struct mm_struct * mm, unsigned long addr, off_t * offset) { - u32 cookie = 0; + unsigned long cookie = 0; struct vm_area_struct * vma; for (vma = find_vma(mm, addr); vma; vma = vma->vm_next) { @@ -188,7 +188,7 @@ } -static u32 last_cookie = ~0UL; +static unsigned long last_cookie = ~0UL; static void add_cpu_switch(int i) { @@ -199,7 +199,7 @@ } -static void add_ctx_switch(pid_t pid, u32 cookie) +static void add_ctx_switch(pid_t pid, unsigned long cookie) { add_event_entry(ESCAPE_CODE); add_event_entry(CTX_SWITCH_CODE); @@ -208,7 +208,7 @@ } -static void add_cookie_switch(u32 cookie) +static void add_cookie_switch(unsigned long cookie) { add_event_entry(ESCAPE_CODE); add_event_entry(COOKIE_SWITCH_CODE); @@ -225,7 +225,7 @@ static void add_us_sample(struct mm_struct * mm, struct op_sample * s) { - u32 cookie; + unsigned long cookie; off_t offset; cookie = lookup_dcookie(mm, s->eip, &offset); @@ -317,7 +317,7 @@ { struct mm_struct * mm = 0; struct task_struct * new; - u32 cookie; + unsigned long cookie; int i; for (i=0; i < cpu_buf->pos; ++i) { diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/Kconfig linux-2.5-bk/fs/Kconfig --- linux-2.5.45/fs/Kconfig Wed Oct 30 16:42:59 2002 +++ linux-2.5-bk/fs/Kconfig Thu Oct 31 22:12:57 2002 @@ -289,6 +289,31 @@ of your root partition (the one containing the directory /) cannot be compiled as a module, and so this may be dangerous. +config EXT3_FS_XATTR + bool "Ext3 extended attributes" + depends on EXT3_FS + default y + ---help--- + Extended attributes are name:value pairs associated with inodes by + the kernel or by users (see the attr(5) manual page, or visit + for details). + + If unsure, say N. + + You need this for POSIX ACL support on ext3. + +config EXT3_FS_POSIX_ACL + bool "Ext3 POSIX Access Control Lists" + depends on EXT3_FS_XATTR + ---help--- + Posix Access Control Lists (ACLs) support permissions for users and + groups beyond the owner/group/world scheme. + + To learn more about Access Control Lists, visit the Posix ACLs for + Linux website . + + If you don't know what Access Control Lists are, say N + # CONFIG_JBD could be its own option (even modular), but until there are # other users than ext3, we will simply make it be the same as CONFIG_EXT3_FS # dep_tristate ' Journal Block Device support (JBD for ext3)' CONFIG_JBD $CONFIG_EXT3_FS @@ -578,6 +603,10 @@ say M here and read . The module will be called ramfs.o. +config HUGETLBFS + bool "HugeTLB file system support" + depends on HUGETLB_PAGE + config ISO9660_FS tristate "ISO 9660 CDROM file system support" ---help--- @@ -935,6 +964,28 @@ be compiled as a module, and so this could be dangerous. Most everyone wants to say Y here. +config EXT2_FS_XATTR + bool "Ext2 extended attributes" + depends on EXT2_FS + ---help--- + Extended attributes are name:value pairs associated with inodes by + the kernel or by users (see the attr(5) manual page, or visit + for details). + + If unsure, say N. + +config EXT2_FS_POSIX_ACL + bool "Ext2 POSIX Access Control Lists" + depends on EXT2_FS_XATTR + ---help--- + Posix Access Control Lists (ACLs) support permissions for users and + groups beyond the owner/group/world scheme. + + To learn more about Access Control Lists, visit the Posix ACLs for + Linux website . + + If you don't know what Access Control Lists are, say N + config SYSV_FS tristate "System V/Xenix/V7/Coherent file system support" ---help--- @@ -1406,6 +1457,18 @@ depends on ZISOFS default ISO9660_FS +# Meta block cache for Extended Attributes (ext2/ext3) +config FS_MBCACHE + tristate + depends on EXT2_FS_XATTR || EXT3_FS_XATTR + default m if EXT2_FS=m || EXT3_FS=m + default y if EXT2_FS=y || EXT3_FS=y + +# Posix ACL utility routines (for now, only ext2/ext3) +config FS_POSIX_ACL + bool + depends on EXT2_FS_POSIX_ACL || EXT3_FS_POSIX_ACL + default y menu "Partition Types" diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/Makefile linux-2.5-bk/fs/Makefile --- linux-2.5.45/fs/Makefile Wed Oct 30 16:42:59 2002 +++ linux-2.5-bk/fs/Makefile Thu Oct 31 22:12:57 2002 @@ -6,7 +6,8 @@ # export-objs := open.o dcache.o buffer.o bio.o inode.o dquot.o mpage.o aio.o \ - fcntl.o read_write.o dcookies.o fcblist.o + fcntl.o read_write.o dcookies.o fcblist.o mbcache.o \ + posix_acl.o xattr_acl.o obj-y := open.o read_write.o devices.o file_table.o buffer.o \ bio.o super.o block_dev.o char_dev.o stat.o exec.o pipe.o \ @@ -30,6 +31,9 @@ obj-$(CONFIG_BINFMT_ELF) += binfmt_elf.o +obj-$(CONFIG_FS_MBCACHE) += mbcache.o +obj-$(CONFIG_FS_POSIX_ACL) += posix_acl.o xattr_acl.o + obj-$(CONFIG_QUOTA) += dquot.o obj-$(CONFIG_QFMT_V1) += quota_v1.o obj-$(CONFIG_QFMT_V2) += quota_v2.o @@ -37,7 +41,7 @@ obj-$(CONFIG_PROC_FS) += proc/ obj-y += partitions/ -obj-y += driverfs/ sysfs/ +obj-y += sysfs/ obj-y += devpts/ obj-$(CONFIG_PROFILING) += dcookies.o @@ -48,6 +52,7 @@ obj-$(CONFIG_EXT2_FS) += ext2/ obj-$(CONFIG_CRAMFS) += cramfs/ obj-$(CONFIG_RAMFS) += ramfs/ +obj-$(CONFIG_HUGETLBFS) += hugetlbfs/ obj-$(CONFIG_CODA_FS) += coda/ obj-$(CONFIG_INTERMEZZO_FS) += intermezzo/ obj-$(CONFIG_MINIX_FS) += minix/ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/attr.c linux-2.5-bk/fs/attr.c --- linux-2.5.45/fs/attr.c Wed Oct 30 16:41:56 2002 +++ linux-2.5-bk/fs/attr.c Thu Oct 31 22:12:57 2002 @@ -95,7 +95,7 @@ return error; } -static int setattr_mask(unsigned int ia_valid) +int setattr_mask(unsigned int ia_valid) { unsigned long dn_mask = 0; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/binfmt_aout.c linux-2.5-bk/fs/binfmt_aout.c --- linux-2.5.45/fs/binfmt_aout.c Wed Oct 30 16:42:19 2002 +++ linux-2.5-bk/fs/binfmt_aout.c Thu Oct 31 22:12:57 2002 @@ -307,6 +307,7 @@ (current->mm->start_data = N_DATADDR(ex)); current->mm->brk = ex.a_bss + (current->mm->start_brk = N_BSSADDR(ex)); + current->mm->free_area_cache = TASK_UNMAPPED_BASE; current->mm->rss = 0; current->mm->mmap = NULL; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/binfmt_elf.c linux-2.5-bk/fs/binfmt_elf.c --- linux-2.5.45/fs/binfmt_elf.c Wed Oct 30 16:42:54 2002 +++ linux-2.5-bk/fs/binfmt_elf.c Thu Oct 31 22:12:57 2002 @@ -619,6 +619,7 @@ /* Do this so that we can load the interpreter, if need be. We will change some of these later */ current->mm->rss = 0; + current->mm->free_area_cache = TASK_UNMAPPED_BASE; retval = setup_arg_pages(bprm); if (retval < 0) { send_sig(SIGKILL, current, 0); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/cifs/AUTHORS linux-2.5-bk/fs/cifs/AUTHORS --- linux-2.5.45/fs/cifs/AUTHORS Wed Oct 30 16:41:38 2002 +++ linux-2.5-bk/fs/cifs/AUTHORS Thu Oct 31 22:12:58 2002 @@ -14,3 +14,9 @@ the other members of the Storage Network Industry Association CIFS Technical Workgroup for their work specifying this highly complex protocol and finally thanks to the Samba team for their technical advice and encouragement. + +Patch Contributors +------------------ +Zwane Mwaikambo +Andi Kleen + diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/cifs/CHANGES linux-2.5-bk/fs/cifs/CHANGES --- linux-2.5.45/fs/cifs/CHANGES Wed Oct 30 16:43:39 2002 +++ linux-2.5-bk/fs/cifs/CHANGES Thu Oct 31 22:12:58 2002 @@ -1,3 +1,24 @@ +Version 0.58 +------------ +Changed read and write to go through pagecache. Added additional address space operations. +Memory mapped operations now working. + +Version 0.57 +------------ +Added writepage code for additional memory mapping support. Fixed leak in xids causing +the simultaneous operations counter (/proc/fs/cifs/SimultaneousOps) to increase on +every stat call. Additional formatting cleanup. + +Version 0.56 +------------ +Fix bigendian bug in order of time conversion. Merge 2.5 to 2.4 version. Formatting cleanup. + +Version 0.55 +------------ +Fixes from Zwane Mwaikambo for adding missing return code checking in a few places. +Also included a modified version of his fix to protect global list manipulation of +the smb session and tree connection and mid related global variables. + Version 0.54 ------------ Fix problem with captive thread hanging around at unmount time. Adjust to 2.5.42-pre diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/cifs/TODO linux-2.5-bk/fs/cifs/TODO --- linux-2.5.45/fs/cifs/TODO Wed Oct 30 16:42:29 2002 +++ linux-2.5-bk/fs/cifs/TODO Thu Oct 31 22:12:58 2002 @@ -31,9 +31,6 @@ j) finish off the mount helper, mount.cifs - (started) -k) support for memory mapped files only partially works until support for -MS_INVALIDATE implemented. readpage and writepage code not finished (started) - KNOWN BUGS (updated October 8nd, 2002) ==================================== 1) symbolic links (Windows reparse points) are recognized but @@ -42,6 +39,8 @@ symlink text beginning with slash 2) delete of file with read-only attribute set will fail (may be ok) 3) autoreconnection logic is only partially complete. +4) there may be a problem with truncating a memmapped file to smaller than 4k with +the size being reported as exactly 4k. Misc testing to do ================= diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/cifs/cifs_debug.c linux-2.5-bk/fs/cifs/cifs_debug.c --- linux-2.5.45/fs/cifs/cifs_debug.c Wed Oct 30 16:42:55 2002 +++ linux-2.5-bk/fs/cifs/cifs_debug.c Thu Oct 31 22:12:58 2002 @@ -79,18 +79,20 @@ buf += length; i = 0; + read_lock(&GlobalSMBSeslock); list_for_each(tmp, &GlobalSMBSessionList) { i++; ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList); length = sprintf(buf, - "\n%d) Name: %s Domain: %s HowManyMounts: %d LocalUsersToSameServer: %d\n\t ServerOS: %s ServerNOS: %s Capabilities: 0x%x ", - i, ses->serverName, ses->serverDomain, - atomic_read(&ses->inUse), - atomic_read(&ses->server->socketUseCount), - ses->serverOS, ses->serverNOS, ses->capabilities); + "\n%d) Name: %s Domain: %s HowManyMounts: %d ServerOS: %s ServerNOS: %s Capabilities: 0x%x\n", + i, ses->serverName, ses->serverDomain, atomic_read(&ses->inUse), + ses->serverOS, ses->serverNOS, ses->capabilities); buf += length; + if(ses->server) + buf += sprintf(buf, "\tLocal Users To Same Server: %d ",atomic_read(&ses->server->socketUseCount)); } + read_unlock(&GlobalSMBSeslock); sprintf(buf, "\n"); buf++; printk("\nTotal Buffer so far: %s\n", buf_start); @@ -99,6 +101,7 @@ buf += length; i = 0; + read_lock(&GlobalSMBSeslock); list_for_each(tmp, &GlobalTreeConnectionList) { i++; tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); @@ -122,6 +125,7 @@ tcon->fsDevInfo.DeviceType); buf += length; } + read_unlock(&GlobalSMBSeslock); length = sprintf(buf, "\n"); buf += length; *eof = 1; @@ -156,22 +160,22 @@ cifs_stats_read(char *buf, char **beginBuffer, off_t offset, int length, int *eof, void *data) { - int item_length; + int item_length; length = sprintf(buf, "Currently Allocated structures\nCIFS Sessions: %d\n",sesInfoAllocCount.counter); - buf += length; - item_length = - sprintf(buf,"Shares (unique mount targets): %d\n",tconInfoAllocCount.counter); - length += item_length; - buf += item_length; - item_length = - sprintf(buf,"Allocated SMB Request and Response Buffers: %d\n",bufAllocCount.counter); - length += item_length; - buf += item_length; - item_length = - sprintf(buf,"Active Operations (MIDs in use): %d\n",midCount.counter); - length += item_length; + buf += length; + item_length = + sprintf(buf,"Shares (unique mount targets): %d\n",tconInfoAllocCount.counter); + length += item_length; + buf += item_length; + item_length = + sprintf(buf,"Allocated SMB Request and Response Buffers: %d\n",bufAllocCount.counter); + length += item_length; + buf += item_length; + item_length = + sprintf(buf,"Active Operations (MIDs in use): %d\n",midCount.counter); + length += item_length; return length; } @@ -262,10 +266,13 @@ remove_proc_entry("DebugData", proc_fs_cifs); remove_proc_entry("cifsFYI", proc_fs_cifs); remove_proc_entry("TraceSMB", proc_fs_cifs); - remove_proc_entry("MaxSimultaneousOps", proc_fs_cifs); + remove_proc_entry("SimultaneousOps", proc_fs_cifs); remove_proc_entry("TotalOps", proc_fs_cifs); remove_proc_entry("MultiuserMount", proc_fs_cifs); remove_proc_entry("oplockEnabled", proc_fs_cifs); + remove_proc_entry("NTLMV2Enabled",proc_fs_cifs); + remove_proc_entry("ExtendedSecurity",proc_fs_cifs); + remove_proc_entry("PacketSigningEnabled",proc_fs_cifs); remove_proc_entry("cifs", proc_root_fs); } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/cifs/cifsfs.c linux-2.5-bk/fs/cifs/cifsfs.c --- linux-2.5.45/fs/cifs/cifsfs.c Wed Oct 30 16:42:58 2002 +++ linux-2.5-bk/fs/cifs/cifsfs.c Thu Oct 31 22:12:58 2002 @@ -64,8 +64,10 @@ struct cifs_sb_info *cifs_sb; int rc = 0; - sb->s_fs_info = kmalloc(sizeof(struct cifs_sb_info),GFP_KERNEL); + sb->s_fs_info = kmalloc(sizeof(struct cifs_sb_info),GFP_KERNEL); cifs_sb = CIFS_SB(sb); + if(cifs_sb == NULL) + return -ENOMEM; cifs_sb->local_nls = load_nls_default(); /* needed for ASCII cp to Unicode converts */ rc = cifs_mount(sb, cifs_sb, data, devname); @@ -97,13 +99,12 @@ if (inode) iput(inode); -/* rc = cifs_umount(sb); BB is CIFS unmount routine needed? */ if (rc) { - cERROR(1, ("cifs_umount failed with return code %d\n", rc)); + cERROR(1, ("cifs_mount failed with no root inode")); } out_mount_failed: - if(cifs_sb) - kfree(cifs_sb); + if(cifs_sb) + kfree(cifs_sb); return -EINVAL; } @@ -115,14 +116,16 @@ cFYI(1, ("In cifs_put_super\n")); cifs_sb = CIFS_SB(sb); - rc = cifs_umount(sb, cifs_sb); + if(cifs_sb == NULL) { + cFYI(1,("\nEmpty cifs superblock info passed to unmount")); + return; + } + rc = cifs_umount(sb, cifs_sb); if (rc) { cERROR(1, ("cifs_umount failed with return code %d\n", rc)); } - if(cifs_sb) { - unload_nls(cifs_sb->local_nls); - kfree(cifs_sb); - } + unload_nls(cifs_sb->local_nls); + kfree(cifs_sb); return; } @@ -155,7 +158,7 @@ __fsid_t f_fsid; int f_namelen; */ /* BB get from info put in tcon struct at mount time with call to QFSAttrInfo */ - + FreeXid(xid); return 0; /* always return success? what if volume is no longer available? */ } @@ -176,7 +179,7 @@ atomic_set(&cifs_inode->inUse, 0); cifs_inode->time = 0; cifs_inode->clientCanCache = 0; - INIT_LIST_HEAD(&cifs_inode->openFileList); + INIT_LIST_HEAD(&cifs_inode->openFileList); return &cifs_inode->vfs_inode; } @@ -258,6 +261,7 @@ struct inode_operations cifs_dir_inode_ops = { .create = cifs_create, .lookup = cifs_lookup, + .getattr = cifs_getattr, .unlink = cifs_unlink, .link = cifs_hardlink, .mkdir = cifs_mkdir, @@ -271,6 +275,7 @@ struct inode_operations cifs_file_inode_ops = { /* revalidate:cifs_revalidate, */ .setattr = cifs_setattr, + .getattr = cifs_getattr, .rename = cifs_rename, }; @@ -278,17 +283,18 @@ .readlink = cifs_readlink, .follow_link = cifs_follow_link, /* BB add the following two eventually */ - /* revalidate: cifs_revalidate, - setattr: cifs_notify_change, *//* BB do we need notify change */ + /* revalidate: cifs_revalidate, + setattr: cifs_notify_change, *//* BB do we need notify change */ }; struct file_operations cifs_file_ops = { - .read = cifs_read, - .write = cifs_write, + .read = generic_file_read, + .write = generic_file_write, .open = cifs_open, .release = cifs_close, .lock = cifs_lock, .fsync = cifs_fsync, + .mmap = cifs_file_mmap, }; struct file_operations cifs_dir_ops = { @@ -387,8 +393,11 @@ atomic_set(&tconInfoAllocCount, 0); atomic_set(&bufAllocCount, 0); atomic_set(&midCount, 0); + GlobalCurrentXid = 0; GlobalTotalActiveXid = 0; GlobalMaxActiveXid = 0; + GlobalSMBSeslock = RW_LOCK_UNLOCKED; + GlobalMid_Lock = RW_LOCK_UNLOCKED; rc = cifs_init_inodecache(); if (!rc) { @@ -419,7 +428,7 @@ #if CONFIG_PROC_FS cifs_proc_clean(); #endif - unregister_filesystem(&cifs_fs_type); + unregister_filesystem(&cifs_fs_type); cifs_destroy_inodecache(); cifs_destroy_mids(); cifs_destroy_request_bufs(); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/cifs/cifsfs.h linux-2.5-bk/fs/cifs/cifsfs.h --- linux-2.5.45/fs/cifs/cifsfs.h Wed Oct 30 16:42:17 2002 +++ linux-2.5-bk/fs/cifs/cifsfs.h Thu Oct 31 22:12:58 2002 @@ -55,6 +55,7 @@ extern int cifs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); extern int cifs_revalidate(struct dentry *); +extern int cifs_getattr(struct vfsmount *, struct dentry *, struct kstat *); extern int cifs_setattr(struct dentry *, struct iattr *); extern struct inode_operations cifs_file_inode_ops; @@ -73,7 +74,7 @@ size_t write_size, loff_t * poffset); extern int cifs_lock(struct file *, int, struct file_lock *); extern int cifs_fsync(struct file *, struct dentry *, int); - +extern int cifs_file_mmap(struct file * , struct vm_area_struct *); extern struct file_operations cifs_dir_ops; extern int cifs_dir_open(struct inode *inode, struct file *file); extern int cifs_readdir(struct file *file, void *direntry, filldir_t filldir); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/cifs/cifsglob.h linux-2.5-bk/fs/cifs/cifsglob.h --- linux-2.5.45/fs/cifs/cifsglob.h Wed Oct 30 16:42:19 2002 +++ linux-2.5-bk/fs/cifs/cifsglob.h Thu Oct 31 22:12:58 2002 @@ -165,7 +165,7 @@ */ struct cifsTconInfo { struct list_head cifsConnectionList; - struct list_head openFileList; + struct list_head openFileList; struct semaphore tconSem; struct cifsSesInfo *ses; /* pointer to session associated with */ char treeName[MAX_TREE_SIZE + 1]; /* The ascii or unicode name of this resource depending on the ses->capabilities *//* BB fill in this field */ @@ -201,6 +201,7 @@ __u16 netfid; /* file id from remote */ /* BB add lock scope info here if needed */ ; /* lock scope id (0 if none) */ + struct file * pfile; /* needed for writepage */ int endOfSearch:1; /* we have reached end of search */ int closePend:1; /* file is marked to close */ }; @@ -212,12 +213,12 @@ struct cifsInodeInfo { struct list_head lockList; /* BB add in lists for dirty pages - i.e. write caching info for oplock */ - struct list_head openFileList; + struct list_head openFileList; __u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system etc. */ atomic_t inUse; /* num concurrent users (local openers cifs) of file */ unsigned long time; /* jiffies of last update/check of inode */ int clientCanCache:1; /* oplocked. We need to extend cases beyond this i.e. what - if file read-only or if file locked? or if file on r/o vol? */ + if file read-only or if file locked? or if file on r/o vol? */ struct inode vfs_inode; }; @@ -244,7 +245,7 @@ struct cifsSesInfo *ses; /* smb was sent to this server */ struct task_struct *tsk; /* task waiting for response */ struct smb_hdr *resp_buf; /* response buffer */ - int midState; /* wish this could be an enum but can not pass that to wait_event */ + int midState; /* wish this were enum but can not pass to wait_event */ }; #define MID_FREE 0 @@ -292,13 +293,10 @@ */ GLOBAL_EXTERN struct smbUidInfo *GlobalUidList[UID_HASH]; -GLOBAL_EXTERN struct list_head GlobalServerList; /* BB this one is not implemented yet */ +GLOBAL_EXTERN struct list_head GlobalServerList; /* BB not implemented yet */ GLOBAL_EXTERN struct list_head GlobalSMBSessionList; GLOBAL_EXTERN struct list_head GlobalTreeConnectionList; -/* - * Global list of free SMB structures - */ -GLOBAL_EXTERN void *GlobalFreeSMB; +GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; /* protects list inserts on 3 above */ /* * Global transaction id (XID) information @@ -306,7 +304,8 @@ GLOBAL_EXTERN unsigned int GlobalCurrentXid; /* protected by GlobalMid_Sem */ GLOBAL_EXTERN unsigned int GlobalTotalActiveXid; /* prot by GlobalMid_Sem */ GLOBAL_EXTERN unsigned int GlobalMaxActiveXid; /* prot by GlobalMid_Sem */ - +GLOBAL_EXTERN rwlock_t GlobalMid_Lock; /* protects above and list operations */ + /* on midQ entries */ GLOBAL_EXTERN char Local_System_Name[15]; /* @@ -321,13 +320,12 @@ /* Misc globals */ GLOBAL_EXTERN unsigned int multiuser_mount; /* if enabled allows new sessions - to be established on existing mount if we - have the uid/password or Kerberos credential - or equivalent for current user */ + to be established on existing mount if we + have the uid/password or Kerberos credential + or equivalent for current user */ GLOBAL_EXTERN unsigned int oplockEnabled; GLOBAL_EXTERN unsigned int extended_security; /* if on, session setup sent - with more secure ntlmssp2 challenge/resp */ + with more secure ntlmssp2 challenge/resp */ GLOBAL_EXTERN unsigned int ntlmv2_support; /* better optional password hash */ GLOBAL_EXTERN unsigned int sign_CIFS_PDUs; /* enable smb packet signing */ - diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/cifs/cifsproto.h linux-2.5-bk/fs/cifs/cifsproto.h --- linux-2.5.45/fs/cifs/cifsproto.h Wed Oct 30 16:41:37 2002 +++ linux-2.5-bk/fs/cifs/cifsproto.h Thu Oct 31 22:12:58 2002 @@ -31,7 +31,7 @@ extern struct smb_hdr *buf_get(void); extern void buf_release(void *); extern int smb_send(struct socket *, struct smb_hdr *, - unsigned int /* length */ , struct sockaddr *); + unsigned int /* length */ , struct sockaddr *); extern unsigned int _GetXid(void); extern void _FreeXid(unsigned int); #define GetXid() (int)_GetXid(); cFYI(1,("\nCIFS VFS: in %s as Xid: %d with uid: %d",__FUNCTION__, xid,current->fsuid)); @@ -41,70 +41,70 @@ extern void renew_parental_timestamps(struct dentry *direntry); extern void *kcalloc(size_t mem, int type); extern int SendReceive(const unsigned int /* xid */ , struct cifsSesInfo *, - struct smb_hdr * /* input */ , - struct smb_hdr * /* out */ , - int * /* bytes returned */ , const int long_op); + struct smb_hdr * /* input */ , + struct smb_hdr * /* out */ , + int * /* bytes returned */ , const int long_op); extern int checkSMBhdr(struct smb_hdr *smb, __u16 mid); extern int checkSMB(struct smb_hdr *smb, __u16 mid, int length); extern int is_valid_oplock_break(struct smb_hdr *smb); extern int smbCalcSize(struct smb_hdr *ptr); extern int decode_negTokenInit(unsigned char *security_blob, int length, - enum securityEnum *secType); + enum securityEnum *secType); extern int map_smb_to_linux_error(struct smb_hdr *smb); extern void header_assemble(struct smb_hdr *, char /* command */ , - const struct cifsTconInfo *, int - /* length of fixed section (word count) in two byte units */ - ); + const struct cifsTconInfo *, int + /* length of fixed section (word count) in two byte units */ + ); extern time_t cifs_NTtimeToUnix(u64 /* utc nanoseconds since 1601 */ ); extern u64 cifs_UnixTimeToNT(time_t); extern void RevUcode_to_Ucode(char *revUnicode, char *UnicodeName); extern void Ucode_to_RevUcode(char *Unicode, char *revUnicodeName); extern void RevUcode_to_Ucode_with_Len(char *revUnicode, char *UnicodeName, - int Len); + int Len); extern void Ucode_to_RevUcode_with_Len(char *Unicode, char *revUnicodeName, - int Len); + int Len); extern int cifs_get_inode_info(struct inode **pinode, - const unsigned char *search_path, - struct super_block *sb); + const unsigned char *search_path, + struct super_block *sb); extern int cifs_get_inode_info_unix(struct inode **pinode, - const unsigned char *search_path, - struct super_block *sb); + const unsigned char *search_path, + struct super_block *sb); extern int CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses, - char cryptokey[CIFS_CRYPTO_KEY_SIZE]); + char cryptokey[CIFS_CRYPTO_KEY_SIZE]); extern int CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses, - char *user, char *domain, - char *session_key, char *ntlm_session_key, - const struct nls_table *); + char *user, char *domain, + char *session_key, char *ntlm_session_key, + const struct nls_table *); extern int CIFSSpnegoSessSetup(unsigned int xid, struct cifsSesInfo *ses, - char *user, char *domain, - char *SecurityBlob, - int SecurityBlobLength, - const struct nls_table *); + char *user, char *domain, + char *SecurityBlob, + int SecurityBlobLength, + const struct nls_table *); extern int CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, - struct cifsSesInfo *ses, - char *domain, - char *ntlm_session_key, - int *ntlmv2_flag, - const struct nls_table *); + struct cifsSesInfo *ses, + char *domain, + char *ntlm_session_key, + int *ntlmv2_flag, + const struct nls_table *); extern int CIFSNTLMSSPAuthSessSetup(unsigned int xid, - struct cifsSesInfo *ses, char *user, - char *domain, char *ntlm_session_key, - char *lanman_session_key, - int ntlmv2_flag, - const struct nls_table *); + struct cifsSesInfo *ses, char *user, + char *domain, char *ntlm_session_key, + char *lanman_session_key, + int ntlmv2_flag, + const struct nls_table *); extern int CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, - const char *tree, struct cifsTconInfo *tcon, - const struct nls_table *); + const char *tree, struct cifsTconInfo *tcon, + const struct nls_table *); extern int CIFSFindFirst(const int xid, const struct cifsTconInfo *tcon, - const char *searchName, - FILE_DIRECTORY_INFO * findData, - T2_FFIRST_RSP_PARMS * findParms, - const struct nls_table *nls_codepage, - int *pUnicodeFlag, - int *pUnixFlag /* if Unix extensions used */ ); + const char *searchName, + FILE_DIRECTORY_INFO * findData, + T2_FFIRST_RSP_PARMS * findParms, + const struct nls_table *nls_codepage, + int *pUnicodeFlag, + int *pUnixFlag /* if Unix extensions used */ ); extern int CIFSFindNext(const int xid, const struct cifsTconInfo *tcon, FILE_DIRECTORY_INFO * findData, T2_FNEXT_RSP_PARMS * findParms, @@ -112,48 +112,48 @@ int *UnicodeFlag, int *pUnixFlag); extern int CIFSSMBQPathInfo(const int xid, const struct cifsTconInfo *tcon, - const unsigned char *searchName, - FILE_ALL_INFO * findData, - const struct nls_table *nls_codepage); + const unsigned char *searchName, + FILE_ALL_INFO * findData, + const struct nls_table *nls_codepage); extern int CIFSSMBUnixQPathInfo(const int xid, - const struct cifsTconInfo *tcon, - const unsigned char *searchName, - FILE_UNIX_BASIC_INFO * pFindData, - const struct nls_table *nls_codepage); + const struct cifsTconInfo *tcon, + const unsigned char *searchName, + FILE_UNIX_BASIC_INFO * pFindData, + const struct nls_table *nls_codepage); extern int CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses, - const unsigned char *searchName, - unsigned char **targetUNCs, - int *number_of_UNC_in_array, - const struct nls_table *nls_codepage); + const unsigned char *searchName, + unsigned char **targetUNCs, + int *number_of_UNC_in_array, + const struct nls_table *nls_codepage); extern int connect_to_dfs_path(int xid, struct cifsSesInfo *pSesInfo, - const char *old_path, - const struct nls_table *nls_codepage); + const char *old_path, + const struct nls_table *nls_codepage); extern int CIFSSMBQFSInfo(const int xid, const struct cifsTconInfo *tcon, - struct statfs *FSData, - const struct nls_table *nls_codepage); + struct statfs *FSData, + const struct nls_table *nls_codepage); extern int CIFSSMBQFSAttributeInfo(const int xid, - struct cifsTconInfo *tcon, - const struct nls_table *nls_codepage); + struct cifsTconInfo *tcon, + const struct nls_table *nls_codepage); extern int CIFSSMBQFSDeviceInfo(const int xid, struct cifsTconInfo *tcon, - const struct nls_table *nls_codepage); + const struct nls_table *nls_codepage); extern int CIFSSMBQFSUnixInfo(const int xid, struct cifsTconInfo *tcon, - const struct nls_table *nls_codepage); + const struct nls_table *nls_codepage); extern int CIFSSMBSetTimes(const int xid, struct cifsTconInfo *tcon, - char *fileName, FILE_BASIC_INFO * data, - const struct nls_table *nls_codepage); + char *fileName, FILE_BASIC_INFO * data, + const struct nls_table *nls_codepage); extern int CIFSSMBSetEOF(const int xid, struct cifsTconInfo *tcon, - char *fileName, __u64 size,int setAllocationSizeFlag, - const struct nls_table *nls_codepage); + char *fileName, __u64 size,int setAllocationSizeFlag, + const struct nls_table *nls_codepage); extern int CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size, __u16 fileHandle,__u32 opener_pid, int AllocSizeFlag); extern int CIFSSMBUnixSetPerms(const int xid, struct cifsTconInfo *pTcon, - char *full_path, __u64 mode, __u64 uid, - __u64 gid, const struct nls_table *nls_codepage); + char *full_path, __u64 mode, __u64 uid, + __u64 gid, const struct nls_table *nls_codepage); extern int CIFSSMBMkDir(const int xid, const struct cifsTconInfo *tcon, const char *newName, @@ -162,49 +162,49 @@ const char *name, const struct nls_table *nls_codepage); extern int CIFSSMBDelFile(const int xid, const struct cifsTconInfo *tcon, - const char *name, - const struct nls_table *nls_codepage); + const char *name, + const struct nls_table *nls_codepage); extern int CIFSSMBRename(const int xid, const struct cifsTconInfo *tcon, - const char *fromName, const char *toName, - const struct nls_table *nls_codepage); + const char *fromName, const char *toName, + const struct nls_table *nls_codepage); extern int CIFSCreateHardLink(const int xid, - const struct cifsTconInfo *tcon, - const char *fromName, const char *toName, - const struct nls_table *nls_codepage); + const struct cifsTconInfo *tcon, + const char *fromName, const char *toName, + const struct nls_table *nls_codepage); extern int CIFSUnixCreateHardLink(const int xid, - const struct cifsTconInfo *tcon, - const char *fromName, const char *toName, - const struct nls_table *nls_codepage); + const struct cifsTconInfo *tcon, + const char *fromName, const char *toName, + const struct nls_table *nls_codepage); extern int CIFSUnixCreateSymLink(const int xid, - const struct cifsTconInfo *tcon, - const char *fromName, const char *toName, - const struct nls_table *nls_codepage); + const struct cifsTconInfo *tcon, + const char *fromName, const char *toName, + const struct nls_table *nls_codepage); extern int CIFSSMBUnixQuerySymLink(const int xid, - const struct cifsTconInfo *tcon, - const unsigned char *searchName, - char *syminfo, const int buflen, - const struct nls_table *nls_codepage); + const struct cifsTconInfo *tcon, + const unsigned char *searchName, + char *syminfo, const int buflen, + const struct nls_table *nls_codepage); extern int CIFSSMBOpen(const int xid, const struct cifsTconInfo *tcon, - const char *fileName, const int disposition, - const int access_flags, const int omode, - __u16 * netfid, int *pOplock, - const struct nls_table *nls_codepage); + const char *fileName, const int disposition, + const int access_flags, const int omode, + __u16 * netfid, int *pOplock, + const struct nls_table *nls_codepage); extern int CIFSSMBClose(const int xid, const struct cifsTconInfo *tcon, const int smb_file_id); extern int CIFSSMBRead(const int xid, const struct cifsTconInfo *tcon, - const int netfid, unsigned int count, - const __u64 lseek, unsigned int *nbytes, char *buf); + const int netfid, unsigned int count, + const __u64 lseek, unsigned int *nbytes, char *buf); extern int CIFSSMBWrite(const int xid, const struct cifsTconInfo *tcon, const int netfid, const unsigned int count, const __u64 lseek, unsigned int *nbytes, const char *buf, const int long_op); extern int CIFSSMBLock(const int xid, const struct cifsTconInfo *tcon, - const __u16 netfid, const __u64 len, - const __u64 offset, const __u32 numUnlock, - const __u32 numLock, const __u8 lockType, - const int waitFlag); + const __u16 netfid, const __u64 len, + const __u64 offset, const __u32 numUnlock, + const __u32 numLock, const __u8 lockType, + const int waitFlag); extern int CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon); extern int CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses); @@ -220,32 +220,32 @@ /* BB routines below not implemented yet BB */ extern int CIFSBuildServerList(int xid, char *serverBufferList, - int recordlength, int *entries, - int *totalEntries, int *topoChangedFlag); + int recordlength, int *entries, + int *totalEntries, int *topoChangedFlag); extern int CIFSSMBQueryShares(int xid, struct cifsTconInfo *tcon, - struct shareInfo *shareList, int bufferLen, - int *entries, int *totalEntries); + struct shareInfo *shareList, int bufferLen, + int *entries, int *totalEntries); extern int CIFSSMBQueryAlias(int xid, struct cifsTconInfo *tcon, - struct aliasInfo *aliasList, int bufferLen, - int *entries, int *totalEntries); + struct aliasInfo *aliasList, int bufferLen, + int *entries, int *totalEntries); extern int CIFSSMBAliasInfo(int xid, struct cifsTconInfo *tcon, - char *aliasName, char *serverName, - char *shareName, char *comment); + char *aliasName, char *serverName, + char *shareName, char *comment); extern int CIFSSMBGetShareInfo(int xid, struct cifsTconInfo *tcon, - char *share, char *comment); + char *share, char *comment); extern int CIFSSMBGetUserPerms(int xid, struct cifsTconInfo *tcon, - char *userName, char *searchName, int *perms); + char *userName, char *searchName, int *perms); extern int CIFSSMBSync(int xid, struct cifsTconInfo *tcon, int netfid, int pid); extern int CIFSSMBSeek(int xid, - struct cifsTconInfo *tcon, - int netfid, - int pid, - int whence, unsigned long offset, long long *newoffset); + struct cifsTconInfo *tcon, + int netfid, + int pid, + int whence, unsigned long offset, long long *newoffset); extern int CIFSSMBCopy(int xid, - struct cifsTconInfo *ftcon, - char *fromName, - struct cifsTconInfo *ttcon, - char *toName, int ofun, int flags); -#endif /* _CIFSPROTO_H */ + struct cifsTconInfo *ftcon, + char *fromName, + struct cifsTconInfo *ttcon, + char *toName, int ofun, int flags); +#endif /* _CIFSPROTO_H */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/cifs/cifssmb.c linux-2.5-bk/fs/cifs/cifssmb.c --- linux-2.5.45/fs/cifs/cifssmb.c Wed Oct 30 16:42:20 2002 +++ linux-2.5-bk/fs/cifs/cifssmb.c Thu Oct 31 22:12:58 2002 @@ -487,7 +487,7 @@ (tcon->ses->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFF00)); pSMB->MaxCountHigh = 0; - pSMB->ByteCount = 0; /* no need to do le conversion since it is 0 */ + pSMB->ByteCount = 0; /* no need to do le conversion since it is 0 */ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); @@ -497,15 +497,20 @@ } else { pSMBr->DataLength = le16_to_cpu(pSMBr->DataLength); *nbytes = pSMBr->DataLength; - /* BB check that DataLength would not go beyond end of SMB BB */ - if (pSMBr->DataLength > CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE) { + /*check that DataLength would not go beyond end of SMB */ + if ((pSMBr->DataLength > CIFS_MAX_MSGSIZE) + || (pSMBr->DataLength > count)) { rc = -EIO; *nbytes = 0; } else { pReadData = (char *) (&pSMBr->hdr.Protocol) + le16_to_cpu(pSMBr->DataOffset); - copy_to_user(buf, pReadData, pSMBr->DataLength); +/* if(rc = copy_to_user(buf, pReadData, pSMBr->DataLength)) { + cERROR(1,("\nFaulting on read rc = %d",rc)); + rc = -EFAULT; + }*/ /* can not use copy_to_user when using page cache*/ + memcpy(buf,pReadData,pSMBr->DataLength); } } @@ -544,7 +549,8 @@ pSMB->DataLengthHigh = 0; pSMB->DataOffset = cpu_to_le16(offsetof(struct smb_com_write_req,Data) - 4); - copy_from_user(pSMB->Data, buf, pSMB->DataLengthLow); + + memcpy(pSMB->Data,buf,pSMB->DataLengthLow); pSMB->ByteCount += pSMB->DataLengthLow + 1 /* pad */ ; pSMB->DataLengthLow = cpu_to_le16(pSMB->DataLengthLow); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/cifs/connect.c linux-2.5-bk/fs/cifs/connect.c --- linux-2.5.45/fs/cifs/connect.c Wed Oct 30 16:43:40 2002 +++ linux-2.5-bk/fs/cifs/connect.c Thu Oct 31 22:12:58 2002 @@ -219,6 +219,7 @@ } task_to_wake = NULL; + read_lock(&GlobalMid_Lock); list_for_each(tmp, &server->pending_mid_q) { mid_entry = list_entry(tmp, struct mid_q_entry, @@ -234,7 +235,7 @@ MID_RESPONSE_RECEIVED; } } - + read_unlock(&GlobalMid_Lock); if (task_to_wake) { smb_buffer = NULL; /* will be freed by users thread after he is done */ wake_up_process(task_to_wake); @@ -256,12 +257,14 @@ } /* BB add code to lock SMB sessions while releasing */ if(server->ssocket) { - sock_release(csocket); + sock_release(csocket); server->ssocket = NULL; - } + } set_fs(temp_fs); if (smb_buffer) /* buffer usually freed in free_mid - need to free it on error or exit */ buf_release(smb_buffer); + + read_lock(&GlobalSMBSeslock); if (list_empty(&server->pending_mid_q)) { /* loop through server session structures attached to this and mark them dead */ list_for_each(tmp, &GlobalSMBSessionList) { @@ -275,10 +278,12 @@ } kfree(server); } else /* BB need to more gracefully handle the rare negative session - response case because response will be still outstanding */ + response case because response will be still outstanding */ cERROR(1, ("\nThere are still active MIDs in queue and we are exiting but we can not delete mid_q_entries or TCP_Server_Info structure due to pending requests MEMORY LEAK!!\n ")); /* BB wake up waitors, and/or wait and/or free stale mids and try again? BB */ /* BB Need to fix bug in error path above - perhaps wait until smb requests time out and then free the tcp per server struct BB */ + read_unlock(&GlobalSMBSeslock); + cFYI(1, ("\nAbout to exit from demultiplex thread\n")); return 0; @@ -421,7 +426,7 @@ struct cifsSesInfo *ses; *psrvTcp = NULL; - + read_lock(&GlobalSMBSeslock); list_for_each(tmp, &GlobalSMBSessionList) { ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList); if (ses->server) { @@ -432,12 +437,15 @@ /* BB check if reconnection needed */ if (strncmp (ses->userName, userName, - MAX_USERNAME_SIZE) == 0) + MAX_USERNAME_SIZE) == 0){ + read_unlock(&GlobalSMBSeslock); return ses; /* found exact match on both tcp and SMB sessions */ + } } } /* else tcp and smb sessions need reconnection */ } + read_unlock(&GlobalSMBSeslock); return NULL; } @@ -447,6 +455,7 @@ struct list_head *tmp; struct cifsTconInfo *tcon; + read_lock(&GlobalSMBSeslock); list_for_each(tmp, &GlobalTreeConnectionList) { cFYI(1, ("\nNext tcon - ")); tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); @@ -473,13 +482,16 @@ if (strncmp (tcon->ses->userName, userName, - MAX_USERNAME_SIZE) == 0) + MAX_USERNAME_SIZE) == 0) { + read_unlock(&GlobalSMBSeslock); return tcon;/* also matched user (smb session)*/ + } } } } } } + read_unlock(&GlobalSMBSeslock); return NULL; } @@ -599,7 +611,7 @@ { int rc = 0; int xid; - int ntlmv2_flag = FALSE; + int ntlmv2_flag = FALSE; struct socket *csocket; struct sockaddr_in sin_server; /* struct sockaddr_in6 sin_server6; */ @@ -616,7 +628,11 @@ xid = GetXid(); cFYI(0, ("\nEntering cifs_mount. Xid: %d with: %s\n", xid, mount_data)); - parse_mount_options(mount_data, devname, &volume_info); + if(parse_mount_options(mount_data, devname, &volume_info)) { + FreeXid(xid); + return -EINVAL; + } + if (volume_info.username) { cFYI(1, ("\nUsername: %s ", volume_info.username)); @@ -634,7 +650,7 @@ cERROR(1, ("\nCIFS mount error: No UNC path (e.g. -o unc=//192.168.1.100/public) specified ")); FreeXid(xid); - return -ENODEV; + return -EINVAL; } /* BB add support to use the multiuser_mount flag BB */ existingCifsSes = @@ -720,18 +736,18 @@ && (pSesInfo->secType == RawNTLMSSP)) { cFYI(1, ("\nNTLMSSP sesssetup ")); rc = CIFSNTLMSSPNegotiateSessSetup(xid, - pSesInfo, - cryptKey, - volume_info.domainname, - &ntlmv2_flag, - cifs_sb->local_nls); + pSesInfo, + cryptKey, + volume_info.domainname, + &ntlmv2_flag, + cifs_sb->local_nls); if (!rc) { - if(ntlmv2_flag) { - cFYI(1,("\nAble to use the more secure NTLM version 2 password hash")); - /* SMBNTv2encrypt( ...); */ /* BB fix this up - - and note that Samba client equivalent looks wrong */ - } else - SMBNTencrypt(password_with_pad,cryptKey,ntlm_session_key); + if(ntlmv2_flag) { + cFYI(1,("\nAble to use the more secure NTLM version 2 password hash")); + /* SMBNTv2encrypt( ...); */ /* BB fix this up - + and note that Samba client equivalent looks wrong */ + } else + SMBNTencrypt(password_with_pad,cryptKey,ntlm_session_key); /* for better security the weaker lanman hash not sent in AuthSessSetup so why bother calculating it */ @@ -742,13 +758,13 @@ cryptKey, session_key); */ rc = CIFSNTLMSSPAuthSessSetup(xid, - pSesInfo, - volume_info. - username, - volume_info.domainname, - ntlm_session_key, - session_key, - ntlmv2_flag, + pSesInfo, + volume_info. + username, + volume_info.domainname, + ntlm_session_key, + session_key, + ntlmv2_flag, cifs_sb->local_nls); } } else { /* old style NTLM 0.12 session setup */ @@ -805,6 +821,7 @@ "", cifs_sb-> local_nls); + FreeXid(xid); return -ENODEV; } else { rc = CIFSTCon(xid, pSesInfo, @@ -893,8 +910,8 @@ pSMB->req_no_secext.MaxBufferSize = cpu_to_le16(ses->maxBuf); pSMB->req_no_secext.MaxMpxCount = cpu_to_le16(ses->maxReq); - if(ses->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) - smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + if(ses->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; pSMB->req_no_secext.Capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS; @@ -928,8 +945,8 @@ *bcc_ptr = 0; bcc_ptr++; } - if(user == NULL) - bytes_returned = 0; /* skill null user */ + if(user == NULL) + bytes_returned = 0; /* skill null user */ else bytes_returned = cifs_strtoUCS((wchar_t *) bcc_ptr, user, 100, nls_codepage); @@ -960,10 +977,10 @@ bcc_ptr += 2 * bytes_returned; bcc_ptr += 2; } else { - if(user != NULL) { + if(user != NULL) { strncpy(bcc_ptr, user, 200); bcc_ptr += strnlen(user, 200); - } + } *bcc_ptr = 0; bcc_ptr++; if (domain == NULL) { @@ -1065,7 +1082,6 @@ kcalloc(2, GFP_KERNEL); } } else { /* ASCII */ - len = strnlen(bcc_ptr, 1024); if (((long) bcc_ptr + len) - (long) pByteArea(smb_buffer_response) @@ -1148,8 +1164,8 @@ pSMB->req.MaxBufferSize = cpu_to_le16(ses->maxBuf); pSMB->req.MaxMpxCount = cpu_to_le16(ses->maxReq); - if(ses->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) - smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + if(ses->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; pSMB->req.Capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | @@ -1394,7 +1410,7 @@ PCHALLENGE_MESSAGE SecurityBlob2; cFYI(1, ("\nIn NTLMSSP sesssetup (negotiate) ")); - *pNTLMv2_flag = FALSE; + *pNTLMv2_flag = FALSE; smb_buffer = buf_get(); if (smb_buffer == 0) { return -ENOMEM; @@ -1413,8 +1429,8 @@ pSMB->req.MaxBufferSize = cpu_to_le16(ses->maxBuf); pSMB->req.MaxMpxCount = cpu_to_le16(ses->maxReq); - if(ses->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) - smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + if(ses->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; pSMB->req.Capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | @@ -1441,8 +1457,8 @@ NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_OEM | NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_NTLM | 0x80000000 | NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_128; - if(ntlmv2_support) - SecurityBlob->NegotiateFlags |= NTLMSSP_NEGOTIATE_NTLMV2; + if(ntlmv2_support) + SecurityBlob->NegotiateFlags |= NTLMSSP_NEGOTIATE_NTLMV2; /* setup pointers to domain name and workstation name */ bcc_ptr += SecurityBlobLength; @@ -1558,8 +1574,8 @@ memcpy(challenge_from_server, SecurityBlob2->Challenge, CIFS_CRYPTO_KEY_SIZE); - if(SecurityBlob2->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLMV2) - *pNTLMv2_flag = TRUE; + if(SecurityBlob2->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLMV2) + *pNTLMv2_flag = TRUE; if (smb_buffer->Flags2 &= SMBFLG2_UNICODE) { if ((long) (bcc_ptr) % 2) { remaining_words = @@ -1645,7 +1661,6 @@ kcalloc(2, GFP_KERNEL); } } else { /* ASCII */ - len = strnlen(bcc_ptr, 1024); if (((long) bcc_ptr + len) - (long) pByteArea(smb_buffer_response) @@ -1742,8 +1757,8 @@ pSMB->req.hdr.Uid = ses->Suid; - if(ses->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) - smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + if(ses->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; pSMB->req.Capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | @@ -1771,8 +1786,8 @@ NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_TARGET_INFO | 0x80000000 | NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_128; - if(ntlmv2_flag) - SecurityBlob->NegotiateFlags |= NTLMSSP_NEGOTIATE_NTLMV2; + if(ntlmv2_flag) + SecurityBlob->NegotiateFlags |= NTLMSSP_NEGOTIATE_NTLMV2; /* setup pointers to domain name and workstation name */ @@ -2032,7 +2047,6 @@ ses->serverNOS = kcalloc(2, GFP_KERNEL); } } else { /* ASCII */ - len = strnlen(bcc_ptr, 1024); if (((long) bcc_ptr + len) - (long) pByteArea(smb_buffer_response) @@ -2116,8 +2130,8 @@ bcc_ptr = &(pSMB->Password[0]); bcc_ptr++; /* skip password */ - if(ses->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) - smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + if(ses->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; if (ses->capabilities & CAP_STATUS32) { smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS; @@ -2202,6 +2216,7 @@ struct cifsSesInfo *ses = NULL; xid = GetXid(); + if (cifs_sb->tcon) { ses = cifs_sb->tcon->ses; /* save ptr to ses before delete tcon!*/ rc = CIFSSMBTDis(xid, cifs_sb->tcon); @@ -2218,16 +2233,11 @@ FreeXid(xid); return 0; } - /* wake_up_process(ses->server->tsk);*/ /* was worth a try */ schedule_timeout(HZ / 4); /* give captive thread time to exit */ - if((ses->server) && (ses->server->ssocket)) { - cFYI(1,("\nWaking up socket by sending it signal ")); - send_sig(SIGINT,ses->server->tsk,1); - /* No luck figuring out a better way to_close socket */ - /*ses->server->ssocket->sk->prot->close(ses->server->ssocket->sk,0);*/ - /* ses->server->ssocket = NULL; */ /* serialize better */ - /* sock_wake_async(ses->server->ssocket,3,POLL_HUP); */ - } + if((ses->server) && (ses->server->ssocket)) { + cFYI(1,("\nWaking up socket by sending it signal ")); + send_sig(SIGINT,ses->server->tsk,1); + } } else cFYI(1, ("\nNo session or bad tcon")); } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/cifs/file.c linux-2.5-bk/fs/cifs/file.c --- linux-2.5.45/fs/cifs/file.c Wed Oct 30 16:41:32 2002 +++ linux-2.5-bk/fs/cifs/file.c Thu Oct 31 22:12:58 2002 @@ -35,6 +35,8 @@ #include "cifs_debug.h" #include "cifs_fs_sb.h" + + int cifs_open(struct inode *inode, struct file *file) { @@ -42,8 +44,8 @@ int xid, oplock; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; - struct cifsFileInfo *pCifsFile; - struct cifsInodeInfo *pCifsInode; + struct cifsFileInfo *pCifsFile; + struct cifsInodeInfo *pCifsInode; char *full_path = NULL; int desiredAccess = 0x20197; int disposition = FILE_OPEN; @@ -104,28 +106,27 @@ if (file->private_data) { memset(file->private_data, 0, sizeof (struct cifsFileInfo)); - pCifsFile = (struct cifsFileInfo *) file->private_data; + pCifsFile = (struct cifsFileInfo *) file->private_data; pCifsFile->netfid = netfid; pCifsFile->pid = current->pid; - list_add(&pCifsFile->tlist,&pTcon->openFileList); - pCifsInode = CIFS_I(file->f_dentry->d_inode); - list_add(&pCifsFile->flist,&pCifsInode->openFileList); - if(file->f_flags & O_CREAT) { - /* time to set mode which we can not - set earlier due to problems creating new read-only files */ - if (cifs_sb->tcon->ses->capabilities & CAP_UNIX) - CIFSSMBUnixSetPerms(xid, pTcon, full_path, inode->i_mode, - 0xFFFFFFFFFFFFFFFF, - 0xFFFFFFFFFFFFFFFF, - cifs_sb->local_nls); - else {/* BB to be implemented via Windows secrty descriptors*/ - /* eg CIFSSMBWinSetPerms(xid,pTcon,full_path,mode,-1,-1,local_nls);*/ - /* in the meantime we could set the r/o dos attribute - when perms are e.g.: - mode & 0222 == 0 */ - } - } - + pCifsFile->pfile = file; /* needed for writepage */ + list_add(&pCifsFile->tlist,&pTcon->openFileList); + pCifsInode = CIFS_I(file->f_dentry->d_inode); + list_add(&pCifsFile->flist,&pCifsInode->openFileList); + if(file->f_flags & O_CREAT) { + /* time to set mode which we can not set earlier due + to problems creating new read-only files */ + if (cifs_sb->tcon->ses->capabilities & CAP_UNIX) + CIFSSMBUnixSetPerms(xid, pTcon, full_path, inode->i_mode, + 0xFFFFFFFFFFFFFFFF, + 0xFFFFFFFFFFFFFFFF, + cifs_sb->local_nls); + else {/* BB implement via Windows security descriptors */ + /* eg CIFSSMBWinSetPerms(xid,pTcon,full_path,mode,-1,-1,local_nls);*/ + /* in the meantime could set r/o dos attribute when perms are eg: + mode & 0222 == 0 */ + } + } } } @@ -151,10 +152,10 @@ cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; - if (pSMBFile) { - list_del(&pSMBFile->flist); - list_del(&pSMBFile->tlist); - rc = CIFSSMBClose(xid, pTcon, pSMBFile->netfid); + if (pSMBFile) { + list_del(&pSMBFile->flist); + list_del(&pSMBFile->tlist); + rc = CIFSSMBClose(xid, pTcon, pSMBFile->netfid); kfree(file->private_data); file->private_data = NULL; } else @@ -162,7 +163,6 @@ FreeXid(xid); return rc; - } int @@ -344,20 +344,24 @@ file->f_dentry->d_inode->i_size = *poffset; } mark_inode_dirty_sync(file->f_dentry->d_inode); + FreeXid(xid); return total_written; } static int -cifs_writepage(struct page *page) +cifs_partialpagewrite(struct page *page,unsigned from, unsigned to) { struct address_space *mapping = page->mapping; + loff_t offset = (loff_t)page->index << PAGE_CACHE_SHIFT; + char * write_data = kmap(page); int rc = -EFAULT; int bytes_written = 0; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; - int xid; struct inode *inode = page->mapping->host; - loff_t *poffset = NULL; + struct cifsInodeInfo *cifsInode; + struct cifsFileInfo *open_file = NULL; + int xid; xid = GetXid(); @@ -365,51 +369,90 @@ pTcon = cifs_sb->tcon; /* figure out which file struct to use - if (file->private_data == NULL) { + if (file->private_data == NULL) { FreeXid(xid); return -EBADF; - } + } */ if (!mapping) { FreeXid(xid); return -EFAULT; + } else if(!mapping->host) { + FreeXid(xid); + return -EFAULT; } - /* BB fix and add missing call to cifs_writepage_sync here */ + if((to > PAGE_CACHE_SIZE) || (from > to)) + return -EIO; - inode->i_ctime = inode->i_mtime = CURRENT_TIME; /* BB is this right? */ - if ((bytes_written > 0) && (poffset)) { - if (*poffset > inode->i_size) - inode->i_size = *poffset; + offset += (loff_t)from; + write_data += from; + + cifsInode = CIFS_I(mapping->host); + if(!list_empty(&(cifsInode->openFileList))) { + open_file = list_entry(cifsInode->openFileList.next, + struct cifsFileInfo, flist); + /* We could check if file is open for writing first */ + if(open_file->pfile) + bytes_written = cifs_write(open_file->pfile, write_data, + to-from, &offset); + /* Does mm or vfs already set times? */ + inode->i_atime = inode->i_mtime = CURRENT_TIME; + if ((bytes_written > 0) && (offset)) { + rc = 0; + if (offset > inode->i_size) + inode->i_size = offset; + } else if(bytes_written < 0) { + rc = bytes_written; + } + mark_inode_dirty_sync(inode); + } else { + cFYI(1,("\nNo open files to get file handle from")); + rc = -EIO; } - mark_inode_dirty_sync(inode); FreeXid(xid); return rc; } static int -cifs_prepare_write(struct file *file, struct page *page, unsigned offset, - unsigned to) +cifs_writepage(struct page* page) { - return 0; /* eventually add code to flush any incompatible requests */ + int rc = -EFAULT; + int xid; + + xid = GetXid(); + page_cache_get(page); + rc = cifs_partialpagewrite(page,0,PAGE_CACHE_SIZE); + /* insert call to SetPageToUpdate like function here? */ + unlock_page(page); + page_cache_release(page); + FreeXid(xid); + return rc; } static int cifs_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to) { - long rc = -EFAULT; - - lock_kernel(); -/* status = cifs_updatepage(file, page, offset, to-offset); */ + int xid,rc; + xid = GetXid(); -/* BB add - do we really need to lock the kernel here for so long ? */ + if(offset > to) + return -EIO; + rc = cifs_partialpagewrite(page,offset,to); - unlock_kernel(); + FreeXid(xid); return rc; } +static int +cifs_prepare_write(struct file *file, struct page *page, unsigned offset, + unsigned to) +{ + return 0; /* eventually add code to flush any incompatible requests */ +} + int cifs_fsync(struct file *file, struct dentry *dentry, int datasync) { @@ -489,13 +532,30 @@ return total_read; } +int +cifs_file_mmap(struct file * file, struct vm_area_struct * vma) +{ + struct dentry * dentry = file->f_dentry; + int rc, xid; + + xid = GetXid(); + rc = cifs_revalidate(dentry); + if (rc) { + cFYI(1,("Validation prior to mmap failed, error=%d\n", rc)); + FreeXid(xid); + return rc; + } + rc = generic_file_mmap(file, vma); + FreeXid(xid); + return rc; +} + static int -cifs_readpage_sync(struct file *file, struct page *page) +cifs_readpage(struct file *file, struct page *page) { - unsigned int count = PAGE_CACHE_SIZE; + loff_t offset = (loff_t)page->index << PAGE_CACHE_SHIFT; + char * read_data; int rc = -EACCES; - int bytes_read = 0; - int total_read = 0; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; int xid; @@ -508,26 +568,33 @@ FreeXid(xid); return -EBADF; } + page_cache_get(page); + read_data = kmap(page); + /* for reads over a certain size we could initiate async read ahead */ - /* BB finish adding missing here */ + rc = cifs_read(file, read_data, PAGE_CACHE_SIZE, &offset); - cFYI(1, - ("\nCount is %d total read %d bytes read %d ", count, total_read, - bytes_read)); + if (rc < 0) + goto io_error; + else { + cFYI(1,("\nBytes read %d ",rc)); + } - FreeXid(xid); - return rc; -} + file->f_dentry->d_inode->i_atime = CURRENT_TIME; -static int -cifs_readpage(struct file *file, struct page *page) -{ - int rc; + if(PAGE_CACHE_SIZE > rc) { + memset(read_data+rc, 0, PAGE_CACHE_SIZE - rc); + } + flush_dcache_page(page); + SetPageUptodate(page); + rc = 0; + +io_error: + kunmap(page); + unlock_page(page); - page_cache_get(page); - rc = cifs_readpage_sync(file, page); - /* for reads over a certain size we could initiate async read ahead */ page_cache_release(page); + FreeXid(xid); return rc; } @@ -546,11 +613,11 @@ /* Linux can not store file creation time unfortunately so we ignore it */ tmp_inode->i_atime = - le64_to_cpu(cifs_NTtimeToUnix(pfindData->LastAccessTime)); + cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastAccessTime)); tmp_inode->i_mtime = - le64_to_cpu(cifs_NTtimeToUnix(pfindData->LastWriteTime)); + cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastWriteTime)); tmp_inode->i_ctime = - le64_to_cpu(cifs_NTtimeToUnix(pfindData->ChangeTime)); + cifs_NTtimeToUnix(le64_to_cpu(pfindData->ChangeTime)); /* should we treat the dos attribute of read-only as read-only mode bit e.g. 555 */ tmp_inode->i_mode = S_IALLUGO & ~(S_ISUID | S_IXGRP); /* 2767 perms - indicate mandatory locking */ cFYI(0, @@ -605,16 +672,12 @@ atomic_inc(&cifsInfo->inUse); tmp_inode->i_atime = - le64_to_cpu(cifs_NTtimeToUnix(pfindData->LastAccessTime)); + cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastAccessTime)); tmp_inode->i_mtime = - le64_to_cpu(cifs_NTtimeToUnix(pfindData->LastModificationTime)); + cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastModificationTime)); tmp_inode->i_ctime = - le64_to_cpu(cifs_NTtimeToUnix(pfindData->LastStatusChange)); + cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastStatusChange)); - /* tmp_inode->i_mtime = - cifs_NTtimeToUnix(pfindData->LastModificationTime); - tmp_inode->i_ctime = - cifs_NTtimeToUnix(pfindData->LastStatusChange); */ tmp_inode->i_mode = le64_to_cpu(pfindData->Permissions); pfindData->Type = le32_to_cpu(pfindData->Type); if (pfindData->Type == UNIX_FILE) { @@ -1013,8 +1076,17 @@ struct address_space_operations cifs_addr_ops = { .readpage = cifs_readpage, - .sync_page = cifs_sync_page, .writepage = cifs_writepage, .prepare_write = cifs_prepare_write, - .commit_write = cifs_commit_write + .commit_write = cifs_commit_write, +/* .sync_page = cifs_sync_page, */ +}; + +/* change over to struct below when sync page tested and complete */ +struct address_space_operations cifs_addr_ops2 = { + .readpage = cifs_readpage, + .writepage = cifs_writepage, + .prepare_write = cifs_prepare_write, + .commit_write = cifs_commit_write, + .sync_page = cifs_sync_page, }; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/cifs/inode.c linux-2.5-bk/fs/cifs/inode.c --- linux-2.5.45/fs/cifs/inode.c Wed Oct 30 16:41:34 2002 +++ linux-2.5-bk/fs/cifs/inode.c Thu Oct 31 22:12:58 2002 @@ -52,9 +52,9 @@ if (rc) { if (rc == -EREMOTE) { /* rc = *//* CIFSGetDFSRefer(xid, pTcon->ses, search_path, - &referrals, - &num_referrals, - cifs_sb->local_nls); */ + &referrals, + &num_referrals, + cifs_sb->local_nls); */ tmp_path = kmalloc(strnlen (pTcon->treeName, @@ -99,12 +99,12 @@ atomic_inc(&cifsInfo->inUse); /* inc on every refresh of inode */ inode->i_atime = - le64_to_cpu(cifs_NTtimeToUnix(findData.LastAccessTime)); + cifs_NTtimeToUnix(le64_to_cpu(findData.LastAccessTime)); inode->i_mtime = - le64_to_cpu(cifs_NTtimeToUnix + cifs_NTtimeToUnix(le64_to_cpu (findData.LastModificationTime)); inode->i_ctime = - le64_to_cpu(cifs_NTtimeToUnix(findData.LastStatusChange)); + cifs_NTtimeToUnix(le64_to_cpu(findData.LastStatusChange)); inode->i_mode = le64_to_cpu(findData.Permissions); findData.Type = le32_to_cpu(findData.Type); if (findData.Type == UNIX_FILE) { @@ -140,6 +140,7 @@ cFYI(1, (" File inode ")); inode->i_op = &cifs_file_inode_ops; inode->i_fop = &cifs_file_ops; + inode->i_data.a_ops = &cifs_addr_ops; } else if (S_ISDIR(inode->i_mode)) { cFYI(1, (" Directory inode")); inode->i_op = &cifs_dir_inode_ops; @@ -182,9 +183,9 @@ if (rc == -EREMOTE) { /* BB add call to new func rc = GetDFSReferral(); */ /* rc = *//* CIFSGetDFSRefer(xid, pTcon->ses, search_path, - &referrals, - &num_referrals, - cifs_sb->local_nls); */ + &referrals, + &num_referrals, + cifs_sb->local_nls); */ tmp_path = kmalloc(strnlen (pTcon->treeName, @@ -265,6 +266,7 @@ cFYI(1, (" File inode ")); inode->i_op = &cifs_file_inode_ops; inode->i_fop = &cifs_file_ops; + inode->i_data.a_ops = &cifs_addr_ops; } else if (S_ISDIR(inode->i_mode)) { cFYI(1, (" Directory inode ")); inode->i_op = &cifs_dir_inode_ops; @@ -441,9 +443,11 @@ cifs_sb_source = CIFS_SB(source_inode->i_sb); pTcon = cifs_sb_source->tcon; - if (pTcon != cifs_sb_target->tcon) + if (pTcon != cifs_sb_target->tcon) { return -EXDEV; /* BB actually could be allowed if same server, but different share. Might eventually add support for this */ + FreeXid(xid); + } fromName = build_path_from_dentry(source_direntry); toName = build_path_from_dentry(target_direntry); @@ -455,6 +459,7 @@ if (toName) kfree(toName); + FreeXid(xid); return rc; } @@ -479,7 +484,7 @@ direntry->d_time, jiffies)); cifsInode = CIFS_I(direntry->d_inode); - +/* BB add check - do not need to revalidate oplocked files */ if ((time_before(jiffies, cifsInode->time + HZ)) && (direntry->d_inode->i_nlink == 1)) { cFYI(1, (" Do not need to revalidate ")); @@ -505,12 +510,20 @@ return rc; } +int cifs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) +{ + int err = cifs_revalidate(dentry); + if (!err) + generic_fillattr(dentry->d_inode, stat); + return err; +} + void cifs_truncate_file(struct inode *inode) { /* BB remove - may not need this function after all BB */ int xid; int rc = 0; - struct cifsFileInfo *open_file = NULL; + struct cifsFileInfo *open_file = NULL; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; struct cifsInodeInfo *cifsInode; @@ -534,21 +547,19 @@ full_path = build_path_from_dentry(dirent); rc = CIFSSMBSetEOF(xid, pTcon, full_path, inode->i_size,FALSE, cifs_sb->local_nls); - cFYI(1,("\nSetEOF (truncate) rc = %d",rc)); - if(rc == -ETXTBSY) { - cifsInode = CIFS_I(inode); - if(!list_empty(&(cifsInode->openFileList))) { - open_file = list_entry(cifsInode->openFileList.next, - struct cifsFileInfo, flist); - /* We could check if file is open for writing first and - also we could also override the smb pid with the pid - of the file opener when sending the CIFS request */ - rc = CIFSSMBSetFileSize(xid, pTcon, inode->i_size, - open_file->netfid,open_file->pid,FALSE); - } else { - cFYI(1,("\nNo open files to get file handle from")); - } - } + cFYI(1,("\nSetEOF (truncate) rc = %d",rc)); + if(rc == -ETXTBSY) { + cifsInode = CIFS_I(inode); + if(!list_empty(&(cifsInode->openFileList))) { + open_file = list_entry(cifsInode->openFileList.next, + struct cifsFileInfo, flist); + /* We could check if file is open for writing first */ + rc = CIFSSMBSetFileSize(xid, pTcon, inode->i_size, + open_file->netfid,open_file->pid,FALSE); + } else { + cFYI(1,("\nNo open files to get file handle from")); + } + } if (!rc) CIFSSMBSetEOF(xid,pTcon,full_path,inode->i_size,TRUE,cifs_sb->local_nls); /* allocation size setting seems optional so ignore return code */ @@ -567,7 +578,7 @@ struct cifsTconInfo *pTcon; char *full_path = NULL; int rc = -EACCES; - struct cifsFileInfo *open_file = NULL; + struct cifsFileInfo *open_file = NULL; FILE_BASIC_INFO time_buf; int set_time = FALSE; __u64 mode = 0xFFFFFFFFFFFFFFFF; @@ -593,19 +604,19 @@ if (attrs->ia_valid & ATTR_SIZE) { rc = CIFSSMBSetEOF(xid, pTcon, full_path, attrs->ia_size,FALSE, cifs_sb->local_nls); - cFYI(1,("\nSetEOF (setattrs) rc = %d",rc)); + cFYI(1,("\nSetEOF (setattrs) rc = %d",rc)); - if(rc == -ETXTBSY) { - if(!list_empty(&(cifsInode->openFileList))) { - open_file = list_entry(cifsInode->openFileList.next, - struct cifsFileInfo, flist); + if(rc == -ETXTBSY) { + if(!list_empty(&(cifsInode->openFileList))) { + open_file = list_entry(cifsInode->openFileList.next, + struct cifsFileInfo, flist); /* We could check if file is open for writing first */ - rc = CIFSSMBSetFileSize(xid, pTcon, attrs->ia_size, - open_file->netfid,open_file->pid,FALSE); - } else { - cFYI(1,("\nNo open files to get file handle from")); - } - } + rc = CIFSSMBSetFileSize(xid, pTcon, attrs->ia_size, + open_file->netfid,open_file->pid,FALSE); + } else { + cFYI(1,("\nNo open files to get file handle from")); + } + } /* Set Allocation Size of file - might not even need to call the following but might as well and it does not hurt if it fails */ CIFSSMBSetEOF(xid, pTcon, full_path, attrs->ia_size, TRUE, cifs_sb->local_nls); @@ -684,5 +695,4 @@ cFYI(1, ("In cifs_delete_inode, inode = 0x%p\n", inode)); /* may have to add back in when safe distributed caching of directories via e.g. FindNotify added */ - } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/cifs/md5.h linux-2.5-bk/fs/cifs/md5.h --- linux-2.5.45/fs/cifs/md5.h Wed Oct 30 16:41:38 2002 +++ linux-2.5-bk/fs/cifs/md5.h Thu Oct 31 22:12:58 2002 @@ -22,17 +22,17 @@ void MD5Init(struct MD5Context *context); void MD5Update(struct MD5Context *context, unsigned char const *buf, - unsigned len); + unsigned len); void MD5Final(unsigned char digest[16], struct MD5Context *context); /* The following definitions come from lib/hmacmd5.c */ void hmac_md5_init_rfc2104(unsigned char *key, int key_len, - struct HMACMD5Context *ctx); + struct HMACMD5Context *ctx); void hmac_md5_init_limK_to_64(const unsigned char *key, int key_len, - struct HMACMD5Context *ctx); + struct HMACMD5Context *ctx); void hmac_md5_update(const unsigned char *text, int text_len, - struct HMACMD5Context *ctx); + struct HMACMD5Context *ctx); void hmac_md5_final(unsigned char *digest, struct HMACMD5Context *ctx); void hmac_md5(unsigned char key[16], unsigned char *data, int data_len, - unsigned char *digest); + unsigned char *digest); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/cifs/misc.c linux-2.5-bk/fs/cifs/misc.c --- linux-2.5.45/fs/cifs/misc.c Wed Oct 30 16:43:43 2002 +++ linux-2.5-bk/fs/cifs/misc.c Thu Oct 31 22:12:58 2002 @@ -28,7 +28,6 @@ extern kmem_cache_t *cifs_req_cachep; -static DECLARE_MUTEX(GlobalMid_Sem); /* also protects XID globals */ __u16 GlobalMid; /* multiplex id - rotating counter */ /* The xid serves as a useful identifier for each incoming vfs request, @@ -42,21 +41,21 @@ { unsigned int xid; - down(&GlobalMid_Sem); + write_lock(&GlobalMid_Lock); GlobalTotalActiveXid++; if (GlobalTotalActiveXid > GlobalMaxActiveXid) GlobalMaxActiveXid = GlobalTotalActiveXid; /* keep high water mark for number of simultaneous vfs ops in our filesystem */ xid = GlobalCurrentXid++; - up(&GlobalMid_Sem); + write_unlock(&GlobalMid_Lock); return xid; } void _FreeXid(unsigned int xid) { - down(&GlobalMid_Sem); + write_lock(&GlobalMid_Lock); GlobalTotalActiveXid--; - up(&GlobalMid_Sem); + write_unlock(&GlobalMid_Lock); } struct cifsSesInfo * @@ -69,9 +68,11 @@ GFP_KERNEL); if (ret_buf) { memset(ret_buf, 0, sizeof (struct cifsSesInfo)); + write_lock(&GlobalSMBSeslock); atomic_inc(&sesInfoAllocCount); list_add(&ret_buf->cifsSessionList, &GlobalSMBSessionList); init_MUTEX(&ret_buf->sesSem); + write_unlock(&GlobalSMBSeslock); } return ret_buf; } @@ -84,8 +85,10 @@ return; } + write_lock(&GlobalSMBSeslock); atomic_dec(&sesInfoAllocCount); list_del(&buf_to_free->cifsSessionList); + write_unlock(&GlobalSMBSeslock); if (buf_to_free->serverOS) kfree(buf_to_free->serverOS); if (buf_to_free->serverDomain) @@ -104,11 +107,13 @@ GFP_KERNEL); if (ret_buf) { memset(ret_buf, 0, sizeof (struct cifsTconInfo)); + write_lock(&GlobalSMBSeslock); atomic_inc(&tconInfoAllocCount); list_add(&ret_buf->cifsConnectionList, &GlobalTreeConnectionList); - INIT_LIST_HEAD(&ret_buf->openFileList); + INIT_LIST_HEAD(&ret_buf->openFileList); init_MUTEX(&ret_buf->tconSem); + write_unlock(&GlobalSMBSeslock); } return ret_buf; } @@ -120,9 +125,10 @@ cFYI(1, ("\nNull buffer passed to tconInfoFree")); return; } - + write_lock(&GlobalSMBSeslock); atomic_dec(&tconInfoAllocCount); list_del(&buf_to_free->cifsConnectionList); + write_unlock(&GlobalSMBSeslock); if (buf_to_free->nativeFileSystem) kfree(buf_to_free->nativeFileSystem); kfree(buf_to_free); @@ -203,9 +209,10 @@ buffer->Pid = tmp & 0xFFFF; tmp >>= 16; buffer->PidHigh = tmp & 0xFFFF; - down(&GlobalMid_Sem); + write_lock(&GlobalMid_Lock); GlobalMid++; buffer->Mid = GlobalMid; + write_unlock(&GlobalMid_Lock); if (treeCon) { buffer->Tid = treeCon->tid; if (treeCon->ses) { @@ -218,13 +225,11 @@ } if (treeCon->Flags & SMB_SHARE_IS_IN_DFS) buffer->Flags2 |= SMBFLG2_DFS; - if(treeCon->ses->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) - buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; - + if(treeCon->ses->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; } /* endian conversion of flags is now done just before sending */ - up(&GlobalMid_Sem); buffer->WordCount = (char) word_count; return; } @@ -233,17 +238,18 @@ checkSMBhdr(struct smb_hdr *smb, __u16 mid) { /* Make sure that this really is an SMB, that it is a response, - and that the message ids match */ - if ((*(unsigned int *) smb->Protocol == cpu_to_le32(0x424d53ff)) && (mid == smb->Mid)) { - if(smb->Flags & SMBFLG_RESPONSE) - return 0; - else { - /* only one valid case where server sends us request */ - if(smb->Command == SMB_COM_LOCKING_ANDX) - return 0; - else - cERROR(1, ("\n Rcvd Request not response ")); - } + and that the message ids match */ + if ((*(unsigned int *) smb->Protocol == cpu_to_le32(0x424d53ff)) && + (mid == smb->Mid)) { + if(smb->Flags & SMBFLG_RESPONSE) + return 0; + else { + /* only one valid case where server sends us request */ + if(smb->Command == SMB_COM_LOCKING_ANDX) + return 0; + else + cERROR(1, ("\n Rcvd Request not response ")); + } } else { /* bad signature or mid */ if (*(unsigned int *) smb->Protocol != cpu_to_le32(0x424d53ff)) cERROR(1, @@ -252,8 +258,8 @@ if (mid != smb->Mid) cERROR(1, ("\n Mids do not match \n")); } - cERROR(1, ("\nCIFS: bad smb detected. The Mid=%d\n", smb->Mid)); - return 1; + cERROR(1, ("\nCIFS: bad smb detected. The Mid=%d\n", smb->Mid)); + return 1; } int @@ -269,7 +275,7 @@ cERROR(1, ("\n Length less than 2 + sizeof smb_hdr ")); if ((length >= sizeof (struct smb_hdr) - 1) && (smb->Status.CifsError != 0)) - return 0; /* this is ok - some error cases do not return wct and bcc */ + return 0; /* some error cases do not return wct and bcc */ } if (4 + ntohl(smb->smb_buf_length) > @@ -298,30 +304,42 @@ int is_valid_oplock_break(struct smb_hdr *buf) { - struct smb_com_lock_req * pSMB = (struct smb_com_lock_req *)buf; - - /* could add check for smb response flag 0x80 */ - cFYI(1,("\nChecking for oplock break")); - if(pSMB->hdr.Command != SMB_COM_LOCKING_ANDX) - return FALSE; - if(pSMB->hdr.Flags & SMBFLG_RESPONSE) - return FALSE; /* server sends us "request" here */ - if(pSMB->hdr.WordCount != 8) - return FALSE; - - cFYI(1,(" oplock type 0x%d level 0x%d",pSMB->LockType,pSMB->OplockLevel)); - if(!(pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE)) - return FALSE; - - /* BB Add following logic: - 1) look up tcon based on tid & uid - 2) look up inode from tcon->openFileList->file->f_dentry->d_inode - 3) flush dirty pages and cached byte range locks and mark inode - 4) depending on break type change to r/o caching or no caching - 5) send oplock break response to server */ - cFYI(1,("\nNeed to process oplock break ")); - - return TRUE; + struct smb_com_lock_req * pSMB = (struct smb_com_lock_req *)buf; + struct list_head *tmp; + struct cifsTconInfo *tcon; + + /* could add check for smb response flag 0x80 */ + cFYI(1,("\nChecking for oplock break")); + if(pSMB->hdr.Command != SMB_COM_LOCKING_ANDX) + return FALSE; + if(pSMB->hdr.Flags & SMBFLG_RESPONSE) + return FALSE; /* server sends us "request" here */ + if(pSMB->hdr.WordCount != 8) + return FALSE; + + cFYI(1,(" oplock type 0x%d level 0x%d",pSMB->LockType,pSMB->OplockLevel)); + if(!(pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE)) + return FALSE; + + /* look up tcon based on tid & uid */ + read_lock(&GlobalSMBSeslock); + list_for_each(tmp, &GlobalTreeConnectionList) { + tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); + if (tcon->tid == buf->Tid) + if(tcon->ses->Suid == buf->Uid) { + /* BB Add following logic: + 2) look up inode from tcon->openFileList->file->f_dentry->d_inode + 3) flush dirty pages and cached byte range locks and mark inode + 4) depending on break type change to r/o caching or no caching + 5) send oplock break response to server */ + read_unlock(&GlobalSMBSeslock); + cFYI(1,("\nFound matching connection, process oplock break")); + return TRUE; + } + } + read_unlock(&GlobalSMBSeslock); + cFYI(1,("\nProcessing oplock break for non-existent connection")); + return TRUE; } void diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/cifs/netmisc.c linux-2.5-bk/fs/cifs/netmisc.c --- linux-2.5.45/fs/cifs/netmisc.c Wed Oct 30 16:43:39 2002 +++ linux-2.5-bk/fs/cifs/netmisc.c Thu Oct 31 22:12:58 2002 @@ -745,7 +745,28 @@ ERRDOS, ERRnoaccess, 0xc0000290}, { ERRDOS, ERRbadfunc, 0xc000029c},}; -void +/***************************************************************************** + Print an error message from the status code + *****************************************************************************/ +static void +cifs_print_status(__u32 status_code) +{ + int idx = 0; + + printk("\nStatus code returned: 0x%08x", status_code); + + while (nt_errs[idx].nt_errstr != NULL) { + if (((nt_errs[idx].nt_errcode) & 0xFFFFFF) == + (status_code & 0xFFFFFF)) { + printk(nt_errs[idx].nt_errstr); + } + idx++; + } + return; +} + + +static void ntstatus_to_dos(__u32 ntstatus, __u8 * eclass, __u16 * ecode) { int i; @@ -781,9 +802,9 @@ if (smb->Flags2 & SMBFLG2_ERR_STATUS) { /* translate the newer STATUS codes to old style errors and then to POSIX errors */ - cFYI(1, - (" !!Mapping cifs error code %d ", smb->Status.CifsError)); smb->Status.CifsError = le32_to_cpu(smb->Status.CifsError); + if(cifsFYI) + cifs_print_status(smb->Status.CifsError); ntstatus_to_dos(smb->Status.CifsError, &smberrclass, &smberrcode); } else { diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/cifs/nterr.c linux-2.5-bk/fs/cifs/nterr.c --- linux-2.5.45/fs/cifs/nterr.c Wed Oct 30 16:41:31 2002 +++ linux-2.5-bk/fs/cifs/nterr.c Thu Oct 31 22:12:58 2002 @@ -20,14 +20,9 @@ */ /* NT error codes - see nterr.h */ -#include "nterr.h" #include #include - -struct nt_err_code_struct { - char *nt_errstr; - __u32 nt_errcode; -}; +#include "nterr.h" const struct nt_err_code_struct nt_errs[] = { {"NT_STATUS_OK", NT_STATUS_OK}, @@ -690,23 +685,3 @@ {"STATUS_SOME_UNMAPPED", STATUS_SOME_UNMAPPED}, {NULL, 0} }; - -/***************************************************************************** - Print an error message from the status code - *****************************************************************************/ -/* void -cifs_print_status(__u32 status_code) -{ - int idx = 0; - - printk("\nStatus code returned: 0x%08x", status_code); - - while (nt_errs[idx].nt_errstr != NULL) { - if (((nt_errs[idx].nt_errcode) & 0xFFFFFF) == - (status_code & 0xFFFFFF)) { - printk(nt_errs[idx].nt_errstr); - } - idx++; - } - return; -} */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/cifs/nterr.h linux-2.5-bk/fs/cifs/nterr.h --- linux-2.5.45/fs/cifs/nterr.h Wed Oct 30 16:41:55 2002 +++ linux-2.5-bk/fs/cifs/nterr.h Thu Oct 31 22:12:58 2002 @@ -22,8 +22,17 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + #ifndef _NTERR_H #define _NTERR_H + +struct nt_err_code_struct { + char *nt_errstr; + __u32 nt_errcode; +}; + +extern const struct nt_err_code_struct nt_errs[]; /* Win32 Status codes. */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/cifs/smbencrypt.c linux-2.5-bk/fs/cifs/smbencrypt.c --- linux-2.5.45/fs/cifs/smbencrypt.c Wed Oct 30 16:42:55 2002 +++ linux-2.5-bk/fs/cifs/smbencrypt.c Thu Oct 31 22:12:58 2002 @@ -205,19 +205,26 @@ /* Does the NTLMv2 owfs of a user's password */ void ntv2_owf_gen(const unsigned char owf[16], const char *user_n, - const char *domain_n, unsigned char kr_buf[16], - const struct nls_table *nls_codepage) + const char *domain_n, unsigned char kr_buf[16], + const struct nls_table *nls_codepage) { - wchar_t user_u[1024]; - wchar_t dom_u[1024]; + wchar_t * user_u; + wchar_t * dom_u; + int user_l, domain_l; struct HMACMD5Context ctx; + /* might as well do one alloc to hold both (user_u and dom_u) */ + user_u = kmalloc(2048 * sizeof(wchar_t),GFP_KERNEL); + if(user_u == NULL) + return; + dom_u = user_u + 1024; + /* push_ucs2(NULL, user_u, user_n, (user_l+1)*2, STR_UNICODE|STR_NOALIGN|STR_TERMINATE|STR_UPPER); push_ucs2(NULL, dom_u, domain_n, (domain_l+1)*2, STR_UNICODE|STR_NOALIGN|STR_TERMINATE|STR_UPPER); */ - /* do not think it is supposed to be uppercased */ - int user_l = cifs_strtoUCS(user_u, user_n, 511, nls_codepage); - int domain_l = cifs_strtoUCS(dom_u, domain_n, 511, nls_codepage); + /* do not think it is supposed to be uppercased */ + user_l = cifs_strtoUCS(user_u, user_n, 511, nls_codepage); + domain_l = cifs_strtoUCS(dom_u, domain_n, 511, nls_codepage); user_l++; /* trailing null */ domain_l++; @@ -234,6 +241,7 @@ dump_data(100, owf, 16); dump_data(100, kr_buf, 16); #endif + kfree(user_u); } /* Does the des encryption from the NT or LM MD4 hash. */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/cifs/transport.c linux-2.5-bk/fs/cifs/transport.c --- linux-2.5.45/fs/cifs/transport.c Wed Oct 30 16:42:22 2002 +++ linux-2.5-bk/fs/cifs/transport.c Thu Oct 31 22:12:58 2002 @@ -59,9 +59,11 @@ temp->tsk = current; } if (ses->status == CifsGood) { + write_lock(&GlobalMid_Lock); list_add_tail(&temp->qhead, &ses->server->pending_mid_q); atomic_inc(&midCount); temp->midState = MID_REQUEST_ALLOCATED; + write_unlock(&GlobalMid_Lock); } else { /* BB add reconnect code here BB */ cERROR(1, @@ -77,11 +79,13 @@ DeleteMidQEntry(struct mid_q_entry *midEntry) { /* BB add spinlock to protect midq for each session BB */ + write_lock(&GlobalMid_Lock); midEntry->midState = MID_FREE; - buf_release(midEntry->resp_buf); list_del(&midEntry->qhead); - kmem_cache_free(cifs_mid_cachep, midEntry); atomic_dec(&midCount); + write_unlock(&GlobalMid_Lock); + buf_release(midEntry->resp_buf); + kmem_cache_free(cifs_mid_cachep, midEntry); } int @@ -93,8 +97,8 @@ struct iovec iov; mm_segment_t temp_fs; - if(ssocket == NULL) - return -ENOTSOCK; /* BB eventually add reconnect code here */ + if(ssocket == NULL) + return -ENOTSOCK; /* BB eventually add reconnect code here */ /* ssocket->sk->allocation = GFP_BUFFER; *//* BB is this spurious? */ iov.iov_base = smb_buffer; iov.iov_len = smb_buf_length + 4; @@ -159,8 +163,6 @@ rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length, (struct sockaddr *) &(ses->server->sockAddr)); - cFYI(1, ("\ncifs smb_send rc %d", rc)); /* BB remove */ - /* BB add code to wait for response and copy to out_buf */ if (long_op > 1) /* writes past end of file can take a looooooong time */ timeout = 300 * HZ; else if (long_op == 1) @@ -174,10 +176,9 @@ /* Replace above line with wait_event to get rid of sleep_on per lk guidelines */ timeout = wait_event_interruptible_timeout(ses->server->response_q, - midQ-> - midState & - MID_RESPONSE_RECEIVED, - 15 * HZ); + midQ-> + midState & MID_RESPONSE_RECEIVED, + timeout); cFYI(1, (" with timeout %ld and Out_buf: %p midQ->resp_buf: %p ", timeout, out_buf, midQ->resp_buf)); @@ -240,6 +241,8 @@ rc = -EIO; } - DeleteMidQEntry(midQ); /* BB what if process is killed ? - BB add background daemon to clean up Mid entries from killed processes BB test killing process with active mid */ + DeleteMidQEntry(midQ); /* BB what if process is killed? + - BB add background daemon to clean up Mid entries from + killed processes & test killing process with active mid */ return rc; } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/dcookies.c linux-2.5-bk/fs/dcookies.c --- linux-2.5.45/fs/dcookies.c Wed Oct 30 16:41:33 2002 +++ linux-2.5-bk/fs/dcookies.c Thu Oct 31 22:12:58 2002 @@ -8,7 +8,7 @@ * non-transitory that can be processed at a later date. * This is done by locking the dentry/vfsmnt pair in the * kernel until released by the tasks needing the persistent - * objects. The tag is simply an u32 that refers + * objects. The tag is simply an unsigned long that refers * to the pair and can be looked up from userspace. */ @@ -46,19 +46,19 @@ /* The dentry is locked, its address will do for the cookie */ -static inline u32 dcookie_value(struct dcookie_struct * dcs) +static inline unsigned long dcookie_value(struct dcookie_struct * dcs) { - return (u32)dcs->dentry; + return (unsigned long)dcs->dentry; } -static size_t dcookie_hash(u32 dcookie) +static size_t dcookie_hash(unsigned long dcookie) { - return (dcookie >> 2) & (hash_size - 1); + return (dcookie >> L1_CACHE_SHIFT) & (hash_size - 1); } -static struct dcookie_struct * find_dcookie(u32 dcookie) +static struct dcookie_struct * find_dcookie(unsigned long dcookie) { struct dcookie_struct * found = 0; struct dcookie_struct * dcs; @@ -109,7 +109,7 @@ * value for a dentry/vfsmnt pair. */ int get_dcookie(struct dentry * dentry, struct vfsmount * vfsmnt, - u32 * cookie) + unsigned long * cookie) { int err = 0; struct dcookie_struct * dcs; @@ -142,11 +142,12 @@ /* And here is where the userspace process can look up the cookie value * to retrieve the path. */ -asmlinkage int sys_lookup_dcookie(u32 cookie, char * buf, size_t len) +asmlinkage int sys_lookup_dcookie(u64 cookie64, char * buf, size_t len) { + unsigned long cookie = (unsigned long)cookie64; + int err = -EINVAL; char * kbuf; char * path; - int err = -EINVAL; size_t pathlen; struct dcookie_struct * dcs; @@ -170,19 +171,18 @@ kbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!kbuf) goto out; - memset(kbuf, 0, PAGE_SIZE); /* FIXME: (deleted) ? */ path = d_path(dcs->dentry, dcs->vfsmnt, kbuf, PAGE_SIZE); - err = 0; - + err = -ERANGE; + pathlen = kbuf + PAGE_SIZE - path; - if (len > pathlen) - len = pathlen; - - if (copy_to_user(buf, path, len)) - err = -EFAULT; + if (pathlen <= len) { + err = pathlen; + if (copy_to_user(buf, path, pathlen)) + err = -EFAULT; + } kfree(kbuf); out: diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/driverfs/Makefile linux-2.5-bk/fs/driverfs/Makefile --- linux-2.5.45/fs/driverfs/Makefile Wed Oct 30 16:43:48 2002 +++ linux-2.5-bk/fs/driverfs/Makefile Wed Dec 31 16:00:00 1969 @@ -1,9 +0,0 @@ -# -# Makefile for the driverfs virtual filesystem. -# - -export-objs := inode.o - -obj-y := inode.o - -include $(TOPDIR)/Rules.make diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/driverfs/inode.c linux-2.5-bk/fs/driverfs/inode.c --- linux-2.5.45/fs/driverfs/inode.c Wed Oct 30 16:41:56 2002 +++ linux-2.5-bk/fs/driverfs/inode.c Wed Dec 31 16:00:00 1969 @@ -1,708 +0,0 @@ -/* - * driverfs.c - The device driver file system - * - * Copyright (c) 2001 Patrick Mochel - * - * 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 is a simple, ram-based filesystem, which allows kernel - * callbacks for read/write of files. - * - * Please see Documentation/filesystems/driverfs.txt for more information. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#undef DEBUG - -#ifdef DEBUG -# define DBG(x...) printk(x) -#else -# define DBG(x...) -#endif - -/* Random magic number */ -#define DRIVERFS_MAGIC 0x42454552 - -static struct super_operations driverfs_ops; -static struct file_operations driverfs_file_operations; -static struct inode_operations driverfs_dir_inode_operations; -static struct address_space_operations driverfs_aops; - -static struct vfsmount *driverfs_mount; -static spinlock_t mount_lock = SPIN_LOCK_UNLOCKED; -static int mount_count = 0; - -static struct backing_dev_info driverfs_backing_dev_info = { - .ra_pages = 0, /* No readahead */ - .memory_backed = 1, /* Does not contribute to dirty memory */ -}; - -static int driverfs_readpage(struct file *file, struct page * page) -{ - if (!PageUptodate(page)) { - void *kaddr = kmap_atomic(page, KM_USER0); - - memset(kaddr, 0, PAGE_CACHE_SIZE); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); - SetPageUptodate(page); - } - unlock_page(page); - return 0; -} - -static int driverfs_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to) -{ - if (!PageUptodate(page)) { - void *kaddr = kmap_atomic(page, KM_USER0); - - memset(kaddr, 0, PAGE_CACHE_SIZE); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); - SetPageUptodate(page); - } - return 0; -} - -static int driverfs_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to) -{ - struct inode *inode = page->mapping->host; - loff_t pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to; - - set_page_dirty(page); - if (pos > inode->i_size) - inode->i_size = pos; - return 0; -} - - -struct inode *driverfs_get_inode(struct super_block *sb, int mode, int dev) -{ - struct inode *inode = new_inode(sb); - - if (inode) { - inode->i_mode = mode; - inode->i_uid = current->fsuid; - inode->i_gid = current->fsgid; - inode->i_blksize = PAGE_CACHE_SIZE; - inode->i_blocks = 0; - inode->i_rdev = NODEV; - inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; - inode->i_mapping->a_ops = &driverfs_aops; - inode->i_mapping->backing_dev_info = &driverfs_backing_dev_info; - switch (mode & S_IFMT) { - default: - init_special_inode(inode, mode, dev); - break; - case S_IFREG: - inode->i_fop = &driverfs_file_operations; - break; - case S_IFDIR: - inode->i_op = &driverfs_dir_inode_operations; - inode->i_fop = &simple_dir_operations; - - /* directory inodes start off with i_nlink == 2 (for "." entry) */ - inode->i_nlink++; - break; - case S_IFLNK: - inode->i_op = &page_symlink_inode_operations; - break; - } - } - return inode; -} - -static int driverfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev) -{ - struct inode *inode; - int error = -ENOSPC; - - if (dentry->d_inode) - return -EEXIST; - - /* only allow create if ->d_fsdata is not NULL (so we can assume it - * comes from the driverfs API below. */ - inode = driverfs_get_inode(dir->i_sb, mode, dev); - if (inode) { - d_instantiate(dentry, inode); - error = 0; - } - return error; -} - -static int driverfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) -{ - int res; - mode = (mode & (S_IRWXUGO|S_ISVTX)) | S_IFDIR; - res = driverfs_mknod(dir, dentry, mode, 0); - if (!res) - dir->i_nlink++; - return res; -} - -static int driverfs_create(struct inode *dir, struct dentry *dentry, int mode) -{ - int res; - mode = (mode & S_IALLUGO) | S_IFREG; - res = driverfs_mknod(dir, dentry, mode, 0); - return res; -} - -static int driverfs_symlink(struct inode * dir, struct dentry *dentry, const char * symname) -{ - struct inode *inode; - int error = -ENOSPC; - - if (dentry->d_inode) - return -EEXIST; - - inode = driverfs_get_inode(dir->i_sb, S_IFLNK|S_IRWXUGO, 0); - if (inode) { - int l = strlen(symname)+1; - error = page_symlink(inode, symname, l); - if (!error) { - d_instantiate(dentry, inode); - dget(dentry); - } else - iput(inode); - } - return error; -} - -static inline int driverfs_positive(struct dentry *dentry) -{ - return (dentry->d_inode && !d_unhashed(dentry)); -} - -static int driverfs_empty(struct dentry *dentry) -{ - struct list_head *list; - - spin_lock(&dcache_lock); - - list_for_each(list, &dentry->d_subdirs) { - struct dentry *de = list_entry(list, struct dentry, d_child); - if (driverfs_positive(de)) { - spin_unlock(&dcache_lock); - return 0; - } - } - - spin_unlock(&dcache_lock); - return 1; -} - -static int driverfs_unlink(struct inode *dir, struct dentry *dentry) -{ - struct inode *inode = dentry->d_inode; - down(&inode->i_sem); - dentry->d_inode->i_nlink--; - up(&inode->i_sem); - d_invalidate(dentry); - dput(dentry); - return 0; -} - -/** - * driverfs_read_file - "read" data from a file. - * @file: file pointer - * @buf: buffer to fill - * @count: number of bytes to read - * @ppos: starting offset in file - * - * Userspace wants data from a file. It is up to the creator of the file to - * provide that data. - * There is a struct device_attribute embedded in file->private_data. We - * obtain that and check if the read callback is implemented. If so, we call - * it, passing the data field of the file entry. - * Said callback is responsible for filling the buffer and returning the number - * of bytes it put in it. We update @ppos correctly. - */ -static ssize_t -driverfs_read_file(struct file *file, char *buf, size_t count, loff_t *ppos) -{ - struct attribute * attr = file->f_dentry->d_fsdata; - struct driver_dir_entry * dir; - unsigned char *page; - ssize_t retval = 0; - - dir = file->f_dentry->d_parent->d_fsdata; - if (!dir->ops->show) - return 0; - - if (count > PAGE_SIZE) - count = PAGE_SIZE; - - page = (unsigned char*)__get_free_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - - while (count > 0) { - ssize_t len; - - len = dir->ops->show(dir,attr,page,count,*ppos); - - if (len <= 0) { - if (len < 0) - retval = len; - break; - } else if (len > count) - len = count; - - if (copy_to_user(buf,page,len)) { - retval = -EFAULT; - break; - } - - *ppos += len; - count -= len; - buf += len; - retval += len; - } - free_page((unsigned long)page); - return retval; -} - -/** - * driverfs_write_file - "write" to a file - * @file: file pointer - * @buf: data to write - * @count: number of bytes - * @ppos: starting offset - * - * Similarly to driverfs_read_file, we act essentially as a bit pipe. - * We check for a "write" callback in file->private_data, and pass - * @buffer, @count, @ppos, and the file entry's data to the callback. - * The number of bytes written is returned, and we handle updating - * @ppos properly. - */ -static ssize_t -driverfs_write_file(struct file *file, const char *buf, size_t count, loff_t *ppos) -{ - struct attribute * attr = file->f_dentry->d_fsdata; - struct driver_dir_entry * dir; - ssize_t retval = 0; - char * page; - - dir = file->f_dentry->d_parent->d_fsdata; - - page = (char *)__get_free_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - - if (count >= PAGE_SIZE) - count = PAGE_SIZE - 1; - if (copy_from_user(page,buf,count)) - goto done; - *(page + count) = '\0'; - - while (count > 0) { - ssize_t len; - - len = dir->ops->store(dir,attr,page + retval,count,*ppos); - - if (len <= 0) { - if (len < 0) - retval = len; - break; - } - retval += len; - count -= len; - *ppos += len; - buf += len; - } - done: - free_page((unsigned long)page); - return retval; -} - -static loff_t -driverfs_file_lseek(struct file *file, loff_t offset, int orig) -{ - loff_t retval = -EINVAL; - - down(&file->f_dentry->d_inode->i_sem); - switch(orig) { - case 0: - if (offset > 0) { - file->f_pos = offset; - retval = file->f_pos; - } - break; - case 1: - if ((offset + file->f_pos) > 0) { - file->f_pos += offset; - retval = file->f_pos; - } - break; - default: - break; - } - up(&file->f_dentry->d_inode->i_sem); - return retval; -} - -static int driverfs_open_file(struct inode * inode, struct file * filp) -{ - struct driver_dir_entry * dir; - int error = 0; - - dir = (struct driver_dir_entry *)filp->f_dentry->d_parent->d_fsdata; - if (dir) { - struct attribute * attr = filp->f_dentry->d_fsdata; - if (attr && dir->ops) { - if (dir->ops->open) - error = dir->ops->open(dir); - goto Done; - } - } - error = -EINVAL; - Done: - return error; -} - -static int driverfs_release(struct inode * inode, struct file * filp) -{ - struct driver_dir_entry * dir; - dir = (struct driver_dir_entry *)filp->f_dentry->d_parent->d_fsdata; - if (dir->ops->close) - dir->ops->close(dir); - return 0; -} - -static struct file_operations driverfs_file_operations = { - .read = driverfs_read_file, - .write = driverfs_write_file, - .llseek = driverfs_file_lseek, - .open = driverfs_open_file, - .release = driverfs_release, -}; - -static struct inode_operations driverfs_dir_inode_operations = { - .lookup = simple_lookup, -}; - -static struct address_space_operations driverfs_aops = { - .readpage = driverfs_readpage, - .writepage = fail_writepage, - .prepare_write = driverfs_prepare_write, - .commit_write = driverfs_commit_write -}; - -static struct super_operations driverfs_ops = { - .statfs = simple_statfs, - .drop_inode = generic_delete_inode, -}; - -static int driverfs_fill_super(struct super_block *sb, void *data, int silent) -{ - struct inode *inode; - struct dentry *root; - - sb->s_blocksize = PAGE_CACHE_SIZE; - sb->s_blocksize_bits = PAGE_CACHE_SHIFT; - sb->s_magic = DRIVERFS_MAGIC; - sb->s_op = &driverfs_ops; - inode = driverfs_get_inode(sb, S_IFDIR | 0755, 0); - - if (!inode) { - DBG("%s: could not get inode!\n",__FUNCTION__); - return -ENOMEM; - } - - root = d_alloc_root(inode); - if (!root) { - DBG("%s: could not get root dentry!\n",__FUNCTION__); - iput(inode); - return -ENOMEM; - } - sb->s_root = root; - return 0; -} - -static struct super_block *driverfs_get_sb(struct file_system_type *fs_type, - int flags, char *dev_name, void *data) -{ - return get_sb_single(fs_type, flags, data, driverfs_fill_super); -} - -static struct file_system_type driverfs_fs_type = { - .owner = THIS_MODULE, - .name = "driverfs", - .get_sb = driverfs_get_sb, - .kill_sb = kill_litter_super, -}; - -static int get_mount(void) -{ - struct vfsmount * mnt; - - spin_lock(&mount_lock); - if (driverfs_mount) { - mntget(driverfs_mount); - ++mount_count; - spin_unlock(&mount_lock); - goto go_ahead; - } - - spin_unlock(&mount_lock); - mnt = kern_mount(&driverfs_fs_type); - - if (IS_ERR(mnt)) { - printk(KERN_ERR "driverfs: could not mount!\n"); - return -ENODEV; - } - - spin_lock(&mount_lock); - if (!driverfs_mount) { - driverfs_mount = mnt; - ++mount_count; - spin_unlock(&mount_lock); - goto go_ahead; - } - - mntget(driverfs_mount); - ++mount_count; - spin_unlock(&mount_lock); - - go_ahead: - DBG("driverfs: mount_count = %d\n",mount_count); - return 0; -} - -static void put_mount(void) -{ - struct vfsmount * mnt; - - spin_lock(&mount_lock); - mnt = driverfs_mount; - --mount_count; - if (!mount_count) - driverfs_mount = NULL; - spin_unlock(&mount_lock); - mntput(mnt); - DBG("driverfs: mount_count = %d\n",mount_count); -} - -static int __init driverfs_init(void) -{ - return register_filesystem(&driverfs_fs_type); -} - -core_initcall(driverfs_init); - -static struct dentry * get_dentry(struct dentry * parent, const char * name) -{ - struct qstr qstr; - - qstr.name = name; - qstr.len = strlen(name); - qstr.hash = full_name_hash(name,qstr.len); - return lookup_hash(&qstr,parent); -} - -/** - * driverfs_create_dir - create a directory in the filesystem - * @entry: directory entry - * @parent: parent directory entry - */ -int -driverfs_create_dir(struct driver_dir_entry * entry, - struct driver_dir_entry * parent) -{ - struct dentry * dentry = NULL; - struct dentry * parent_dentry; - int error = 0; - - if (!entry) - return -EINVAL; - - get_mount(); - - parent_dentry = parent ? parent->dentry : NULL; - - if (!parent_dentry) - if (driverfs_mount && driverfs_mount->mnt_sb) - parent_dentry = driverfs_mount->mnt_sb->s_root; - - if (!parent_dentry) { - put_mount(); - return -EFAULT; - } - - down(&parent_dentry->d_inode->i_sem); - dentry = get_dentry(parent_dentry,entry->name); - if (!IS_ERR(dentry)) { - dentry->d_fsdata = (void *) entry; - entry->dentry = dentry; - error = driverfs_mkdir(parent_dentry->d_inode,dentry,entry->mode); - } else - error = PTR_ERR(dentry); - up(&parent_dentry->d_inode->i_sem); - - if (error) - put_mount(); - return error; -} - -/** - * driverfs_create_file - create a file - * @entry: structure describing the file - * @parent: directory to create it in - */ -int -driverfs_create_file(struct attribute * entry, - struct driver_dir_entry * parent) -{ - struct dentry * dentry; - int error = 0; - - if (!entry || !parent) - return -EINVAL; - - if (!parent->dentry) { - put_mount(); - return -EINVAL; - } - - down(&parent->dentry->d_inode->i_sem); - dentry = get_dentry(parent->dentry,entry->name); - if (!IS_ERR(dentry)) { - dentry->d_fsdata = (void *)entry; - error = driverfs_create(parent->dentry->d_inode,dentry,entry->mode); - } else - error = PTR_ERR(dentry); - up(&parent->dentry->d_inode->i_sem); - return error; -} - -/** - * driverfs_create_symlink - make a symlink - * @parent: directory we're creating in - * @entry: entry describing link - * @target: place we're symlinking to - * - */ -int driverfs_create_symlink(struct driver_dir_entry * parent, - char * name, char * target) -{ - struct dentry * dentry; - int error = 0; - - if (!parent) - return -EINVAL; - - if (!parent->dentry) { - put_mount(); - return -EINVAL; - } - down(&parent->dentry->d_inode->i_sem); - dentry = get_dentry(parent->dentry,name); - if (!IS_ERR(dentry)) - error = driverfs_symlink(parent->dentry->d_inode,dentry,target); - else - error = PTR_ERR(dentry); - up(&parent->dentry->d_inode->i_sem); - return error; -} - -/** - * driverfs_remove_file - exported file removal - * @dir: directory the file supposedly resides in - * @name: name of the file - * - * Try and find the file in the dir's list. - * If it's there, call __remove_file() (above) for the dentry. - */ -void driverfs_remove_file(struct driver_dir_entry * dir, const char * name) -{ - struct dentry * dentry; - - if (!dir->dentry) - return; - - down(&dir->dentry->d_inode->i_sem); - dentry = get_dentry(dir->dentry,name); - if (!IS_ERR(dentry)) { - /* make sure dentry is really there */ - if (dentry->d_inode && - (dentry->d_parent->d_inode == dir->dentry->d_inode)) { - driverfs_unlink(dir->dentry->d_inode,dentry); - } - } - up(&dir->dentry->d_inode->i_sem); -} - -/** - * driverfs_remove_dir - exportable directory removal - * @dir: directory to remove - * - * To make sure we don't orphan anyone, first remove - * all the children in the list, then do clean up the directory. - */ -void driverfs_remove_dir(struct driver_dir_entry * dir) -{ - struct list_head * node, * next; - struct dentry * dentry = dir->dentry; - struct dentry * parent; - - if (!dentry) - goto done; - - parent = dget(dentry->d_parent); - down(&parent->d_inode->i_sem); - down(&dentry->d_inode->i_sem); - - list_for_each_safe(node,next,&dentry->d_subdirs) { - struct dentry * d = list_entry(node,struct dentry,d_child); - /* make sure dentry is still there */ - if (d->d_inode) - driverfs_unlink(dentry->d_inode,d); - } - - d_invalidate(dentry); - if (driverfs_empty(dentry)) { - dentry->d_inode->i_nlink -= 2; - dentry->d_inode->i_flags |= S_DEAD; - parent->d_inode->i_nlink--; - } - up(&dentry->d_inode->i_sem); - dput(dentry); - - up(&parent->d_inode->i_sem); - dput(parent); - done: - put_mount(); -} - -EXPORT_SYMBOL(driverfs_create_file); -EXPORT_SYMBOL(driverfs_create_symlink); -EXPORT_SYMBOL(driverfs_create_dir); -EXPORT_SYMBOL(driverfs_remove_file); -EXPORT_SYMBOL(driverfs_remove_dir); -MODULE_LICENSE("GPL"); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/exec.c linux-2.5-bk/fs/exec.c --- linux-2.5.45/fs/exec.c Wed Oct 30 16:42:27 2002 +++ linux-2.5-bk/fs/exec.c Thu Oct 31 22:12:58 2002 @@ -306,7 +306,7 @@ pte_unmap(pte); goto out; } - lru_cache_add(page); + lru_cache_add_active(page); flush_dcache_page(page); flush_page_to_ram(page); set_pte(pte, pte_mkdirty(pte_mkwrite(mk_pte(page, PAGE_COPY)))); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/ext2/Makefile linux-2.5-bk/fs/ext2/Makefile --- linux-2.5.45/fs/ext2/Makefile Wed Oct 30 16:43:34 2002 +++ linux-2.5-bk/fs/ext2/Makefile Thu Oct 31 22:12:58 2002 @@ -7,4 +7,14 @@ ext2-objs := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \ ioctl.o namei.o super.o symlink.o +export-objs += xattr.o + +ifeq ($(CONFIG_EXT2_FS_XATTR),y) +ext2-objs += xattr.o xattr_user.o +endif + +ifeq ($(CONFIG_EXT2_FS_POSIX_ACL),y) +ext2-objs += acl.o +endif + include $(TOPDIR)/Rules.make diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/ext2/acl.c linux-2.5-bk/fs/ext2/acl.c --- linux-2.5.45/fs/ext2/acl.c Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/fs/ext2/acl.c Thu Oct 31 22:12:58 2002 @@ -0,0 +1,573 @@ +/* + * linux/fs/ext2/acl.c + * + * Copyright (C) 2001 by Andreas Gruenbacher, + */ + +#include +#include +#include +#include +#include "ext2.h" +#include "xattr.h" +#include "acl.h" + +/* + * Convert from filesystem to in-memory representation. + */ +static struct posix_acl * +ext2_acl_from_disk(const void *value, size_t size) +{ + const char *end = (char *)value + size; + int n, count; + struct posix_acl *acl; + + if (!value) + return NULL; + if (size < sizeof(ext2_acl_header)) + return ERR_PTR(-EINVAL); + if (((ext2_acl_header *)value)->a_version != + cpu_to_le32(EXT2_ACL_VERSION)) + return ERR_PTR(-EINVAL); + value = (char *)value + sizeof(ext2_acl_header); + count = ext2_acl_count(size); + if (count < 0) + return ERR_PTR(-EINVAL); + if (count == 0) + return NULL; + acl = posix_acl_alloc(count, GFP_KERNEL); + if (!acl) + return ERR_PTR(-ENOMEM); + for (n=0; n < count; n++) { + ext2_acl_entry *entry = + (ext2_acl_entry *)value; + if ((char *)value + sizeof(ext2_acl_entry_short) > end) + goto fail; + acl->a_entries[n].e_tag = le16_to_cpu(entry->e_tag); + acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm); + switch(acl->a_entries[n].e_tag) { + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + value = (char *)value + + sizeof(ext2_acl_entry_short); + acl->a_entries[n].e_id = ACL_UNDEFINED_ID; + break; + + case ACL_USER: + case ACL_GROUP: + value = (char *)value + sizeof(ext2_acl_entry); + if ((char *)value > end) + goto fail; + acl->a_entries[n].e_id = + le32_to_cpu(entry->e_id); + break; + + default: + goto fail; + } + } + if (value != end) + goto fail; + return acl; + +fail: + posix_acl_release(acl); + return ERR_PTR(-EINVAL); +} + +/* + * Convert from in-memory to filesystem representation. + */ +static void * +ext2_acl_to_disk(const struct posix_acl *acl, size_t *size) +{ + ext2_acl_header *ext_acl; + char *e; + int n; + + *size = ext2_acl_size(acl->a_count); + ext_acl = (ext2_acl_header *)kmalloc(sizeof(ext2_acl_header) + + acl->a_count * sizeof(ext2_acl_entry), GFP_KERNEL); + if (!ext_acl) + return ERR_PTR(-ENOMEM); + ext_acl->a_version = cpu_to_le32(EXT2_ACL_VERSION); + e = (char *)ext_acl + sizeof(ext2_acl_header); + for (n=0; n < acl->a_count; n++) { + ext2_acl_entry *entry = (ext2_acl_entry *)e; + entry->e_tag = cpu_to_le16(acl->a_entries[n].e_tag); + entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm); + switch(acl->a_entries[n].e_tag) { + case ACL_USER: + case ACL_GROUP: + entry->e_id = + cpu_to_le32(acl->a_entries[n].e_id); + e += sizeof(ext2_acl_entry); + break; + + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + e += sizeof(ext2_acl_entry_short); + break; + + default: + goto fail; + } + } + return (char *)ext_acl; + +fail: + kfree(ext_acl); + return ERR_PTR(-EINVAL); +} + +/* + * inode->i_sem: down + */ +static struct posix_acl * +ext2_get_acl(struct inode *inode, int type) +{ + int name_index; + char *value; + struct posix_acl *acl, **p_acl; + const size_t size = ext2_acl_size(EXT2_ACL_MAX_ENTRIES); + int retval; + + if (!test_opt(inode->i_sb, POSIX_ACL)) + return 0; + + switch(type) { + case ACL_TYPE_ACCESS: + p_acl = &EXT2_I(inode)->i_acl; + name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS; + break; + + case ACL_TYPE_DEFAULT: + p_acl = &EXT2_I(inode)->i_default_acl; + name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT; + break; + + default: + return ERR_PTR(-EINVAL); + } + if (*p_acl != EXT2_ACL_NOT_CACHED) + return posix_acl_dup(*p_acl); + value = kmalloc(size, GFP_KERNEL); + if (!value) + return ERR_PTR(-ENOMEM); + + retval = ext2_xattr_get(inode, name_index, "", value, size); + + if (retval == -ENODATA || retval == -ENOSYS) + *p_acl = acl = NULL; + else if (retval < 0) + acl = ERR_PTR(retval); + else { + acl = ext2_acl_from_disk(value, retval); + if (!IS_ERR(acl)) + *p_acl = posix_acl_dup(acl); + } + kfree(value); + return acl; +} + +/* + * inode->i_sem: down + */ +static int +ext2_set_acl(struct inode *inode, int type, struct posix_acl *acl) +{ + int name_index; + void *value = NULL; + struct posix_acl **p_acl; + size_t size; + int error; + + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + if (!test_opt(inode->i_sb, POSIX_ACL)) + return 0; + + switch(type) { + case ACL_TYPE_ACCESS: + name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS; + p_acl = &EXT2_I(inode)->i_acl; + if (acl) { + mode_t mode = inode->i_mode; + error = posix_acl_equiv_mode(acl, &mode); + if (error < 0) + return error; + else { + inode->i_mode = mode; + mark_inode_dirty(inode); + if (error == 0) + acl = NULL; + } + } + break; + + case ACL_TYPE_DEFAULT: + name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT; + p_acl = &EXT2_I(inode)->i_default_acl; + if (!S_ISDIR(inode->i_mode)) + return acl ? -EACCES : 0; + break; + + default: + return -EINVAL; + } + if (acl) { + if (acl->a_count > EXT2_ACL_MAX_ENTRIES) + return -EINVAL; + value = ext2_acl_to_disk(acl, &size); + if (IS_ERR(value)) + return (int)PTR_ERR(value); + } + + error = ext2_xattr_set(inode, name_index, "", value, size, 0); + + if (value) + kfree(value); + if (!error) { + if (*p_acl && *p_acl != EXT2_ACL_NOT_CACHED) + posix_acl_release(*p_acl); + *p_acl = posix_acl_dup(acl); + } + return error; +} + +static int +__ext2_permission(struct inode *inode, int mask, int lock) +{ + int mode = inode->i_mode; + + /* Nobody gets write access to a read-only fs */ + if ((mask & MAY_WRITE) && IS_RDONLY(inode) && + (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) + return -EROFS; + /* Nobody gets write access to an immutable file */ + if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode)) + return -EACCES; + if (current->fsuid == inode->i_uid) { + mode >>= 6; + } else if (test_opt(inode->i_sb, POSIX_ACL)) { + /* ACL can't contain additional permissions if + the ACL_MASK entry is 0 */ + if (!(mode & S_IRWXG)) + goto check_groups; + if (EXT2_I(inode)->i_acl == EXT2_ACL_NOT_CACHED) { + struct posix_acl *acl; + + if (lock) { + down(&inode->i_sem); + acl = ext2_get_acl(inode, ACL_TYPE_ACCESS); + up(&inode->i_sem); + } else + acl = ext2_get_acl(inode, ACL_TYPE_ACCESS); + + if (IS_ERR(acl)) + return PTR_ERR(acl); + posix_acl_release(acl); + if (EXT2_I(inode)->i_acl == EXT2_ACL_NOT_CACHED) + return -EIO; + } + if (EXT2_I(inode)->i_acl) { + int error = posix_acl_permission(inode, + EXT2_I(inode)->i_acl, mask); + if (error == -EACCES) + goto check_capabilities; + return error; + } else + goto check_groups; + } else { +check_groups: + if (in_group_p(inode->i_gid)) + mode >>= 3; + } + if ((mode & mask & S_IRWXO) == mask) + return 0; + +check_capabilities: + /* Allowed to override Discretionary Access Control? */ + if ((mask & (MAY_READ|MAY_WRITE)) || (inode->i_mode & S_IXUGO)) + if (capable(CAP_DAC_OVERRIDE)) + return 0; + /* Read and search granted if capable(CAP_DAC_READ_SEARCH) */ + if (capable(CAP_DAC_READ_SEARCH) && ((mask == MAY_READ) || + (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE)))) + return 0; + return -EACCES; +} + +/* + * Inode operation permission(). + * + * inode->i_sem: up + * BKL held [before 2.5.x] + */ +int +ext2_permission(struct inode *inode, int mask) +{ + return __ext2_permission(inode, mask, 1); +} + +/* + * Used internally if i_sem is already down. + */ +int +ext2_permission_locked(struct inode *inode, int mask) +{ + return __ext2_permission(inode, mask, 0); +} + +/* + * Initialize the ACLs of a new inode. Called from ext2_new_inode. + * + * dir->i_sem: down + * inode->i_sem: up (access to inode is still exclusive) + * BKL held [before 2.5.x] + */ +int +ext2_init_acl(struct inode *inode, struct inode *dir) +{ + struct posix_acl *acl = NULL; + int error = 0; + + if (!S_ISLNK(inode->i_mode)) { + if (test_opt(dir->i_sb, POSIX_ACL)) { + acl = ext2_get_acl(dir, ACL_TYPE_DEFAULT); + if (IS_ERR(acl)) + return PTR_ERR(acl); + } + if (!acl) + inode->i_mode &= ~current->fs->umask; + } + if (test_opt(inode->i_sb, POSIX_ACL) && acl) { + struct posix_acl *clone; + mode_t mode; + + if (S_ISDIR(inode->i_mode)) { + error = ext2_set_acl(inode, ACL_TYPE_DEFAULT, acl); + if (error) + goto cleanup; + } + clone = posix_acl_clone(acl, GFP_KERNEL); + error = -ENOMEM; + if (!clone) + goto cleanup; + mode = inode->i_mode; + error = posix_acl_create_masq(clone, &mode); + if (error >= 0) { + inode->i_mode = mode; + if (error > 0) { + /* This is an extended ACL */ + error = ext2_set_acl(inode, + ACL_TYPE_ACCESS, clone); + } + } + posix_acl_release(clone); + } +cleanup: + posix_acl_release(acl); + return error; +} + +/* + * Does chmod for an inode that may have an Access Control List. The + * inode->i_mode field must be updated to the desired value by the caller + * before calling this function. + * Returns 0 on success, or a negative error number. + * + * We change the ACL rather than storing some ACL entries in the file + * mode permission bits (which would be more efficient), because that + * would break once additional permissions (like ACL_APPEND, ACL_DELETE + * for directories) are added. There are no more bits available in the + * file mode. + * + * inode->i_sem: down + * BKL held [before 2.5.x] + */ +int +ext2_acl_chmod(struct inode *inode) +{ + struct posix_acl *acl, *clone; + int error; + + if (!test_opt(inode->i_sb, POSIX_ACL)) + return 0; + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + acl = ext2_get_acl(inode, ACL_TYPE_ACCESS); + if (IS_ERR(acl) || !acl) + return PTR_ERR(acl); + clone = posix_acl_clone(acl, GFP_KERNEL); + posix_acl_release(acl); + if (!clone) + return -ENOMEM; + error = posix_acl_chmod_masq(clone, inode->i_mode); + if (!error) + error = ext2_set_acl(inode, ACL_TYPE_ACCESS, clone); + posix_acl_release(clone); + return error; +} + +/* + * Extended attribut handlers + */ +static size_t +ext2_xattr_list_acl_access(char *list, struct inode *inode, + const char *name, int name_len) +{ + const size_t len = sizeof(XATTR_NAME_ACL_ACCESS)-1; + + if (!test_opt(inode->i_sb, POSIX_ACL)) + return 0; + if (list) + memcpy(list, XATTR_NAME_ACL_ACCESS, len); + return len; +} + +static size_t +ext2_xattr_list_acl_default(char *list, struct inode *inode, + const char *name, int name_len) +{ + const size_t len = sizeof(XATTR_NAME_ACL_DEFAULT)-1; + + if (!test_opt(inode->i_sb, POSIX_ACL)) + return 0; + if (list) + memcpy(list, XATTR_NAME_ACL_DEFAULT, len); + return len; +} + +static int +ext2_xattr_get_acl(struct inode *inode, int type, void *buffer, size_t size) +{ + struct posix_acl *acl; + int error; + + if (!test_opt(inode->i_sb, POSIX_ACL)) + return -EOPNOTSUPP; + + acl = ext2_get_acl(inode, type); + if (IS_ERR(acl)) + return PTR_ERR(acl); + if (acl == NULL) + return -ENODATA; + error = posix_acl_to_xattr(acl, buffer, size); + posix_acl_release(acl); + + return error; +} + +static int +ext2_xattr_get_acl_access(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return ext2_xattr_get_acl(inode, ACL_TYPE_ACCESS, buffer, size); +} + +static int +ext2_xattr_get_acl_default(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return ext2_xattr_get_acl(inode, ACL_TYPE_DEFAULT, buffer, size); +} + +static int +ext2_xattr_set_acl(struct inode *inode, int type, const void *value, size_t size) +{ + struct posix_acl *acl; + int error; + + if (!test_opt(inode->i_sb, POSIX_ACL)) + return -EOPNOTSUPP; + if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + return -EPERM; + + if (value) { + acl = posix_acl_from_xattr(value, size); + if (IS_ERR(acl)) + return PTR_ERR(acl); + else if (acl) { + error = posix_acl_valid(acl); + if (error) + goto release_and_out; + } + } else + acl = NULL; + + error = ext2_set_acl(inode, type, acl); + +release_and_out: + posix_acl_release(acl); + return error; +} + +static int +ext2_xattr_set_acl_access(struct inode *inode, const char *name, + const void *value, size_t size, int flags) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return ext2_xattr_set_acl(inode, ACL_TYPE_ACCESS, value, size); +} + +static int +ext2_xattr_set_acl_default(struct inode *inode, const char *name, + const void *value, size_t size, int flags) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return ext2_xattr_set_acl(inode, ACL_TYPE_DEFAULT, value, size); +} + +struct ext2_xattr_handler ext2_xattr_acl_access_handler = { + prefix: XATTR_NAME_ACL_ACCESS, + list: ext2_xattr_list_acl_access, + get: ext2_xattr_get_acl_access, + set: ext2_xattr_set_acl_access, +}; + +struct ext2_xattr_handler ext2_xattr_acl_default_handler = { + prefix: XATTR_NAME_ACL_DEFAULT, + list: ext2_xattr_list_acl_default, + get: ext2_xattr_get_acl_default, + set: ext2_xattr_set_acl_default, +}; + +void +exit_ext2_acl(void) +{ + ext2_xattr_unregister(EXT2_XATTR_INDEX_POSIX_ACL_ACCESS, + &ext2_xattr_acl_access_handler); + ext2_xattr_unregister(EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT, + &ext2_xattr_acl_default_handler); +} + +int __init +init_ext2_acl(void) +{ + int error; + + error = ext2_xattr_register(EXT2_XATTR_INDEX_POSIX_ACL_ACCESS, + &ext2_xattr_acl_access_handler); + if (error) + goto fail; + error = ext2_xattr_register(EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT, + &ext2_xattr_acl_default_handler); + if (error) + goto fail; + return 0; + +fail: + exit_ext2_acl(); + return error; +} diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/ext2/acl.h linux-2.5-bk/fs/ext2/acl.h --- linux-2.5.45/fs/ext2/acl.h Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/fs/ext2/acl.h Thu Oct 31 22:12:58 2002 @@ -0,0 +1,88 @@ +/* + File: fs/ext2/acl.h + + (C) 2001 Andreas Gruenbacher, +*/ + +#include + +#define EXT2_ACL_VERSION 0x0001 +#define EXT2_ACL_MAX_ENTRIES 32 + +typedef struct { + __u16 e_tag; + __u16 e_perm; + __u32 e_id; +} ext2_acl_entry; + +typedef struct { + __u16 e_tag; + __u16 e_perm; +} ext2_acl_entry_short; + +typedef struct { + __u32 a_version; +} ext2_acl_header; + +static inline size_t ext2_acl_size(int count) +{ + if (count <= 4) { + return sizeof(ext2_acl_header) + + count * sizeof(ext2_acl_entry_short); + } else { + return sizeof(ext2_acl_header) + + 4 * sizeof(ext2_acl_entry_short) + + (count - 4) * sizeof(ext2_acl_entry); + } +} + +static inline int ext2_acl_count(size_t size) +{ + ssize_t s; + size -= sizeof(ext2_acl_header); + s = size - 4 * sizeof(ext2_acl_entry_short); + if (s < 0) { + if (size % sizeof(ext2_acl_entry_short)) + return -1; + return size / sizeof(ext2_acl_entry_short); + } else { + if (s % sizeof(ext2_acl_entry)) + return -1; + return s / sizeof(ext2_acl_entry) + 4; + } +} + +#ifdef CONFIG_EXT2_FS_POSIX_ACL + +/* Value for inode->u.ext2_i.i_acl and inode->u.ext2_i.i_default_acl + if the ACL has not been cached */ +#define EXT2_ACL_NOT_CACHED ((void *)-1) + +/* acl.c */ +extern int ext2_permission (struct inode *, int); +extern int ext2_permission_locked (struct inode *, int); +extern int ext2_acl_chmod (struct inode *); +extern int ext2_init_acl (struct inode *, struct inode *); + +extern int init_ext2_acl(void); +extern void exit_ext2_acl(void); + +#else +#include +#define ext2_permission NULL +#define ext2_get_acl NULL +#define ext2_set_acl NULL + +static inline int +ext2_acl_chmod (struct inode *inode) +{ + return 0; +} + +static inline int ext2_init_acl (struct inode *inode, struct inode *dir) +{ + inode->i_mode &= ~current->fs->umask; + return 0; +} +#endif + diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/ext2/ext2.h linux-2.5-bk/fs/ext2/ext2.h --- linux-2.5.45/fs/ext2/ext2.h Wed Oct 30 16:43:45 2002 +++ linux-2.5-bk/fs/ext2/ext2.h Thu Oct 31 22:12:58 2002 @@ -10,6 +10,7 @@ __u32 i_faddr; __u8 i_frag_no; __u8 i_frag_size; + __u16 i_state; __u32 i_file_acl; __u32 i_dir_acl; __u32 i_dtime; @@ -19,11 +20,21 @@ __u32 i_prealloc_block; __u32 i_prealloc_count; __u32 i_dir_start_lookup; +#ifdef CONFIG_EXT2_FS_POSIX_ACL + struct posix_acl *i_acl; + struct posix_acl *i_default_acl; +#endif rwlock_t i_meta_lock; struct inode vfs_inode; }; /* + * Inode dynamic state flags + */ +#define EXT2_STATE_NEW 0x00000001 /* inode is newly created */ + + +/* * Function prototypes */ @@ -45,6 +56,7 @@ extern void ext2_free_blocks (struct inode *, unsigned long, unsigned long); extern unsigned long ext2_count_free_blocks (struct super_block *); +extern unsigned long ext2_count_dirs (struct super_block *); extern void ext2_check_blocks_bitmap (struct super_block *); extern struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb, unsigned int block_group, @@ -78,6 +90,7 @@ extern int ext2_sync_inode (struct inode *); extern void ext2_discard_prealloc (struct inode *); extern void ext2_truncate (struct inode *); +extern int ext2_setattr (struct dentry *, struct iattr *); /* ioctl.c */ extern int ext2_ioctl (struct inode *, struct file *, unsigned int, @@ -110,6 +123,8 @@ /* namei.c */ extern struct inode_operations ext2_dir_inode_operations; +extern struct inode_operations ext2_special_inode_operations; /* symlink.c */ extern struct inode_operations ext2_fast_symlink_inode_operations; +extern struct inode_operations ext2_symlink_inode_operations; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/ext2/file.c linux-2.5-bk/fs/ext2/file.c --- linux-2.5.45/fs/ext2/file.c Wed Oct 30 16:42:26 2002 +++ linux-2.5-bk/fs/ext2/file.c Thu Oct 31 22:12:58 2002 @@ -18,8 +18,10 @@ * (jj@sunsite.ms.mff.cuni.cz) */ -#include "ext2.h" #include +#include "ext2.h" +#include "xattr.h" +#include "acl.h" /* * Called when an inode is released. Note that this is different @@ -55,4 +57,10 @@ struct inode_operations ext2_file_inode_operations = { .truncate = ext2_truncate, + .setxattr = ext2_setxattr, + .getxattr = ext2_getxattr, + .listxattr = ext2_listxattr, + .removexattr = ext2_removexattr, + .setattr = ext2_setattr, + .permission = ext2_permission, }; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/ext2/ialloc.c linux-2.5-bk/fs/ext2/ialloc.c --- linux-2.5.45/fs/ext2/ialloc.c Wed Oct 30 16:42:56 2002 +++ linux-2.5-bk/fs/ext2/ialloc.c Thu Oct 31 22:12:58 2002 @@ -13,11 +13,14 @@ */ #include -#include "ext2.h" #include #include #include #include +#include +#include "ext2.h" +#include "xattr.h" +#include "acl.h" /* * ialloc.c contains the inodes allocation and deallocation routines @@ -97,6 +100,7 @@ */ if (!is_bad_inode(inode)) { /* Quota is already initialized in iput() */ + ext2_xattr_delete_inode(inode); DQUOT_FREE_INODE(inode); DQUOT_DROP(inode); } @@ -205,6 +209,7 @@ * For other inodes, search forward from the parent directory\'s block * group to find a free inode. */ +#if 0 static int find_group_dir(struct super_block *sb, int parent_group) { @@ -238,9 +243,141 @@ mark_buffer_dirty(best_bh); return best_group; } +#endif + +/* + * Orlov's allocator for directories. + * + * We always try to spread first-level directories. + * + * If there are blockgroups with both free inodes and free blocks counts + * not worse than average we return one with smallest directory count. + * Otherwise we simply return a random group. + * + * For the rest rules look so: + * + * It's OK to put directory into a group unless + * it has too many directories already (max_dirs) or + * it has too few free inodes left (min_inodes) or + * it has too few free blocks left (min_blocks) or + * it's already running too large debt (max_debt). + * Parent's group is prefered, if it doesn't satisfy these + * conditions we search cyclically through the rest. If none + * of the groups look good we just look for a group with more + * free inodes than average (starting at parent's group). + * + * Debt is incremented each time we allocate a directory and decremented + * when we allocate an inode, within 0--255. + */ -static int find_group_other(struct super_block *sb, int parent_group) +#define INODE_COST 64 +#define BLOCK_COST 256 + +static int find_group_orlov(struct super_block *sb, struct inode *parent) { + int parent_group = EXT2_I(parent)->i_block_group; + struct ext2_sb_info *sbi = EXT2_SB(sb); + struct ext2_super_block *es = sbi->s_es; + int ngroups = sbi->s_groups_count; + int inodes_per_group = EXT2_INODES_PER_GROUP(sb); + int avefreei = le32_to_cpu(es->s_free_inodes_count) / ngroups; + int avefreeb = le32_to_cpu(es->s_free_blocks_count) / ngroups; + int blocks_per_dir; + int ndirs = sbi->s_dir_count; + int max_debt, max_dirs, min_blocks, min_inodes; + int group = -1, i; + struct ext2_group_desc *desc; + struct buffer_head *bh; + + if (parent == sb->s_root->d_inode) { + struct ext2_group_desc *best_desc = NULL; + struct buffer_head *best_bh = NULL; + int best_ndir = inodes_per_group; + int best_group = -1; + + get_random_bytes(&group, sizeof(group)); + parent_group = (unsigned)group % ngroups; + for (i = 0; i < ngroups; i++) { + group = (parent_group + i) % ngroups; + desc = ext2_get_group_desc (sb, group, &bh); + if (!desc || !desc->bg_free_inodes_count) + continue; + if (le16_to_cpu(desc->bg_used_dirs_count) >= best_ndir) + continue; + if (le16_to_cpu(desc->bg_free_inodes_count) < avefreei) + continue; + if (le16_to_cpu(desc->bg_free_blocks_count) < avefreeb) + continue; + best_group = group; + best_ndir = le16_to_cpu(desc->bg_used_dirs_count); + best_desc = desc; + best_bh = bh; + } + if (best_group >= 0) { + desc = best_desc; + bh = best_bh; + group = best_group; + goto found; + } + goto fallback; + } + + blocks_per_dir = (le32_to_cpu(es->s_blocks_count) - + le32_to_cpu(es->s_free_blocks_count)) / ndirs; + + max_dirs = ndirs / ngroups + inodes_per_group / 16; + min_inodes = avefreei - inodes_per_group / 4; + min_blocks = avefreeb - EXT2_BLOCKS_PER_GROUP(sb) / 4; + + max_debt = EXT2_BLOCKS_PER_GROUP(sb) / max(blocks_per_dir, BLOCK_COST); + if (max_debt * INODE_COST > inodes_per_group) + max_debt = inodes_per_group / INODE_COST; + if (max_debt > 255) + max_debt = 255; + if (max_debt == 0) + max_debt = 1; + + for (i = 0; i < ngroups; i++) { + group = (parent_group + i) % ngroups; + desc = ext2_get_group_desc (sb, group, &bh); + if (!desc || !desc->bg_free_inodes_count) + continue; + if (sbi->debts[group] >= max_debt) + continue; + if (le16_to_cpu(desc->bg_used_dirs_count) >= max_dirs) + continue; + if (le16_to_cpu(desc->bg_free_inodes_count) < min_inodes) + continue; + if (le16_to_cpu(desc->bg_free_blocks_count) < min_blocks) + continue; + goto found; + } + +fallback: + for (i = 0; i < ngroups; i++) { + group = (parent_group + i) % ngroups; + desc = ext2_get_group_desc (sb, group, &bh); + if (!desc || !desc->bg_free_inodes_count) + continue; + if (le16_to_cpu(desc->bg_free_inodes_count) >= avefreei) + goto found; + } + + return -1; + +found: + desc->bg_free_inodes_count = + cpu_to_le16(le16_to_cpu(desc->bg_free_inodes_count) - 1); + desc->bg_used_dirs_count = + cpu_to_le16(le16_to_cpu(desc->bg_used_dirs_count) + 1); + sbi->s_dir_count++; + mark_buffer_dirty(bh); + return group; +} + +static int find_group_other(struct super_block *sb, struct inode *parent) +{ + int parent_group = EXT2_I(parent)->i_block_group; int ngroups = EXT2_SB(sb)->s_groups_count; struct ext2_group_desc *desc; struct buffer_head *bh; @@ -300,7 +437,6 @@ struct ext2_super_block * es; struct ext2_inode_info *ei; int err; - struct inode *ret; sb = dir->i_sb; inode = new_inode(sb); @@ -312,16 +448,15 @@ es = EXT2_SB(sb)->s_es; repeat: if (S_ISDIR(mode)) - group = find_group_dir(sb, EXT2_I(dir)->i_block_group); + group = find_group_orlov(sb, dir); else - group = find_group_other(sb, EXT2_I(dir)->i_block_group); + group = find_group_other(sb, dir); err = -ENOSPC; if (group == -1) goto fail; err = -EIO; - brelse(bitmap_bh); bitmap_bh = read_inode_bitmap(sb, group); if (!bitmap_bh) goto fail2; @@ -337,6 +472,7 @@ ll_rw_block(WRITE, 1, &bitmap_bh); wait_on_buffer(bitmap_bh); } + brelse(bitmap_bh); ino = group * EXT2_INODES_PER_GROUP(sb) + i + 1; if (ino < EXT2_FIRST_INO(sb) || ino > le32_to_cpu(es->s_inodes_count)) { @@ -350,6 +486,15 @@ es->s_free_inodes_count = cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) - 1); + + if (S_ISDIR(mode)) { + if (EXT2_SB(sb)->debts[group] < 255) + EXT2_SB(sb)->debts[group]++; + } else { + if (EXT2_SB(sb)->debts[group]) + EXT2_SB(sb)->debts[group]--; + } + mark_buffer_dirty(EXT2_SB(sb)->s_sbh); sb->s_dirt = 1; inode->i_uid = current->fsuid; @@ -386,27 +531,34 @@ ei->i_prealloc_block = 0; ei->i_prealloc_count = 0; ei->i_dir_start_lookup = 0; + ei->i_state = EXT2_STATE_NEW; if (ei->i_flags & EXT2_SYNC_FL) inode->i_flags |= S_SYNC; if (ei->i_flags & EXT2_DIRSYNC_FL) inode->i_flags |= S_DIRSYNC; inode->i_generation = EXT2_SB(sb)->s_next_generation++; insert_inode_hash(inode); - mark_inode_dirty(inode); unlock_super(sb); - ret = inode; if(DQUOT_ALLOC_INODE(inode)) { DQUOT_DROP(inode); - inode->i_flags |= S_NOQUOTA; - inode->i_nlink = 0; - iput(inode); - ret = ERR_PTR(-EDQUOT); - } else { - ext2_debug("allocating inode %lu\n", inode->i_ino); - ext2_preread_inode(inode); + goto fail3; + } + err = ext2_init_acl(inode, dir); + if (err) { + DQUOT_FREE_INODE(inode); + goto fail3; } - goto out; + mark_inode_dirty(inode); + ext2_debug("allocating inode %lu\n", inode->i_ino); + ext2_preread_inode(inode); + return inode; + +fail3: + inode->i_flags |= S_NOQUOTA; + inode->i_nlink = 0; + iput(inode); + return ERR_PTR(err); fail2: desc = ext2_get_group_desc (sb, group, &bh2); @@ -420,10 +572,10 @@ unlock_super(sb); make_bad_inode(inode); iput(inode); - ret = ERR_PTR(err); - goto out; + return ERR_PTR(err); bad_count: + brelse(bitmap_bh); ext2_error (sb, "ext2_new_inode", "Free inodes count corrupted in group %d", group); @@ -436,9 +588,6 @@ desc->bg_free_inodes_count = 0; mark_buffer_dirty(bh2); goto repeat; -out: - brelse(bitmap_bh); - return ret; } unsigned long ext2_count_free_inodes (struct super_block * sb) @@ -477,6 +626,21 @@ #else return le32_to_cpu(EXT2_SB(sb)->s_es->s_free_inodes_count); #endif +} + +/* Called at mount-time, super-block is locked */ +unsigned long ext2_count_dirs (struct super_block * sb) +{ + unsigned long count = 0; + int i; + + for (i = 0; i < EXT2_SB(sb)->s_groups_count; i++) { + struct ext2_group_desc *gdp = ext2_get_group_desc (sb, i, NULL); + if (!gdp) + continue; + count += le16_to_cpu(gdp->bg_used_dirs_count); + } + return count; } #ifdef CONFIG_EXT2_CHECK diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/ext2/inode.c linux-2.5-bk/fs/ext2/inode.c --- linux-2.5.45/fs/ext2/inode.c Wed Oct 30 16:41:51 2002 +++ linux-2.5-bk/fs/ext2/inode.c Thu Oct 31 22:12:58 2002 @@ -22,7 +22,6 @@ * Assorted race fixes, rewrite of ext2_get_block() by Al Viro, 2000 */ -#include "ext2.h" #include #include #include @@ -31,6 +30,8 @@ #include #include #include +#include "ext2.h" +#include "acl.h" MODULE_AUTHOR("Remy Card and others"); MODULE_DESCRIPTION("Second Extended Filesystem"); @@ -39,6 +40,18 @@ static int ext2_update_inode(struct inode * inode, int do_sync); /* + * Test whether an inode is a fast symlink. + */ +static inline int ext2_inode_is_fast_symlink(struct inode *inode) +{ + int ea_blocks = EXT2_I(inode)->i_file_acl ? + (inode->i_sb->s_blocksize >> 9) : 0; + + return (S_ISLNK(inode->i_mode) && + inode->i_blocks - ea_blocks == 0); +} + +/* * Called at each iput() */ void ext2_put_inode (struct inode * inode) @@ -51,9 +64,7 @@ */ void ext2_delete_inode (struct inode * inode) { - if (is_bad_inode(inode) || - inode->i_ino == EXT2_ACL_IDX_INO || - inode->i_ino == EXT2_ACL_DATA_INO) + if (is_bad_inode(inode)) goto no_delete; EXT2_I(inode)->i_dtime = CURRENT_TIME; mark_inode_dirty(inode); @@ -843,6 +854,8 @@ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) return; + if (ext2_inode_is_fast_symlink(inode)) + return; if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) return; @@ -929,8 +942,7 @@ struct ext2_group_desc * gdp; *p = NULL; - if ((ino != EXT2_ROOT_INO && ino != EXT2_ACL_IDX_INO && - ino != EXT2_ACL_DATA_INO && ino < EXT2_FIRST_INO(sb)) || + if ((ino != EXT2_ROOT_INO && ino < EXT2_FIRST_INO(sb)) || ino > le32_to_cpu(EXT2_SB(sb)->s_es->s_inodes_count)) goto Einval; @@ -971,6 +983,10 @@ struct ext2_inode * raw_inode = ext2_get_inode(inode->i_sb, ino, &bh); int n; +#ifdef CONFIG_EXT2_FS_POSIX_ACL + ei->i_acl = EXT2_ACL_NOT_CACHED; + ei->i_default_acl = EXT2_ACL_NOT_CACHED; +#endif if (IS_ERR(raw_inode)) goto bad_inode; @@ -1012,6 +1028,7 @@ ei->i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl); ei->i_dtime = 0; inode->i_generation = le32_to_cpu(raw_inode->i_generation); + ei->i_state = 0; ei->i_next_alloc_block = 0; ei->i_next_alloc_goal = 0; ei->i_prealloc_count = 0; @@ -1025,9 +1042,7 @@ for (n = 0; n < EXT2_N_BLOCKS; n++) ei->i_data[n] = raw_inode->i_block[n]; - if (ino == EXT2_ACL_IDX_INO || ino == EXT2_ACL_DATA_INO) - /* Nothing to do */ ; - else if (S_ISREG(inode->i_mode)) { + if (S_ISREG(inode->i_mode)) { inode->i_op = &ext2_file_inode_operations; inode->i_fop = &ext2_file_operations; inode->i_mapping->a_ops = &ext2_aops; @@ -1036,15 +1051,17 @@ inode->i_fop = &ext2_dir_operations; inode->i_mapping->a_ops = &ext2_aops; } else if (S_ISLNK(inode->i_mode)) { - if (!inode->i_blocks) + if (ext2_inode_is_fast_symlink(inode)) inode->i_op = &ext2_fast_symlink_inode_operations; else { - inode->i_op = &page_symlink_inode_operations; + inode->i_op = &ext2_symlink_inode_operations; inode->i_mapping->a_ops = &ext2_aops; } - } else + } else { + inode->i_op = &ext2_special_inode_operations; init_special_inode(inode, inode->i_mode, le32_to_cpu(raw_inode->i_block[0])); + } brelse (bh); if (ei->i_flags & EXT2_SYNC_FL) inode->i_flags |= S_SYNC; @@ -1076,12 +1093,11 @@ if (IS_ERR(raw_inode)) return -EIO; - if (ino == EXT2_ACL_IDX_INO || ino == EXT2_ACL_DATA_INO) { - ext2_error (sb, "ext2_write_inode", "bad inode number: %lu", - (unsigned long) ino); - brelse(bh); - return -EIO; - } + /* For fields not not tracking in the in-memory inode, + * initialise them to zero for new inodes. */ + if (ei->i_state & EXT2_STATE_NEW) + memset(raw_inode, 0, EXT2_SB(sb)->s_inode_size); + raw_inode->i_mode = cpu_to_le16(inode->i_mode); if (!(test_opt(sb, NO_UID32))) { raw_inode->i_uid_low = cpu_to_le16(low_16_bits(uid)); @@ -1152,6 +1168,7 @@ err = -EIO; } } + ei->i_state &= ~EXT2_STATE_NEW; brelse (bh); return err; } @@ -1165,3 +1182,18 @@ { return ext2_update_inode (inode, 1); } + +int ext2_setattr(struct dentry *dentry, struct iattr *iattr) +{ + struct inode *inode = dentry->d_inode; + int error; + + error = inode_change_ok(inode, iattr); + if (error) + return error; + inode_setattr(inode, iattr); + if (iattr->ia_valid & ATTR_MODE) + error = ext2_acl_chmod(inode); + return error; +} + diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/ext2/namei.c linux-2.5-bk/fs/ext2/namei.c --- linux-2.5.45/fs/ext2/namei.c Wed Oct 30 16:43:39 2002 +++ linux-2.5-bk/fs/ext2/namei.c Thu Oct 31 22:12:58 2002 @@ -29,8 +29,10 @@ * David S. Miller (davem@caip.rutgers.edu), 1995 */ -#include "ext2.h" #include +#include "ext2.h" +#include "xattr.h" +#include "acl.h" /* * Couple of helper functions - make the code slightly cleaner. @@ -137,7 +139,10 @@ struct inode * inode = ext2_new_inode (dir, mode); int err = PTR_ERR(inode); if (!IS_ERR(inode)) { - init_special_inode(inode, mode, rdev); + init_special_inode(inode, inode->i_mode, rdev); +#ifdef CONFIG_EXT2_FS_EXT_ATTR + inode->i_op = &ext2_special_inode_operations; +#endif mark_inode_dirty(inode); err = ext2_add_nondir(dentry, inode); } @@ -162,7 +167,7 @@ if (l > sizeof (EXT2_I(inode)->i_data)) { /* slow symlink */ - inode->i_op = &page_symlink_inode_operations; + inode->i_op = &ext2_symlink_inode_operations; inode->i_mapping->a_ops = &ext2_aops; err = page_symlink(inode, symname, l); if (err) @@ -368,4 +373,19 @@ .rmdir = ext2_rmdir, .mknod = ext2_mknod, .rename = ext2_rename, + .setxattr = ext2_setxattr, + .getxattr = ext2_getxattr, + .listxattr = ext2_listxattr, + .removexattr = ext2_removexattr, + .setattr = ext2_setattr, + .permission = ext2_permission, +}; + +struct inode_operations ext2_special_inode_operations = { + .setxattr = ext2_setxattr, + .getxattr = ext2_getxattr, + .listxattr = ext2_listxattr, + .removexattr = ext2_removexattr, + .setattr = ext2_setattr, + .permission = ext2_permission, }; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/ext2/super.c linux-2.5-bk/fs/ext2/super.c --- linux-2.5.45/fs/ext2/super.c Wed Oct 30 16:43:06 2002 +++ linux-2.5-bk/fs/ext2/super.c Thu Oct 31 22:12:58 2002 @@ -19,7 +19,6 @@ #include #include #include -#include "ext2.h" #include #include #include @@ -27,7 +26,9 @@ #include #include #include - +#include "ext2.h" +#include "xattr.h" +#include "acl.h" static void ext2_sync_super(struct super_block *sb, struct ext2_super_block *es); @@ -52,16 +53,12 @@ va_start (args, fmt); vsprintf (error_buf, fmt, args); va_end (args); - if (test_opt (sb, ERRORS_PANIC) || - (le16_to_cpu(sbi->s_es->s_errors) == EXT2_ERRORS_PANIC && - !test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_RO))) + if (test_opt (sb, ERRORS_PANIC)) panic ("EXT2-fs panic (device %s): %s: %s\n", sb->s_id, function, error_buf); printk (KERN_CRIT "EXT2-fs error (device %s): %s: %s\n", sb->s_id, function, error_buf); - if (test_opt (sb, ERRORS_RO) || - (le16_to_cpu(sbi->s_es->s_errors) == EXT2_ERRORS_RO && - !test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_PANIC))) { + if (test_opt (sb, ERRORS_RO)) { printk ("Remounting filesystem read-only\n"); sb->s_flags |= MS_RDONLY; } @@ -131,6 +128,7 @@ int i; struct ext2_sb_info *sbi = EXT2_SB(sb); + ext2_xattr_put_super(sb); if (!(sb->s_flags & MS_RDONLY)) { struct ext2_super_block *es = sbi->s_es; @@ -157,6 +155,10 @@ ei = (struct ext2_inode_info *)kmem_cache_alloc(ext2_inode_cachep, SLAB_KERNEL); if (!ei) return NULL; +#ifdef CONFIG_EXT2_FS_POSIX_ACL + ei->i_acl = EXT2_ACL_NOT_CACHED; + ei->i_default_acl = EXT2_ACL_NOT_CACHED; +#endif return &ei->vfs_inode; } @@ -193,6 +195,26 @@ printk(KERN_INFO "ext2_inode_cache: not all structures were freed\n"); } +#ifdef CONFIG_EXT2_FS_POSIX_ACL + +static void ext2_clear_inode(struct inode *inode) +{ + struct ext2_inode_info *ei = EXT2_I(inode); + + if (ei->i_acl && ei->i_acl != EXT2_ACL_NOT_CACHED) { + posix_acl_release(ei->i_acl); + ei->i_acl = EXT2_ACL_NOT_CACHED; + } + if (ei->i_default_acl && ei->i_default_acl != EXT2_ACL_NOT_CACHED) { + posix_acl_release(ei->i_default_acl); + ei->i_default_acl = EXT2_ACL_NOT_CACHED; + } +} + +#else +# define ext2_clear_inode NULL +#endif + static struct super_operations ext2_sops = { .alloc_inode = ext2_alloc_inode, .destroy_inode = ext2_destroy_inode, @@ -204,6 +226,7 @@ .write_super = ext2_write_super, .statfs = ext2_statfs, .remount_fs = ext2_remount, + .clear_inode = ext2_clear_inode, }; /* Yes, most of these are left as NULL!! @@ -216,12 +239,61 @@ .get_parent = ext2_get_parent, }; +static unsigned long get_sb_block(void **data) +{ + unsigned long sb_block; + char *options = (char *) *data; + + if (!options || strncmp(options, "sb=", 3) != 0) + return 1; /* Default location */ + options += 3; + sb_block = simple_strtoul(options, &options, 0); + if (*options && *options != ',') { + printk("EXT2-fs: Invalid sb specification: %s\n", + (char *) *data); + return 1; + } + if (*options == ',') + options++; + *data = (void *) options; + return sb_block; +} + +static int want_value(char *value, char *option) +{ + if (!value || !*value) { + printk(KERN_NOTICE "EXT2-fs: the %s option needs an argument\n", + option); + return -1; + } + return 0; +} + +static int want_null_value(char *value, char *option) +{ + if (*value) { + printk(KERN_NOTICE "EXT2-fs: Invalid %s argument: %s\n", + option, value); + return -1; + } + return 0; +} + +static int want_numeric(char *value, char *option, unsigned long *number) +{ + if (want_value(value, option)) + return -1; + *number = simple_strtoul(value, &value, 0); + if (want_null_value(value, option)) + return -1; + return 0; +} + /* * This function has been shamelessly adapted from the msdos fs */ -static int parse_options (char * options, unsigned long * sb_block, - unsigned short *resuid, unsigned short * resgid, - unsigned long * mount_options) +static int parse_options (char * options, + struct ext2_sb_info *sbi) { char * this_char; char * value; @@ -233,23 +305,37 @@ continue; if ((value = strchr (this_char, '=')) != NULL) *value++ = 0; +#ifdef CONFIG_EXT2_FS_XATTR + if (!strcmp (this_char, "user_xattr")) + set_opt (sbi->s_mount_opt, XATTR_USER); + else if (!strcmp (this_char, "nouser_xattr")) + clear_opt (sbi->s_mount_opt, XATTR_USER); + else +#endif +#ifdef CONFIG_EXT2_FS_POSIX_ACL + if (!strcmp(this_char, "acl")) + set_opt(sbi->s_mount_opt, POSIX_ACL); + else if (!strcmp(this_char, "noacl")) + clear_opt(sbi->s_mount_opt, POSIX_ACL); + else +#endif if (!strcmp (this_char, "bsddf")) - clear_opt (*mount_options, MINIX_DF); + clear_opt (sbi->s_mount_opt, MINIX_DF); else if (!strcmp (this_char, "nouid32")) { - set_opt (*mount_options, NO_UID32); + set_opt (sbi->s_mount_opt, NO_UID32); } else if (!strcmp (this_char, "check")) { if (!value || !*value || !strcmp (value, "none")) - clear_opt (*mount_options, CHECK); + clear_opt (sbi->s_mount_opt, CHECK); else #ifdef CONFIG_EXT2_CHECK - set_opt (*mount_options, CHECK); + set_opt (sbi->s_mount_opt, CHECK); #else printk("EXT2 Check option not supported\n"); #endif } else if (!strcmp (this_char, "debug")) - set_opt (*mount_options, DEBUG); + set_opt (sbi->s_mount_opt, DEBUG); else if (!strcmp (this_char, "errors")) { if (!value || !*value) { printk ("EXT2-fs: the errors option requires " @@ -257,19 +343,19 @@ return 0; } if (!strcmp (value, "continue")) { - clear_opt (*mount_options, ERRORS_RO); - clear_opt (*mount_options, ERRORS_PANIC); - set_opt (*mount_options, ERRORS_CONT); + clear_opt (sbi->s_mount_opt, ERRORS_RO); + clear_opt (sbi->s_mount_opt, ERRORS_PANIC); + set_opt (sbi->s_mount_opt, ERRORS_CONT); } else if (!strcmp (value, "remount-ro")) { - clear_opt (*mount_options, ERRORS_CONT); - clear_opt (*mount_options, ERRORS_PANIC); - set_opt (*mount_options, ERRORS_RO); + clear_opt (sbi->s_mount_opt, ERRORS_CONT); + clear_opt (sbi->s_mount_opt, ERRORS_PANIC); + set_opt (sbi->s_mount_opt, ERRORS_RO); } else if (!strcmp (value, "panic")) { - clear_opt (*mount_options, ERRORS_CONT); - clear_opt (*mount_options, ERRORS_RO); - set_opt (*mount_options, ERRORS_PANIC); + clear_opt (sbi->s_mount_opt, ERRORS_CONT); + clear_opt (sbi->s_mount_opt, ERRORS_RO); + set_opt (sbi->s_mount_opt, ERRORS_PANIC); } else { printk ("EXT2-fs: Invalid errors option: %s\n", @@ -279,52 +365,25 @@ } else if (!strcmp (this_char, "grpid") || !strcmp (this_char, "bsdgroups")) - set_opt (*mount_options, GRPID); + set_opt (sbi->s_mount_opt, GRPID); else if (!strcmp (this_char, "minixdf")) - set_opt (*mount_options, MINIX_DF); + set_opt (sbi->s_mount_opt, MINIX_DF); else if (!strcmp (this_char, "nocheck")) - clear_opt (*mount_options, CHECK); + clear_opt (sbi->s_mount_opt, CHECK); else if (!strcmp (this_char, "nogrpid") || !strcmp (this_char, "sysvgroups")) - clear_opt (*mount_options, GRPID); + clear_opt (sbi->s_mount_opt, GRPID); else if (!strcmp (this_char, "resgid")) { - if (!value || !*value) { - printk ("EXT2-fs: the resgid option requires " - "an argument\n"); - return 0; - } - *resgid = simple_strtoul (value, &value, 0); - if (*value) { - printk ("EXT2-fs: Invalid resgid option: %s\n", - value); + unsigned long v; + if (want_numeric(value, "resgid", &v)) return 0; - } + sbi->s_resgid = v; } else if (!strcmp (this_char, "resuid")) { - if (!value || !*value) { - printk ("EXT2-fs: the resuid option requires " - "an argument"); - return 0; - } - *resuid = simple_strtoul (value, &value, 0); - if (*value) { - printk ("EXT2-fs: Invalid resuid option: %s\n", - value); - return 0; - } - } - else if (!strcmp (this_char, "sb")) { - if (!value || !*value) { - printk ("EXT2-fs: the sb option requires " - "an argument"); + unsigned long v; + if (want_numeric(value, "resuid", &v)) return 0; - } - *sb_block = simple_strtoul (value, &value, 0); - if (*value) { - printk ("EXT2-fs: Invalid sb option: %s\n", - value); - return 0; - } + sbi->s_resuid = v; } /* Silently ignore the quota options */ else if (!strcmp (this_char, "grpquota") @@ -458,16 +517,32 @@ return res; } +static unsigned long descriptor_loc(struct super_block *sb, + unsigned long logic_sb_block, + int nr) +{ + struct ext2_sb_info *sbi = EXT2_SB(sb); + unsigned long bg, first_data_block, first_meta_bg; + + first_data_block = le32_to_cpu(sbi->s_es->s_first_data_block); + first_meta_bg = le32_to_cpu(sbi->s_es->s_first_meta_bg); + + if (!EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_META_BG) || + nr < first_meta_bg) + return (logic_sb_block + nr + 1); + bg = sbi->s_desc_per_block * nr; + return (first_data_block + 1 + (bg * sbi->s_blocks_per_group)); +} + static int ext2_fill_super(struct super_block *sb, void *data, int silent) { struct buffer_head * bh; struct ext2_sb_info * sbi; struct ext2_super_block * es; - unsigned long sb_block = 1; - unsigned short resuid = EXT2_DEF_RESUID; - unsigned short resgid = EXT2_DEF_RESGID; - unsigned long logic_sb_block = 1; + unsigned long block, sb_block = 1; + unsigned long logic_sb_block = get_sb_block(&data); unsigned long offset = 0; + unsigned long def_mount_opts; int blocksize = BLOCK_SIZE; int db_count; int i, j; @@ -485,12 +560,6 @@ * This is important for devices that have a hardware * sectorsize that is larger than the default. */ - - sbi->s_mount_opt = 0; - if (!parse_options ((char *) data, &sb_block, &resuid, &resgid, - &sbi->s_mount_opt)) - goto failed_sbi; - blocksize = sb_min_blocksize(sb, BLOCK_SIZE); if (!blocksize) { printk ("EXT2-fs: unable to set blocksize\n"); @@ -498,9 +567,8 @@ } /* - * If the superblock doesn't start on a sector boundary, - * calculate the offset. FIXME(eric) this doesn't make sense - * that we would have to do this. + * If the superblock doesn't start on a hardware sector boundary, + * calculate the offset. */ if (blocksize != BLOCK_SIZE) { logic_sb_block = (sb_block*BLOCK_SIZE) / blocksize; @@ -524,6 +592,35 @@ sb->s_id); goto failed_mount; } + + /* Set defaults before we parse the mount options */ + def_mount_opts = le32_to_cpu(es->s_default_mount_opts); + if (def_mount_opts & EXT2_DEFM_DEBUG) + set_opt(sbi->s_mount_opt, DEBUG); + if (def_mount_opts & EXT2_DEFM_BSDGROUPS) + set_opt(sbi->s_mount_opt, GRPID); + if (def_mount_opts & EXT2_DEFM_UID16) + set_opt(sbi->s_mount_opt, NO_UID32); + if (def_mount_opts & EXT2_DEFM_XATTR_USER) + set_opt(sbi->s_mount_opt, XATTR_USER); + if (def_mount_opts & EXT2_DEFM_ACL) + set_opt(sbi->s_mount_opt, POSIX_ACL); + + if (le16_to_cpu(sbi->s_es->s_errors) == EXT2_ERRORS_PANIC) + set_opt(sbi->s_mount_opt, ERRORS_PANIC); + else if (le16_to_cpu(sbi->s_es->s_errors) == EXT2_ERRORS_RO) + set_opt(sbi->s_mount_opt, ERRORS_RO); + + sbi->s_resuid = le16_to_cpu(es->s_def_resuid); + sbi->s_resgid = le16_to_cpu(es->s_def_resgid); + + if (!parse_options ((char *) data, sbi)) + goto failed_mount; + + sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | + ((EXT2_SB(sb)->s_mount_opt & EXT2_MOUNT_POSIX_ACL) ? + MS_POSIXACL : 0); + if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV && (EXT2_HAS_COMPAT_FEATURE(sb, ~0U) || EXT2_HAS_RO_COMPAT_FEATURE(sb, ~0U) || @@ -582,7 +679,9 @@ } else { sbi->s_inode_size = le16_to_cpu(es->s_inode_size); sbi->s_first_ino = le32_to_cpu(es->s_first_ino); - if (sbi->s_inode_size != EXT2_GOOD_OLD_INODE_SIZE) { + if ((sbi->s_inode_size < EXT2_GOOD_OLD_INODE_SIZE) || + (sbi->s_inode_size & (sbi->s_inode_size - 1)) || + (sbi->s_inode_size > blocksize)) { printk ("EXT2-fs: unsupported inode size: %d\n", sbi->s_inode_size); goto failed_mount; @@ -605,14 +704,6 @@ sbi->s_desc_per_block = sb->s_blocksize / sizeof (struct ext2_group_desc); sbi->s_sbh = bh; - if (resuid != EXT2_DEF_RESUID) - sbi->s_resuid = resuid; - else - sbi->s_resuid = le16_to_cpu(es->s_def_resuid); - if (resgid != EXT2_DEF_RESGID) - sbi->s_resgid = resgid; - else - sbi->s_resgid = le16_to_cpu(es->s_def_resgid); sbi->s_mount_state = le16_to_cpu(es->s_state); sbi->s_addr_per_block_bits = log2 (EXT2_ADDR_PER_BLOCK(sb)); @@ -665,8 +756,16 @@ printk ("EXT2-fs: not enough memory\n"); goto failed_mount; } + sbi->debts = kmalloc(sbi->s_groups_count * sizeof(*sbi->debts), + GFP_KERNEL); + if (!sbi->debts) { + printk ("EXT2-fs: not enough memory\n"); + goto failed_mount_group_desc; + } + memset(sbi->debts, 0, sbi->s_groups_count * sizeof(*sbi->debts)); for (i = 0; i < db_count; i++) { - sbi->s_group_desc[i] = sb_bread(sb, logic_sb_block + i + 1); + block = descriptor_loc(sb, logic_sb_block, i); + sbi->s_group_desc[i] = sb_bread(sb, block); if (!sbi->s_group_desc[i]) { for (j = 0; j < i; j++) brelse (sbi->s_group_desc[j]); @@ -681,6 +780,7 @@ goto failed_mount2; } sbi->s_gdb_count = db_count; + sbi->s_dir_count = ext2_count_dirs(sb); get_random_bytes(&sbi->s_next_generation, sizeof(u32)); /* * set up enough so that it can read an inode @@ -706,6 +806,7 @@ failed_mount2: for (i = 0; i < db_count; i++) brelse(sbi->s_group_desc[i]); +failed_mount_group_desc: kfree(sbi->s_group_desc); failed_mount: brelse(bh); @@ -767,22 +868,16 @@ { struct ext2_sb_info * sbi = EXT2_SB(sb); struct ext2_super_block * es; - unsigned short resuid = sbi->s_resuid; - unsigned short resgid = sbi->s_resgid; - unsigned long new_mount_opt; - unsigned long tmp; /* * Allow the "check" option to be passed as a remount option. */ - new_mount_opt = sbi->s_mount_opt; - if (!parse_options (data, &tmp, &resuid, &resgid, - &new_mount_opt)) + if (!parse_options (data, sbi)) return -EINVAL; - sbi->s_mount_opt = new_mount_opt; - sbi->s_resuid = resuid; - sbi->s_resgid = resgid; + sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | + ((sbi->s_mount_opt & EXT2_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0); + es = sbi->s_es; if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) return 0; @@ -883,7 +978,10 @@ static int __init init_ext2_fs(void) { - int err = init_inodecache(); + int err = init_ext2_xattr(); + if (err) + return err; + err = init_inodecache(); if (err) goto out1; err = register_filesystem(&ext2_fs_type); @@ -893,6 +991,7 @@ out: destroy_inodecache(); out1: + exit_ext2_xattr(); return err; } @@ -900,6 +999,7 @@ { unregister_filesystem(&ext2_fs_type); destroy_inodecache(); + exit_ext2_xattr(); } module_init(init_ext2_fs) diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/ext2/symlink.c linux-2.5-bk/fs/ext2/symlink.c --- linux-2.5.45/fs/ext2/symlink.c Wed Oct 30 16:42:20 2002 +++ linux-2.5-bk/fs/ext2/symlink.c Thu Oct 31 22:12:58 2002 @@ -18,6 +18,7 @@ */ #include "ext2.h" +#include "xattr.h" static int ext2_readlink(struct dentry *dentry, char *buffer, int buflen) { @@ -31,7 +32,20 @@ return vfs_follow_link(nd, (char *)ei->i_data); } +struct inode_operations ext2_symlink_inode_operations = { + .readlink = page_readlink, + .follow_link = page_follow_link, + .setxattr = ext2_setxattr, + .getxattr = ext2_getxattr, + .listxattr = ext2_listxattr, + .removexattr = ext2_removexattr, +}; + struct inode_operations ext2_fast_symlink_inode_operations = { .readlink = ext2_readlink, .follow_link = ext2_follow_link, + .setxattr = ext2_setxattr, + .getxattr = ext2_getxattr, + .listxattr = ext2_listxattr, + .removexattr = ext2_removexattr, }; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/ext2/xattr.c linux-2.5-bk/fs/ext2/xattr.c --- linux-2.5.45/fs/ext2/xattr.c Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/fs/ext2/xattr.c Thu Oct 31 22:12:58 2002 @@ -0,0 +1,1135 @@ +/* + * linux/fs/ext2/xattr.c + * + * Copyright (C) 2001 by Andreas Gruenbacher, + * + * Fix by Harrison Xing . + * Extended attributes for symlinks and special files added per + * suggestion of Luka Renko . + */ + +/* + * Extended attributes are stored on disk blocks allocated outside of + * any inode. The i_file_acl field is then made to point to this allocated + * block. If all extended attributes of an inode are identical, these + * inodes may share the same extended attribute block. Such situations + * are automatically detected by keeping a cache of recent attribute block + * numbers and hashes over the block's contents in memory. + * + * + * Extended attribute block layout: + * + * +------------------+ + * | header | + * ¦ entry 1 | | + * | entry 2 | | growing downwards + * | entry 3 | v + * | four null bytes | + * | . . . | + * | value 1 | ^ + * | value 3 | | growing upwards + * | value 2 | | + * +------------------+ + * + * The block header is followed by multiple entry descriptors. These entry + * descriptors are variable in size, and alligned to EXT2_XATTR_PAD + * byte boundaries. The entry descriptors are sorted by attribute name, + * so that two extended attribute blocks can be compared efficiently. + * + * Attribute values are aligned to the end of the block, stored in + * no specific order. They are also padded to EXT2_XATTR_PAD byte + * boundaries. No additional gaps are left between them. + * + * Locking strategy + * ---------------- + * The VFS already holds the BKL and the inode->i_sem semaphore when any of + * the xattr inode operations are called, so we are guaranteed that only one + * processes accesses extended attributes of an inode at any time. + * + * For writing we also grab the ext2_xattr_sem semaphore. This ensures that + * only a single process is modifying an extended attribute block, even + * if the block is shared among inodes. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ext2.h" +#include "xattr.h" +#include "acl.h" + +/* These symbols may be needed by a module. */ +EXPORT_SYMBOL(ext2_xattr_register); +EXPORT_SYMBOL(ext2_xattr_unregister); +EXPORT_SYMBOL(ext2_xattr_get); +EXPORT_SYMBOL(ext2_xattr_list); +EXPORT_SYMBOL(ext2_xattr_set); + +#define HDR(bh) ((struct ext2_xattr_header *)((bh)->b_data)) +#define ENTRY(ptr) ((struct ext2_xattr_entry *)(ptr)) +#define FIRST_ENTRY(bh) ENTRY(HDR(bh)+1) +#define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0) + +#ifdef EXT2_XATTR_DEBUG +# define ea_idebug(inode, f...) do { \ + printk(KERN_DEBUG "inode %s:%ld: ", \ + kdevname(inode->i_dev), inode->i_ino); \ + printk(f); \ + printk("\n"); \ + } while (0) +# define ea_bdebug(bh, f...) do { \ + printk(KERN_DEBUG "block %s:%ld: ", \ + kdevname(bh->b_dev), bh->b_blocknr); \ + printk(f); \ + printk("\n"); \ + } while (0) +#else +# define ea_idebug(f...) +# define ea_bdebug(f...) +#endif + +static int ext2_xattr_set2(struct inode *, struct buffer_head *, + struct ext2_xattr_header *); + +static int ext2_xattr_cache_insert(struct buffer_head *); +static struct buffer_head *ext2_xattr_cache_find(struct inode *, + struct ext2_xattr_header *); +static void ext2_xattr_cache_remove(struct buffer_head *); +static void ext2_xattr_rehash(struct ext2_xattr_header *, + struct ext2_xattr_entry *); + +static struct mb_cache *ext2_xattr_cache; + +/* + * If a file system does not share extended attributes among inodes, + * we should not need the ext2_xattr_sem semaphore. However, the + * filesystem may still contain shared blocks, so we always take + * the lock. + */ + +static DECLARE_MUTEX(ext2_xattr_sem); +static struct ext2_xattr_handler *ext2_xattr_handlers[EXT2_XATTR_INDEX_MAX]; +static rwlock_t ext2_handler_lock = RW_LOCK_UNLOCKED; + +int +ext2_xattr_register(int name_index, struct ext2_xattr_handler *handler) +{ + int error = -EINVAL; + + if (name_index > 0 && name_index <= EXT2_XATTR_INDEX_MAX) { + write_lock(&ext2_handler_lock); + if (!ext2_xattr_handlers[name_index-1]) { + ext2_xattr_handlers[name_index-1] = handler; + error = 0; + } + write_unlock(&ext2_handler_lock); + } + return error; +} + +void +ext2_xattr_unregister(int name_index, struct ext2_xattr_handler *handler) +{ + if (name_index > 0 || name_index <= EXT2_XATTR_INDEX_MAX) { + write_lock(&ext2_handler_lock); + ext2_xattr_handlers[name_index-1] = NULL; + write_unlock(&ext2_handler_lock); + } +} + +static inline const char * +strcmp_prefix(const char *a, const char *a_prefix) +{ + while (*a_prefix && *a == *a_prefix) { + a++; + a_prefix++; + } + return *a_prefix ? NULL : a; +} + +/* + * Decode the extended attribute name, and translate it into + * the name_index and name suffix. + */ +static struct ext2_xattr_handler * +ext2_xattr_resolve_name(const char **name) +{ + struct ext2_xattr_handler *handler = NULL; + int i; + + if (!*name) + return NULL; + read_lock(&ext2_handler_lock); + for (i=0; iprefix); + if (n) { + handler = ext2_xattr_handlers[i]; + *name = n; + break; + } + } + } + read_unlock(&ext2_handler_lock); + return handler; +} + +static inline struct ext2_xattr_handler * +ext2_xattr_handler(int name_index) +{ + struct ext2_xattr_handler *handler = NULL; + if (name_index > 0 && name_index <= EXT2_XATTR_INDEX_MAX) { + read_lock(&ext2_handler_lock); + handler = ext2_xattr_handlers[name_index-1]; + read_unlock(&ext2_handler_lock); + } + return handler; +} + +/* + * Inode operation getxattr() + * + * dentry->d_inode->i_sem down + * BKL held [before 2.5.x] + */ +ssize_t +ext2_getxattr(struct dentry *dentry, const char *name, + void *buffer, size_t size) +{ + struct ext2_xattr_handler *handler; + struct inode *inode = dentry->d_inode; + + handler = ext2_xattr_resolve_name(&name); + if (!handler) + return -EOPNOTSUPP; + return handler->get(inode, name, buffer, size); +} + +/* + * Inode operation listxattr() + * + * dentry->d_inode->i_sem down + * BKL held [before 2.5.x] + */ +ssize_t +ext2_listxattr(struct dentry *dentry, char *buffer, size_t size) +{ + return ext2_xattr_list(dentry->d_inode, buffer, size); +} + +/* + * Inode operation setxattr() + * + * dentry->d_inode->i_sem down + * BKL held [before 2.5.x] + */ +int +ext2_setxattr(struct dentry *dentry, const char *name, + void *value, size_t size, int flags) +{ + struct ext2_xattr_handler *handler; + struct inode *inode = dentry->d_inode; + + if (size == 0) + value = ""; /* empty EA, do not remove */ + handler = ext2_xattr_resolve_name(&name); + if (!handler) + return -EOPNOTSUPP; + return handler->set(inode, name, value, size, flags); +} + +/* + * Inode operation removexattr() + * + * dentry->d_inode->i_sem down + * BKL held [before 2.5.x] + */ +int +ext2_removexattr(struct dentry *dentry, const char *name) +{ + struct ext2_xattr_handler *handler; + struct inode *inode = dentry->d_inode; + + handler = ext2_xattr_resolve_name(&name); + if (!handler) + return -EOPNOTSUPP; + return handler->set(inode, name, NULL, 0, XATTR_REPLACE); +} + +/* + * ext2_xattr_get() + * + * Copy an extended attribute into the buffer + * provided, or compute the buffer size required. + * Buffer is NULL to compute the size of the buffer required. + * + * Returns a negative error number on failure, or the number of bytes + * used / required on success. + */ +int +ext2_xattr_get(struct inode *inode, int name_index, const char *name, + void *buffer, size_t buffer_size) +{ + struct buffer_head *bh = NULL; + struct ext2_xattr_entry *entry; + unsigned int size; + char *end; + int name_len, error; + + ea_idebug(inode, "name=%d.%s, buffer=%p, buffer_size=%ld", + name_index, name, buffer, (long)buffer_size); + + if (name == NULL) + return -EINVAL; + if (!EXT2_I(inode)->i_file_acl) + return -ENODATA; + ea_idebug(inode, "reading block %d", EXT2_I(inode)->i_file_acl); + bh = sb_bread(inode->i_sb, EXT2_I(inode)->i_file_acl); + if (!bh) + return -EIO; + ea_bdebug(bh, "b_count=%d, refcount=%d", + atomic_read(&(bh->b_count)), le32_to_cpu(HDR(bh)->h_refcount)); + end = bh->b_data + bh->b_size; + if (HDR(bh)->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) || + HDR(bh)->h_blocks != cpu_to_le32(1)) { +bad_block: ext2_error(inode->i_sb, "ext2_xattr_get", + "inode %ld: bad block %d", inode->i_ino, + EXT2_I(inode)->i_file_acl); + error = -EIO; + goto cleanup; + } + /* find named attribute */ + name_len = strlen(name); + + error = -ERANGE; + if (name_len > 255) + goto cleanup; + entry = FIRST_ENTRY(bh); + while (!IS_LAST_ENTRY(entry)) { + struct ext2_xattr_entry *next = + EXT2_XATTR_NEXT(entry); + if ((char *)next >= end) + goto bad_block; + if (name_index == entry->e_name_index && + name_len == entry->e_name_len && + memcmp(name, entry->e_name, name_len) == 0) + goto found; + entry = next; + } + /* Check the remaining name entries */ + while (!IS_LAST_ENTRY(entry)) { + struct ext2_xattr_entry *next = + EXT2_XATTR_NEXT(entry); + if ((char *)next >= end) + goto bad_block; + entry = next; + } + if (ext2_xattr_cache_insert(bh)) + ea_idebug(inode, "cache insert failed"); + error = -ENODATA; + goto cleanup; +found: + /* check the buffer size */ + if (entry->e_value_block != 0) + goto bad_block; + size = le32_to_cpu(entry->e_value_size); + if (size > inode->i_sb->s_blocksize || + le16_to_cpu(entry->e_value_offs) + size > inode->i_sb->s_blocksize) + goto bad_block; + + if (ext2_xattr_cache_insert(bh)) + ea_idebug(inode, "cache insert failed"); + if (buffer) { + error = -ERANGE; + if (size > buffer_size) + goto cleanup; + /* return value of attribute */ + memcpy(buffer, bh->b_data + le16_to_cpu(entry->e_value_offs), + size); + } + error = size; + +cleanup: + brelse(bh); + + return error; +} + +/* + * ext2_xattr_list() + * + * Copy a list of attribute names into the buffer + * provided, or compute the buffer size required. + * Buffer is NULL to compute the size of the buffer required. + * + * Returns a negative error number on failure, or the number of bytes + * used / required on success. + */ +int +ext2_xattr_list(struct inode *inode, char *buffer, size_t buffer_size) +{ + struct buffer_head *bh = NULL; + struct ext2_xattr_entry *entry; + unsigned int size = 0; + char *buf, *end; + int error; + + ea_idebug(inode, "buffer=%p, buffer_size=%ld", + buffer, (long)buffer_size); + + if (!EXT2_I(inode)->i_file_acl) + return 0; + ea_idebug(inode, "reading block %d", EXT2_I(inode)->i_file_acl); + bh = sb_bread(inode->i_sb, EXT2_I(inode)->i_file_acl); + if (!bh) + return -EIO; + ea_bdebug(bh, "b_count=%d, refcount=%d", + atomic_read(&(bh->b_count)), le32_to_cpu(HDR(bh)->h_refcount)); + end = bh->b_data + bh->b_size; + if (HDR(bh)->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) || + HDR(bh)->h_blocks != cpu_to_le32(1)) { +bad_block: ext2_error(inode->i_sb, "ext2_xattr_list", + "inode %ld: bad block %d", inode->i_ino, + EXT2_I(inode)->i_file_acl); + error = -EIO; + goto cleanup; + } + /* compute the size required for the list of attribute names */ + for (entry = FIRST_ENTRY(bh); !IS_LAST_ENTRY(entry); + entry = EXT2_XATTR_NEXT(entry)) { + struct ext2_xattr_handler *handler; + struct ext2_xattr_entry *next = + EXT2_XATTR_NEXT(entry); + if ((char *)next >= end) + goto bad_block; + + handler = ext2_xattr_handler(entry->e_name_index); + if (handler) { + size += handler->list(NULL, inode, entry->e_name, + entry->e_name_len) + 1; + } + } + + if (ext2_xattr_cache_insert(bh)) + ea_idebug(inode, "cache insert failed"); + if (!buffer) { + error = size; + goto cleanup; + } else { + error = -ERANGE; + if (size > buffer_size) + goto cleanup; + } + + /* list the attribute names */ + buf = buffer; + for (entry = FIRST_ENTRY(bh); !IS_LAST_ENTRY(entry); + entry = EXT2_XATTR_NEXT(entry)) { + struct ext2_xattr_handler *handler; + + handler = ext2_xattr_handler(entry->e_name_index); + if (handler) { + buf += handler->list(buf, inode, entry->e_name, + entry->e_name_len); + *buf++ = '\0'; + } + } + error = size; + +cleanup: + brelse(bh); + + return error; +} + +/* + * If the EXT2_FEATURE_COMPAT_EXT_ATTR feature of this file system is + * not set, set it. + */ +static void ext2_xattr_update_super_block(struct super_block *sb) +{ + if (EXT2_HAS_COMPAT_FEATURE(sb, EXT2_FEATURE_COMPAT_EXT_ATTR)) + return; + + lock_super(sb); + EXT2_SB(sb)->s_es->s_feature_compat |= + cpu_to_le32(EXT2_FEATURE_COMPAT_EXT_ATTR); + sb->s_dirt = 1; + mark_buffer_dirty(EXT2_SB(sb)->s_sbh); + unlock_super(sb); +} + +/* + * ext2_xattr_set() + * + * Create, replace or remove an extended attribute for this inode. Buffer + * is NULL to remove an existing extended attribute, and non-NULL to + * either replace an existing extended attribute, or create a new extended + * attribute. The flags XATTR_REPLACE and XATTR_CREATE + * specify that an extended attribute must exist and must not exist + * previous to the call, respectively. + * + * Returns 0, or a negative error number on failure. + */ +int +ext2_xattr_set(struct inode *inode, int name_index, const char *name, + const void *value, size_t value_len, int flags) +{ + struct super_block *sb = inode->i_sb; + struct buffer_head *bh = NULL; + struct ext2_xattr_header *header = NULL; + struct ext2_xattr_entry *here, *last; + unsigned int name_len; + int min_offs = sb->s_blocksize, not_found = 1, free, error; + char *end; + + /* + * header -- Points either into bh, or to a temporarily + * allocated buffer. + * here -- The named entry found, or the place for inserting, within + * the block pointed to by header. + * last -- Points right after the last named entry within the block + * pointed to by header. + * min_offs -- The offset of the first value (values are aligned + * towards the end of the block). + * end -- Points right after the block pointed to by header. + */ + + ea_idebug(inode, "name=%d.%s, value=%p, value_len=%ld", + name_index, name, value, (long)value_len); + + if (IS_RDONLY(inode)) + return -EROFS; + if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) + return -EPERM; + if (value == NULL) + value_len = 0; + if (name == NULL) + return -EINVAL; + name_len = strlen(name); + if (name_len > 255 || value_len > sb->s_blocksize) + return -ERANGE; + down(&ext2_xattr_sem); + + if (EXT2_I(inode)->i_file_acl) { + /* The inode already has an extended attribute block. */ + bh = sb_bread(sb, EXT2_I(inode)->i_file_acl); + error = -EIO; + if (!bh) + goto cleanup; + ea_bdebug(bh, "b_count=%d, refcount=%d", + atomic_read(&(bh->b_count)), + le32_to_cpu(HDR(bh)->h_refcount)); + header = HDR(bh); + end = bh->b_data + bh->b_size; + if (header->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) || + header->h_blocks != cpu_to_le32(1)) { +bad_block: ext2_error(sb, "ext2_xattr_set", + "inode %ld: bad block %d", inode->i_ino, + EXT2_I(inode)->i_file_acl); + error = -EIO; + goto cleanup; + } + /* Find the named attribute. */ + here = FIRST_ENTRY(bh); + while (!IS_LAST_ENTRY(here)) { + struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(here); + if ((char *)next >= end) + goto bad_block; + if (!here->e_value_block && here->e_value_size) { + int offs = le16_to_cpu(here->e_value_offs); + if (offs < min_offs) + min_offs = offs; + } + not_found = name_index - here->e_name_index; + if (!not_found) + not_found = name_len - here->e_name_len; + if (!not_found) + not_found = memcmp(name, here->e_name,name_len); + if (not_found <= 0) + break; + here = next; + } + last = here; + /* We still need to compute min_offs and last. */ + while (!IS_LAST_ENTRY(last)) { + struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(last); + if ((char *)next >= end) + goto bad_block; + if (!last->e_value_block && last->e_value_size) { + int offs = le16_to_cpu(last->e_value_offs); + if (offs < min_offs) + min_offs = offs; + } + last = next; + } + + /* Check whether we have enough space left. */ + free = min_offs - ((char*)last - (char*)header) - sizeof(__u32); + } else { + /* We will use a new extended attribute block. */ + free = sb->s_blocksize - + sizeof(struct ext2_xattr_header) - sizeof(__u32); + here = last = NULL; /* avoid gcc uninitialized warning. */ + } + + if (not_found) { + /* Request to remove a nonexistent attribute? */ + error = -ENODATA; + if (flags & XATTR_REPLACE) + goto cleanup; + error = 0; + if (value == NULL) + goto cleanup; + else + free -= EXT2_XATTR_LEN(name_len); + } else { + /* Request to create an existing attribute? */ + error = -EEXIST; + if (flags & XATTR_CREATE) + goto cleanup; + if (!here->e_value_block && here->e_value_size) { + unsigned int size = le32_to_cpu(here->e_value_size); + + if (le16_to_cpu(here->e_value_offs) + size > + sb->s_blocksize || size > sb->s_blocksize) + goto bad_block; + free += EXT2_XATTR_SIZE(size); + } + } + free -= EXT2_XATTR_SIZE(value_len); + error = -ENOSPC; + if (free < 0) + goto cleanup; + + /* Here we know that we can set the new attribute. */ + + if (header) { + if (header->h_refcount == cpu_to_le32(1)) { + ea_bdebug(bh, "modifying in-place"); + ext2_xattr_cache_remove(bh); + } else { + int offset; + + ea_bdebug(bh, "cloning"); + header = kmalloc(bh->b_size, GFP_KERNEL); + error = -ENOMEM; + if (header == NULL) + goto cleanup; + memcpy(header, HDR(bh), bh->b_size); + header->h_refcount = cpu_to_le32(1); + offset = (char *)header - bh->b_data; + here = ENTRY((char *)here + offset); + last = ENTRY((char *)last + offset); + } + } else { + /* Allocate a buffer where we construct the new block. */ + header = kmalloc(sb->s_blocksize, GFP_KERNEL); + error = -ENOMEM; + if (header == NULL) + goto cleanup; + memset(header, 0, sb->s_blocksize); + end = (char *)header + sb->s_blocksize; + header->h_magic = cpu_to_le32(EXT2_XATTR_MAGIC); + header->h_blocks = header->h_refcount = cpu_to_le32(1); + last = here = ENTRY(header+1); + } + + if (not_found) { + /* Insert the new name. */ + int size = EXT2_XATTR_LEN(name_len); + int rest = (char *)last - (char *)here; + memmove((char *)here + size, here, rest); + memset(here, 0, size); + here->e_name_index = name_index; + here->e_name_len = name_len; + memcpy(here->e_name, name, name_len); + } else { + /* Remove the old value. */ + if (!here->e_value_block && here->e_value_size) { + char *first_val = (char *)header + min_offs; + int offs = le16_to_cpu(here->e_value_offs); + char *val = (char *)header + offs; + size_t size = EXT2_XATTR_SIZE( + le32_to_cpu(here->e_value_size)); + memmove(first_val + size, first_val, val - first_val); + memset(first_val, 0, size); + here->e_value_offs = 0; + min_offs += size; + + /* Adjust all value offsets. */ + last = ENTRY(header+1); + while (!IS_LAST_ENTRY(last)) { + int o = le16_to_cpu(last->e_value_offs); + if (!last->e_value_block && o < offs) + last->e_value_offs = + cpu_to_le16(o + size); + last = EXT2_XATTR_NEXT(last); + } + } + if (value == NULL) { + /* Remove this attribute. */ + if (EXT2_XATTR_NEXT(ENTRY(header+1)) == last) { + /* This block is now empty. */ + error = ext2_xattr_set2(inode, bh, NULL); + goto cleanup; + } else { + /* Remove the old name. */ + int size = EXT2_XATTR_LEN(name_len); + last = ENTRY((char *)last - size); + memmove(here, (char*)here + size, + (char*)last - (char*)here); + memset(last, 0, size); + } + } + } + + if (value != NULL) { + /* Insert the new value. */ + here->e_value_size = cpu_to_le32(value_len); + if (value_len) { + size_t size = EXT2_XATTR_SIZE(value_len); + char *val = (char *)header + min_offs - size; + here->e_value_offs = + cpu_to_le16((char *)val - (char *)header); + memset(val + size - EXT2_XATTR_PAD, 0, + EXT2_XATTR_PAD); /* Clear the pad bytes. */ + memcpy(val, value, value_len); + } + } + ext2_xattr_rehash(header, here); + + error = ext2_xattr_set2(inode, bh, header); + +cleanup: + brelse(bh); + if (!(bh && header == HDR(bh))) + kfree(header); + up(&ext2_xattr_sem); + + return error; +} + +/* + * Second half of ext2_xattr_set(): Update the file system. + */ +static int +ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh, + struct ext2_xattr_header *header) +{ + struct super_block *sb = inode->i_sb; + struct buffer_head *new_bh = NULL; + int error; + + if (header) { + new_bh = ext2_xattr_cache_find(inode, header); + if (new_bh) { + /* + * We found an identical block in the cache. + * The old block will be released after updating + * the inode. + */ + ea_bdebug(old_bh, "reusing block %ld", + new_bh->b_blocknr); + + error = -EDQUOT; + if (DQUOT_ALLOC_BLOCK(inode, 1)) + goto cleanup; + + HDR(new_bh)->h_refcount = cpu_to_le32( + le32_to_cpu(HDR(new_bh)->h_refcount) + 1); + ea_bdebug(new_bh, "refcount now=%d", + le32_to_cpu(HDR(new_bh)->h_refcount)); + } else if (old_bh && header == HDR(old_bh)) { + /* Keep this block. */ + new_bh = old_bh; + ext2_xattr_cache_insert(new_bh); + } else { + /* We need to allocate a new block */ + int goal = le32_to_cpu(EXT2_SB(sb)->s_es->s_first_data_block) + + EXT2_I(inode)->i_block_group * EXT2_BLOCKS_PER_GROUP(sb); + int block = ext2_new_block(inode, goal, 0, 0, &error); + if (error) + goto cleanup; + ea_idebug(inode, "creating block %d", block); + + new_bh = sb_getblk(sb, block); + if (!new_bh) { + ext2_free_blocks(inode, block, 1); + error = -EIO; + goto cleanup; + } + lock_buffer(new_bh); + memcpy(new_bh->b_data, header, new_bh->b_size); + set_buffer_uptodate(new_bh); + unlock_buffer(new_bh); + ext2_xattr_cache_insert(new_bh); + + ext2_xattr_update_super_block(sb); + } + mark_buffer_dirty(new_bh); + if (IS_SYNC(inode)) { + ll_rw_block(WRITE, 1, &new_bh); + wait_on_buffer(new_bh); + error = -EIO; + if (buffer_req(new_bh) && !buffer_uptodate(new_bh)) + goto cleanup; + } + } + + /* Update the inode. */ + EXT2_I(inode)->i_file_acl = new_bh ? new_bh->b_blocknr : 0; + inode->i_ctime = CURRENT_TIME; + if (IS_SYNC(inode)) { + error = ext2_sync_inode (inode); + if (error) + goto cleanup; + } else + mark_inode_dirty(inode); + + error = 0; + if (old_bh && old_bh != new_bh) { + /* + * If there was an old block, and we are not still using it, + * we now release the old block. + */ + unsigned int refcount = le32_to_cpu(HDR(old_bh)->h_refcount); + + if (refcount == 1) { + /* Free the old block. */ + ea_bdebug(old_bh, "freeing"); + ext2_free_blocks(inode, old_bh->b_blocknr, 1); + /* We let our caller release old_bh, so we + * need to duplicate the buffer before. */ + get_bh(old_bh); + bforget(old_bh); + } else { + /* Decrement the refcount only. */ + refcount--; + HDR(old_bh)->h_refcount = cpu_to_le32(refcount); + DQUOT_FREE_BLOCK(inode, 1); + mark_buffer_dirty(old_bh); + ea_bdebug(old_bh, "refcount now=%d", refcount); + } + } + +cleanup: + if (old_bh != new_bh) + brelse(new_bh); + + return error; +} + +/* + * ext2_xattr_delete_inode() + * + * Free extended attribute resources associated with this inode. This + * is called immediately before an inode is freed. + */ +void +ext2_xattr_delete_inode(struct inode *inode) +{ + struct buffer_head *bh; + + if (!EXT2_I(inode)->i_file_acl) + return; + down(&ext2_xattr_sem); + + bh = sb_bread(inode->i_sb, EXT2_I(inode)->i_file_acl); + if (!bh) { + ext2_error(inode->i_sb, "ext2_xattr_delete_inode", + "inode %ld: block %d read error", inode->i_ino, + EXT2_I(inode)->i_file_acl); + goto cleanup; + } + ea_bdebug(bh, "b_count=%d", atomic_read(&(bh->b_count))); + if (HDR(bh)->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) || + HDR(bh)->h_blocks != cpu_to_le32(1)) { + ext2_error(inode->i_sb, "ext2_xattr_delete_inode", + "inode %ld: bad block %d", inode->i_ino, + EXT2_I(inode)->i_file_acl); + goto cleanup; + } + ea_bdebug(bh, "refcount now=%d", le32_to_cpu(HDR(bh)->h_refcount) - 1); + if (HDR(bh)->h_refcount == cpu_to_le32(1)) { + ext2_xattr_cache_remove(bh); + ext2_free_blocks(inode, EXT2_I(inode)->i_file_acl, 1); + bforget(bh); + bh = NULL; + } else { + HDR(bh)->h_refcount = cpu_to_le32( + le32_to_cpu(HDR(bh)->h_refcount) - 1); + mark_buffer_dirty(bh); + if (IS_SYNC(inode)) { + ll_rw_block(WRITE, 1, &bh); + wait_on_buffer(bh); + } + DQUOT_FREE_BLOCK(inode, 1); + } + EXT2_I(inode)->i_file_acl = 0; + +cleanup: + brelse(bh); + up(&ext2_xattr_sem); +} + +/* + * ext2_xattr_put_super() + * + * This is called when a file system is unmounted. + */ +void +ext2_xattr_put_super(struct super_block *sb) +{ + mb_cache_shrink(ext2_xattr_cache, sb->s_bdev); +} + + +/* + * ext2_xattr_cache_insert() + * + * Create a new entry in the extended attribute cache, and insert + * it unless such an entry is already in the cache. + * + * Returns 0, or a negative error number on failure. + */ +static int +ext2_xattr_cache_insert(struct buffer_head *bh) +{ + __u32 hash = le32_to_cpu(HDR(bh)->h_hash); + struct mb_cache_entry *ce; + int error; + + ce = mb_cache_entry_alloc(ext2_xattr_cache); + if (!ce) + return -ENOMEM; + error = mb_cache_entry_insert(ce, bh->b_bdev, bh->b_blocknr, &hash); + if (error) { + mb_cache_entry_free(ce); + if (error == -EBUSY) { + ea_bdebug(bh, "already in cache (%d cache entries)", + atomic_read(&ext2_xattr_cache->c_entry_count)); + error = 0; + } + } else { + ea_bdebug(bh, "inserting [%x] (%d cache entries)", (int)hash, + atomic_read(&ext2_xattr_cache->c_entry_count)); + mb_cache_entry_release(ce); + } + return error; +} + +/* + * ext2_xattr_cmp() + * + * Compare two extended attribute blocks for equality. + * + * Returns 0 if the blocks are equal, 1 if they differ, and + * a negative error number on errors. + */ +static int +ext2_xattr_cmp(struct ext2_xattr_header *header1, + struct ext2_xattr_header *header2) +{ + struct ext2_xattr_entry *entry1, *entry2; + + entry1 = ENTRY(header1+1); + entry2 = ENTRY(header2+1); + while (!IS_LAST_ENTRY(entry1)) { + if (IS_LAST_ENTRY(entry2)) + return 1; + if (entry1->e_hash != entry2->e_hash || + entry1->e_name_len != entry2->e_name_len || + entry1->e_value_size != entry2->e_value_size || + memcmp(entry1->e_name, entry2->e_name, entry1->e_name_len)) + return 1; + if (entry1->e_value_block != 0 || entry2->e_value_block != 0) + return -EIO; + if (memcmp((char *)header1 + le16_to_cpu(entry1->e_value_offs), + (char *)header2 + le16_to_cpu(entry2->e_value_offs), + le32_to_cpu(entry1->e_value_size))) + return 1; + + entry1 = EXT2_XATTR_NEXT(entry1); + entry2 = EXT2_XATTR_NEXT(entry2); + } + if (!IS_LAST_ENTRY(entry2)) + return 1; + return 0; +} + +/* + * ext2_xattr_cache_find() + * + * Find an identical extended attribute block. + * + * Returns a pointer to the block found, or NULL if such a block was + * not found or an error occurred. + */ +static struct buffer_head * +ext2_xattr_cache_find(struct inode *inode, struct ext2_xattr_header *header) +{ + __u32 hash = le32_to_cpu(header->h_hash); + struct mb_cache_entry *ce; + + if (!header->h_hash) + return NULL; /* never share */ + ea_idebug(inode, "looking for cached blocks [%x]", (int)hash); + ce = mb_cache_entry_find_first(ext2_xattr_cache, 0, inode->i_bdev, hash); + while (ce) { + struct buffer_head *bh = sb_bread(inode->i_sb, ce->e_block); + + if (!bh) { + ext2_error(inode->i_sb, "ext2_xattr_cache_find", + "inode %ld: block %ld read error", + inode->i_ino, (unsigned long) ce->e_block); + } else if (le32_to_cpu(HDR(bh)->h_refcount) > + EXT2_XATTR_REFCOUNT_MAX) { + ea_idebug(inode, "block %ld refcount %d>%d", + (unsigned long) ce->e_block, + le32_to_cpu(HDR(bh)->h_refcount), + EXT2_XATTR_REFCOUNT_MAX); + } else if (!ext2_xattr_cmp(header, HDR(bh))) { + ea_bdebug(bh, "b_count=%d",atomic_read(&(bh->b_count))); + mb_cache_entry_release(ce); + return bh; + } + brelse(bh); + ce = mb_cache_entry_find_next(ce, 0, inode->i_bdev, hash); + } + return NULL; +} + +/* + * ext2_xattr_cache_remove() + * + * Remove the cache entry of a block from the cache. Called when a + * block becomes invalid. + */ +static void +ext2_xattr_cache_remove(struct buffer_head *bh) +{ + struct mb_cache_entry *ce; + + ce = mb_cache_entry_get(ext2_xattr_cache, bh->b_bdev, bh->b_blocknr); + if (ce) { + ea_bdebug(bh, "removing (%d cache entries remaining)", + atomic_read(&ext2_xattr_cache->c_entry_count)-1); + mb_cache_entry_free(ce); + } else + ea_bdebug(bh, "no cache entry"); +} + +#define NAME_HASH_SHIFT 5 +#define VALUE_HASH_SHIFT 16 + +/* + * ext2_xattr_hash_entry() + * + * Compute the hash of an extended attribute. + */ +static inline void ext2_xattr_hash_entry(struct ext2_xattr_header *header, + struct ext2_xattr_entry *entry) +{ + __u32 hash = 0; + char *name = entry->e_name; + int n; + + for (n=0; n < entry->e_name_len; n++) { + hash = (hash << NAME_HASH_SHIFT) ^ + (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^ + *name++; + } + + if (entry->e_value_block == 0 && entry->e_value_size != 0) { + __u32 *value = (__u32 *)((char *)header + + le16_to_cpu(entry->e_value_offs)); + for (n = (le32_to_cpu(entry->e_value_size) + + EXT2_XATTR_ROUND) >> EXT2_XATTR_PAD_BITS; n; n--) { + hash = (hash << VALUE_HASH_SHIFT) ^ + (hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^ + le32_to_cpu(*value++); + } + } + entry->e_hash = cpu_to_le32(hash); +} + +#undef NAME_HASH_SHIFT +#undef VALUE_HASH_SHIFT + +#define BLOCK_HASH_SHIFT 16 + +/* + * ext2_xattr_rehash() + * + * Re-compute the extended attribute hash value after an entry has changed. + */ +static void ext2_xattr_rehash(struct ext2_xattr_header *header, + struct ext2_xattr_entry *entry) +{ + struct ext2_xattr_entry *here; + __u32 hash = 0; + + ext2_xattr_hash_entry(header, entry); + here = ENTRY(header+1); + while (!IS_LAST_ENTRY(here)) { + if (!here->e_hash) { + /* Block is not shared if an entry's hash value == 0 */ + hash = 0; + break; + } + hash = (hash << BLOCK_HASH_SHIFT) ^ + (hash >> (8*sizeof(hash) - BLOCK_HASH_SHIFT)) ^ + le32_to_cpu(here->e_hash); + here = EXT2_XATTR_NEXT(here); + } + header->h_hash = cpu_to_le32(hash); +} + +#undef BLOCK_HASH_SHIFT + +int __init +init_ext2_xattr(void) +{ + int err; + + err = ext2_xattr_register(EXT2_XATTR_INDEX_USER, &ext2_xattr_user_handler); + if (err) + return err; +#ifdef CONFIG_EXT2_FS_POSIX_ACL + err = init_ext2_acl(); + if (err) + goto out; +#endif + ext2_xattr_cache = mb_cache_create("ext2_xattr", NULL, + sizeof(struct mb_cache_entry) + + sizeof(struct mb_cache_entry_index), 1, 6); + if (!ext2_xattr_cache) { + err = -ENOMEM; + goto out1; + } + return 0; +out1: +#ifdef CONFIG_EXT2_FS_POSIX_ACL + exit_ext2_acl(); +out: +#endif + ext2_xattr_unregister(EXT2_XATTR_INDEX_USER, + &ext2_xattr_user_handler); + return err; +} + +void +exit_ext2_xattr(void) +{ + mb_cache_destroy(ext2_xattr_cache); +#ifdef CONFIG_EXT2_FS_POSIX_ACL + exit_ext2_acl(); +#endif + ext2_xattr_unregister(EXT2_XATTR_INDEX_USER, &ext2_xattr_user_handler); +} diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/ext2/xattr.h linux-2.5-bk/fs/ext2/xattr.h --- linux-2.5.45/fs/ext2/xattr.h Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/fs/ext2/xattr.h Thu Oct 31 22:12:58 2002 @@ -0,0 +1,135 @@ +/* + File: linux/ext2_xattr.h + + On-disk format of extended attributes for the ext2 filesystem. + + (C) 2001 Andreas Gruenbacher, +*/ + +#include +#include +#include + +/* Magic value in attribute blocks */ +#define EXT2_XATTR_MAGIC 0xEA020000 + +/* Maximum number of references to one attribute block */ +#define EXT2_XATTR_REFCOUNT_MAX 1024 + +/* Name indexes */ +#define EXT2_XATTR_INDEX_MAX 10 +#define EXT2_XATTR_INDEX_USER 1 +#define EXT2_XATTR_INDEX_POSIX_ACL_ACCESS 2 +#define EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT 3 + +struct ext2_xattr_header { + __u32 h_magic; /* magic number for identification */ + __u32 h_refcount; /* reference count */ + __u32 h_blocks; /* number of disk blocks used */ + __u32 h_hash; /* hash value of all attributes */ + __u32 h_reserved[4]; /* zero right now */ +}; + +struct ext2_xattr_entry { + __u8 e_name_len; /* length of name */ + __u8 e_name_index; /* attribute name index */ + __u16 e_value_offs; /* offset in disk block of value */ + __u32 e_value_block; /* disk block attribute is stored on (n/i) */ + __u32 e_value_size; /* size of attribute value */ + __u32 e_hash; /* hash value of name and value */ + char e_name[0]; /* attribute name */ +}; + +#define EXT2_XATTR_PAD_BITS 2 +#define EXT2_XATTR_PAD (1<e_name_len)) ) +#define EXT2_XATTR_SIZE(size) \ + (((size) + EXT2_XATTR_ROUND) & ~EXT2_XATTR_ROUND) + +# ifdef CONFIG_EXT2_FS_XATTR + +struct ext2_xattr_handler { + char *prefix; + size_t (*list)(char *list, struct inode *inode, const char *name, + int name_len); + int (*get)(struct inode *inode, const char *name, void *buffer, + size_t size); + int (*set)(struct inode *inode, const char *name, const void *buffer, + size_t size, int flags); +}; + +extern int ext2_xattr_register(int, struct ext2_xattr_handler *); +extern void ext2_xattr_unregister(int, struct ext2_xattr_handler *); + +extern int ext2_setxattr(struct dentry *, const char *, void *, size_t, int); +extern ssize_t ext2_getxattr(struct dentry *, const char *, void *, size_t); +extern ssize_t ext2_listxattr(struct dentry *, char *, size_t); +extern int ext2_removexattr(struct dentry *, const char *); + +extern int ext2_xattr_get(struct inode *, int, const char *, void *, size_t); +extern int ext2_xattr_list(struct inode *, char *, size_t); +extern int ext2_xattr_set(struct inode *, int, const char *, const void *, size_t, int); + +extern void ext2_xattr_delete_inode(struct inode *); +extern void ext2_xattr_put_super(struct super_block *); + +extern int init_ext2_xattr(void); +extern void exit_ext2_xattr(void); + +# else /* CONFIG_EXT2_FS_XATTR */ +# define ext2_setxattr NULL +# define ext2_getxattr NULL +# define ext2_listxattr NULL +# define ext2_removexattr NULL + +static inline int +ext2_xattr_get(struct inode *inode, int name_index, + const char *name, void *buffer, size_t size) +{ + return -EOPNOTSUPP; +} + +static inline int +ext2_xattr_list(struct inode *inode, char *buffer, size_t size) +{ + return -EOPNOTSUPP; +} + +static inline int +ext2_xattr_set(struct inode *inode, int name_index, const char *name, + const void *value, size_t size, int flags) +{ + return -EOPNOTSUPP; +} + +static inline void +ext2_xattr_delete_inode(struct inode *inode) +{ +} + +static inline void +ext2_xattr_put_super(struct super_block *sb) +{ +} + +static inline int +init_ext2_xattr(void) +{ + return 0; +} + +static inline void +exit_ext2_xattr(void) +{ +} + +# endif /* CONFIG_EXT2_FS_XATTR */ + +extern struct ext2_xattr_handler ext2_xattr_user_handler; + diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/ext2/xattr_user.c linux-2.5-bk/fs/ext2/xattr_user.c --- linux-2.5.45/fs/ext2/xattr_user.c Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/fs/ext2/xattr_user.c Thu Oct 31 22:12:58 2002 @@ -0,0 +1,102 @@ +/* + * linux/fs/ext2/xattr_user.c + * Handler for extended user attributes. + * + * Copyright (C) 2001 by Andreas Gruenbacher, + */ + +#include +#include +#include +#include "ext2.h" +#include "xattr.h" + +#ifdef CONFIG_EXT2_FS_POSIX_ACL +# include "acl.h" +#endif + +#define XATTR_USER_PREFIX "user." + +static size_t +ext2_xattr_user_list(char *list, struct inode *inode, + const char *name, int name_len) +{ + const int prefix_len = sizeof(XATTR_USER_PREFIX)-1; + + if (!test_opt(inode->i_sb, XATTR_USER)) + return 0; + + if (list) { + memcpy(list, XATTR_USER_PREFIX, prefix_len); + memcpy(list+prefix_len, name, name_len); + } + return prefix_len + name_len; +} + +static int +ext2_xattr_user_get(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + int error; + + if (strcmp(name, "") == 0) + return -EINVAL; + if (!test_opt(inode->i_sb, XATTR_USER)) + return -EOPNOTSUPP; +#ifdef CONFIG_EXT2_FS_POSIX_ACL + error = ext2_permission_locked(inode, MAY_READ); +#else + error = permission(inode, MAY_READ); +#endif + if (error) + return error; + + return ext2_xattr_get(inode, EXT2_XATTR_INDEX_USER, name, + buffer, size); +} + +static int +ext2_xattr_user_set(struct inode *inode, const char *name, + const void *value, size_t size, int flags) +{ + int error; + + if (strcmp(name, "") == 0) + return -EINVAL; + if (!test_opt(inode->i_sb, XATTR_USER)) + return -EOPNOTSUPP; + if ( !S_ISREG(inode->i_mode) && + (!S_ISDIR(inode->i_mode) || inode->i_mode & S_ISVTX)) + return -EPERM; +#ifdef CONFIG_EXT2_FS_POSIX_ACL + error = ext2_permission_locked(inode, MAY_WRITE); +#else + error = permission(inode, MAY_WRITE); +#endif + if (error) + return error; + + return ext2_xattr_set(inode, EXT2_XATTR_INDEX_USER, name, + value, size, flags); +} + +struct ext2_xattr_handler ext2_xattr_user_handler = { + prefix: XATTR_USER_PREFIX, + list: ext2_xattr_user_list, + get: ext2_xattr_user_get, + set: ext2_xattr_user_set, +}; + +int __init +init_ext2_xattr_user(void) +{ + return ext2_xattr_register(EXT2_XATTR_INDEX_USER, + &ext2_xattr_user_handler); +} + +void +exit_ext2_xattr_user(void) +{ + ext2_xattr_unregister(EXT2_XATTR_INDEX_USER, + &ext2_xattr_user_handler); +} diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/ext3/Makefile linux-2.5-bk/fs/ext3/Makefile --- linux-2.5.45/fs/ext3/Makefile Wed Oct 30 16:41:54 2002 +++ linux-2.5-bk/fs/ext3/Makefile Thu Oct 31 22:12:58 2002 @@ -7,4 +7,14 @@ ext3-objs := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \ ioctl.o namei.o super.o symlink.o hash.o +export-objs += xattr.o + +ifeq ($(CONFIG_EXT3_FS_XATTR),y) +ext3-objs += xattr.o xattr_user.o +endif + +ifeq ($(CONFIG_EXT3_FS_POSIX_ACL),y) +ext3-objs += acl.o +endif + include $(TOPDIR)/Rules.make diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/ext3/acl.c linux-2.5-bk/fs/ext3/acl.c --- linux-2.5.45/fs/ext3/acl.c Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/fs/ext3/acl.c Thu Oct 31 22:12:58 2002 @@ -0,0 +1,590 @@ +/* + * linux/fs/ext3/acl.c + * + * Copyright (C) 2001 by Andreas Gruenbacher, + */ + +#include +#include +#include +#include +#include +#include +#include "xattr.h" +#include "acl.h" + +/* + * Convert from filesystem to in-memory representation. + */ +static struct posix_acl * +ext3_acl_from_disk(const void *value, size_t size) +{ + const char *end = (char *)value + size; + int n, count; + struct posix_acl *acl; + + if (!value) + return NULL; + if (size < sizeof(ext3_acl_header)) + return ERR_PTR(-EINVAL); + if (((ext3_acl_header *)value)->a_version != + cpu_to_le32(EXT3_ACL_VERSION)) + return ERR_PTR(-EINVAL); + value = (char *)value + sizeof(ext3_acl_header); + count = ext3_acl_count(size); + if (count < 0) + return ERR_PTR(-EINVAL); + if (count == 0) + return NULL; + acl = posix_acl_alloc(count, GFP_KERNEL); + if (!acl) + return ERR_PTR(-ENOMEM); + for (n=0; n < count; n++) { + ext3_acl_entry *entry = + (ext3_acl_entry *)value; + if ((char *)value + sizeof(ext3_acl_entry_short) > end) + goto fail; + acl->a_entries[n].e_tag = le16_to_cpu(entry->e_tag); + acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm); + switch(acl->a_entries[n].e_tag) { + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + value = (char *)value + + sizeof(ext3_acl_entry_short); + acl->a_entries[n].e_id = ACL_UNDEFINED_ID; + break; + + case ACL_USER: + case ACL_GROUP: + value = (char *)value + sizeof(ext3_acl_entry); + if ((char *)value > end) + goto fail; + acl->a_entries[n].e_id = + le32_to_cpu(entry->e_id); + break; + + default: + goto fail; + } + } + if (value != end) + goto fail; + return acl; + +fail: + posix_acl_release(acl); + return ERR_PTR(-EINVAL); +} + +/* + * Convert from in-memory to filesystem representation. + */ +static void * +ext3_acl_to_disk(const struct posix_acl *acl, size_t *size) +{ + ext3_acl_header *ext_acl; + char *e; + int n; + + *size = ext3_acl_size(acl->a_count); + ext_acl = (ext3_acl_header *)kmalloc(sizeof(ext3_acl_header) + + acl->a_count * sizeof(ext3_acl_entry), GFP_KERNEL); + if (!ext_acl) + return ERR_PTR(-ENOMEM); + ext_acl->a_version = cpu_to_le32(EXT3_ACL_VERSION); + e = (char *)ext_acl + sizeof(ext3_acl_header); + for (n=0; n < acl->a_count; n++) { + ext3_acl_entry *entry = (ext3_acl_entry *)e; + entry->e_tag = cpu_to_le16(acl->a_entries[n].e_tag); + entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm); + switch(acl->a_entries[n].e_tag) { + case ACL_USER: + case ACL_GROUP: + entry->e_id = + cpu_to_le32(acl->a_entries[n].e_id); + e += sizeof(ext3_acl_entry); + break; + + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + e += sizeof(ext3_acl_entry_short); + break; + + default: + goto fail; + } + } + return (char *)ext_acl; + +fail: + kfree(ext_acl); + return ERR_PTR(-EINVAL); +} + +/* + * Inode operation get_posix_acl(). + * + * inode->i_sem: down + */ +static struct posix_acl * +ext3_get_acl(struct inode *inode, int type) +{ + int name_index; + char *value; + struct posix_acl *acl, **p_acl; + const size_t size = ext3_acl_size(EXT3_ACL_MAX_ENTRIES); + int retval; + + if (!test_opt(inode->i_sb, POSIX_ACL)) + return 0; + + switch(type) { + case ACL_TYPE_ACCESS: + p_acl = &EXT3_I(inode)->i_acl; + name_index = EXT3_XATTR_INDEX_POSIX_ACL_ACCESS; + break; + + case ACL_TYPE_DEFAULT: + p_acl = &EXT3_I(inode)->i_default_acl; + name_index = EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT; + break; + + default: + return ERR_PTR(-EINVAL); + } + if (*p_acl != EXT3_ACL_NOT_CACHED) + return posix_acl_dup(*p_acl); + value = kmalloc(size, GFP_KERNEL); + if (!value) + return ERR_PTR(-ENOMEM); + + retval = ext3_xattr_get(inode, name_index, "", value, size); + + if (retval == -ENODATA || retval == -ENOSYS) + *p_acl = acl = NULL; + else if (retval < 0) + acl = ERR_PTR(retval); + else { + acl = ext3_acl_from_disk(value, retval); + if (!IS_ERR(acl)) + *p_acl = posix_acl_dup(acl); + } + kfree(value); + return acl; +} + +/* + * Set the access or default ACL of an inode. + * + * inode->i_sem: down unless called from ext3_new_inode + */ +static int +ext3_set_acl(handle_t *handle, struct inode *inode, int type, + struct posix_acl *acl) +{ + int name_index; + void *value = NULL; + struct posix_acl **p_acl; + size_t size; + int error; + + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + + switch(type) { + case ACL_TYPE_ACCESS: + name_index = EXT3_XATTR_INDEX_POSIX_ACL_ACCESS; + p_acl = &EXT3_I(inode)->i_acl; + if (acl) { + mode_t mode = inode->i_mode; + error = posix_acl_equiv_mode(acl, &mode); + if (error < 0) + return error; + else { + inode->i_mode = mode; + ext3_mark_inode_dirty(handle, inode); + if (error == 0) + acl = NULL; + } + } + break; + + case ACL_TYPE_DEFAULT: + name_index = EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT; + p_acl = &EXT3_I(inode)->i_default_acl; + if (!S_ISDIR(inode->i_mode)) + return acl ? -EACCES : 0; + break; + + default: + return -EINVAL; + } + if (acl) { + if (acl->a_count > EXT3_ACL_MAX_ENTRIES) + return -EINVAL; + value = ext3_acl_to_disk(acl, &size); + if (IS_ERR(value)) + return (int)PTR_ERR(value); + } + + error = ext3_xattr_set(handle, inode, name_index, "", value, size, 0); + + if (value) + kfree(value); + if (!error) { + if (*p_acl && *p_acl != EXT3_ACL_NOT_CACHED) + posix_acl_release(*p_acl); + *p_acl = posix_acl_dup(acl); + } + return error; +} + +static int +__ext3_permission(struct inode *inode, int mask, int lock) +{ + int mode = inode->i_mode; + + /* Nobody gets write access to a read-only fs */ + if ((mask & MAY_WRITE) && IS_RDONLY(inode) && + (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) + return -EROFS; + /* Nobody gets write access to an immutable file */ + if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode)) + return -EACCES; + if (current->fsuid == inode->i_uid) { + mode >>= 6; + } else if (test_opt(inode->i_sb, POSIX_ACL)) { + /* ACL can't contain additional permissions if + the ACL_MASK entry is 0 */ + if (!(mode & S_IRWXG)) + goto check_groups; + if (EXT3_I(inode)->i_acl == EXT3_ACL_NOT_CACHED) { + struct posix_acl *acl; + + if (lock) { + down(&inode->i_sem); + acl = ext3_get_acl(inode, ACL_TYPE_ACCESS); + up(&inode->i_sem); + } else + acl = ext3_get_acl(inode, ACL_TYPE_ACCESS); + + if (IS_ERR(acl)) + return PTR_ERR(acl); + posix_acl_release(acl); + if (EXT3_I(inode)->i_acl == EXT3_ACL_NOT_CACHED) + return -EIO; + } + if (EXT3_I(inode)->i_acl) { + int error = posix_acl_permission(inode, + EXT3_I(inode)->i_acl, mask); + if (error == -EACCES) + goto check_capabilities; + return error; + } else + goto check_groups; + } else { +check_groups: + if (in_group_p(inode->i_gid)) + mode >>= 3; + } + if ((mode & mask & S_IRWXO) == mask) + return 0; + +check_capabilities: + /* Allowed to override Discretionary Access Control? */ + if ((mask & (MAY_READ|MAY_WRITE)) || (inode->i_mode & S_IXUGO)) + if (capable(CAP_DAC_OVERRIDE)) + return 0; + /* Read and search granted if capable(CAP_DAC_READ_SEARCH) */ + if (capable(CAP_DAC_READ_SEARCH) && ((mask == MAY_READ) || + (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE)))) + return 0; + return -EACCES; +} + +/* + * Inode operation permission(). + * + * inode->i_sem: up + */ +int +ext3_permission(struct inode *inode, int mask) +{ + return __ext3_permission(inode, mask, 1); +} + +/* + * Used internally if i_sem is already down. + */ +int +ext3_permission_locked(struct inode *inode, int mask) +{ + return __ext3_permission(inode, mask, 0); +} + +/* + * Initialize the ACLs of a new inode. Called from ext3_new_inode. + * + * dir->i_sem: down + * inode->i_sem: up (access to inode is still exclusive) + */ +int +ext3_init_acl(handle_t *handle, struct inode *inode, struct inode *dir) +{ + struct posix_acl *acl = NULL; + int error = 0; + + if (!S_ISLNK(inode->i_mode)) { + if (test_opt(dir->i_sb, POSIX_ACL)) { + acl = ext3_get_acl(dir, ACL_TYPE_DEFAULT); + if (IS_ERR(acl)) + return PTR_ERR(acl); + } + if (!acl) + inode->i_mode &= ~current->fs->umask; + } + if (test_opt(inode->i_sb, POSIX_ACL) && acl) { + struct posix_acl *clone; + mode_t mode; + + if (S_ISDIR(inode->i_mode)) { + error = ext3_set_acl(handle, inode, + ACL_TYPE_DEFAULT, acl); + if (error) + goto cleanup; + } + clone = posix_acl_clone(acl, GFP_KERNEL); + error = -ENOMEM; + if (!clone) + goto cleanup; + + mode = inode->i_mode; + error = posix_acl_create_masq(clone, &mode); + if (error >= 0) { + inode->i_mode = mode; + if (error > 0) { + /* This is an extended ACL */ + error = ext3_set_acl(handle, inode, + ACL_TYPE_ACCESS, clone); + } + } + posix_acl_release(clone); + } +cleanup: + posix_acl_release(acl); + return error; +} + +/* + * Does chmod for an inode that may have an Access Control List. The + * inode->i_mode field must be updated to the desired value by the caller + * before calling this function. + * Returns 0 on success, or a negative error number. + * + * We change the ACL rather than storing some ACL entries in the file + * mode permission bits (which would be more efficient), because that + * would break once additional permissions (like ACL_APPEND, ACL_DELETE + * for directories) are added. There are no more bits available in the + * file mode. + * + * inode->i_sem: down + */ +int +ext3_acl_chmod(struct inode *inode) +{ + struct posix_acl *acl, *clone; + int error; + + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + if (!test_opt(inode->i_sb, POSIX_ACL)) + return 0; + acl = ext3_get_acl(inode, ACL_TYPE_ACCESS); + if (IS_ERR(acl) || !acl) + return PTR_ERR(acl); + clone = posix_acl_clone(acl, GFP_KERNEL); + posix_acl_release(acl); + if (!clone) + return -ENOMEM; + error = posix_acl_chmod_masq(clone, inode->i_mode); + if (!error) { + handle_t *handle; + + handle = ext3_journal_start(inode, EXT3_XATTR_TRANS_BLOCKS); + if (IS_ERR(handle)) { + ext3_std_error(inode->i_sb, error); + return PTR_ERR(handle); + } + error = ext3_set_acl(handle, inode, ACL_TYPE_ACCESS, clone); + ext3_journal_stop(handle, inode); + } + posix_acl_release(clone); + return error; +} + +/* + * Extended attribute handlers + */ +static size_t +ext3_xattr_list_acl_access(char *list, struct inode *inode, + const char *name, int name_len) +{ + const size_t len = sizeof(XATTR_NAME_ACL_ACCESS)-1; + + if (!test_opt(inode->i_sb, POSIX_ACL)) + return 0; + if (list) + memcpy(list, XATTR_NAME_ACL_ACCESS, len); + return len; +} + +static size_t +ext3_xattr_list_acl_default(char *list, struct inode *inode, + const char *name, int name_len) +{ + const size_t len = sizeof(XATTR_NAME_ACL_DEFAULT)-1; + + if (!test_opt(inode->i_sb, POSIX_ACL)) + return 0; + if (list) + memcpy(list, XATTR_NAME_ACL_DEFAULT, len); + return len; +} + +static int +ext3_xattr_get_acl(struct inode *inode, int type, void *buffer, size_t size) +{ + struct posix_acl *acl; + int error; + + if (!test_opt(inode->i_sb, POSIX_ACL)) + return -EOPNOTSUPP; + + acl = ext3_get_acl(inode, type); + if (IS_ERR(acl)) + return PTR_ERR(acl); + if (acl == NULL) + return -ENODATA; + error = posix_acl_to_xattr(acl, buffer, size); + posix_acl_release(acl); + + return error; +} + +static int +ext3_xattr_get_acl_access(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return ext3_xattr_get_acl(inode, ACL_TYPE_ACCESS, buffer, size); +} + +static int +ext3_xattr_get_acl_default(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return ext3_xattr_get_acl(inode, ACL_TYPE_DEFAULT, buffer, size); +} + +static int +ext3_xattr_set_acl(struct inode *inode, int type, const void *value, size_t size) +{ + handle_t *handle; + struct posix_acl *acl; + int error; + + if (!test_opt(inode->i_sb, POSIX_ACL)) + return -EOPNOTSUPP; + if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + return -EPERM; + + if (value) { + acl = posix_acl_from_xattr(value, size); + if (IS_ERR(acl)) + return PTR_ERR(acl); + else if (acl) { + error = posix_acl_valid(acl); + if (error) + goto release_and_out; + } + } else + acl = NULL; + + handle = ext3_journal_start(inode, EXT3_XATTR_TRANS_BLOCKS); + if (IS_ERR(handle)) + return PTR_ERR(handle); + error = ext3_set_acl(handle, inode, type, acl); + ext3_journal_stop(handle, inode); + +release_and_out: + posix_acl_release(acl); + return error; +} + +static int +ext3_xattr_set_acl_access(struct inode *inode, const char *name, + const void *value, size_t size, int flags) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return ext3_xattr_set_acl(inode, ACL_TYPE_ACCESS, value, size); +} + +static int +ext3_xattr_set_acl_default(struct inode *inode, const char *name, + const void *value, size_t size, int flags) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return ext3_xattr_set_acl(inode, ACL_TYPE_DEFAULT, value, size); +} + +struct ext3_xattr_handler ext3_xattr_acl_access_handler = { + prefix: XATTR_NAME_ACL_ACCESS, + list: ext3_xattr_list_acl_access, + get: ext3_xattr_get_acl_access, + set: ext3_xattr_set_acl_access, +}; + +struct ext3_xattr_handler ext3_xattr_acl_default_handler = { + prefix: XATTR_NAME_ACL_DEFAULT, + list: ext3_xattr_list_acl_default, + get: ext3_xattr_get_acl_default, + set: ext3_xattr_set_acl_default, +}; + +void +exit_ext3_acl(void) +{ + ext3_xattr_unregister(EXT3_XATTR_INDEX_POSIX_ACL_ACCESS, + &ext3_xattr_acl_access_handler); + ext3_xattr_unregister(EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT, + &ext3_xattr_acl_default_handler); +} + +int __init +init_ext3_acl(void) +{ + int error; + + error = ext3_xattr_register(EXT3_XATTR_INDEX_POSIX_ACL_ACCESS, + &ext3_xattr_acl_access_handler); + if (error) + goto fail; + error = ext3_xattr_register(EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT, + &ext3_xattr_acl_default_handler); + if (error) + goto fail; + return 0; + +fail: + exit_ext3_acl(); + return error; +} diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/ext3/acl.h linux-2.5-bk/fs/ext3/acl.h --- linux-2.5.45/fs/ext3/acl.h Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/fs/ext3/acl.h Thu Oct 31 22:12:58 2002 @@ -0,0 +1,87 @@ +/* + File: fs/ext3/acl.h + + (C) 2001 Andreas Gruenbacher, +*/ + +#include + +#define EXT3_ACL_VERSION 0x0001 +#define EXT3_ACL_MAX_ENTRIES 32 + +typedef struct { + __u16 e_tag; + __u16 e_perm; + __u32 e_id; +} ext3_acl_entry; + +typedef struct { + __u16 e_tag; + __u16 e_perm; +} ext3_acl_entry_short; + +typedef struct { + __u32 a_version; +} ext3_acl_header; + +static inline size_t ext3_acl_size(int count) +{ + if (count <= 4) { + return sizeof(ext3_acl_header) + + count * sizeof(ext3_acl_entry_short); + } else { + return sizeof(ext3_acl_header) + + 4 * sizeof(ext3_acl_entry_short) + + (count - 4) * sizeof(ext3_acl_entry); + } +} + +static inline int ext3_acl_count(size_t size) +{ + ssize_t s; + size -= sizeof(ext3_acl_header); + s = size - 4 * sizeof(ext3_acl_entry_short); + if (s < 0) { + if (size % sizeof(ext3_acl_entry_short)) + return -1; + return size / sizeof(ext3_acl_entry_short); + } else { + if (s % sizeof(ext3_acl_entry)) + return -1; + return s / sizeof(ext3_acl_entry) + 4; + } +} + +#ifdef CONFIG_EXT3_FS_POSIX_ACL + +/* Value for inode->u.ext3_i.i_acl and inode->u.ext3_i.i_default_acl + if the ACL has not been cached */ +#define EXT3_ACL_NOT_CACHED ((void *)-1) + +/* acl.c */ +extern int ext3_permission (struct inode *, int); +extern int ext3_permission_locked (struct inode *, int); +extern int ext3_acl_chmod (struct inode *); +extern int ext3_init_acl (handle_t *, struct inode *, struct inode *); + +extern int init_ext3_acl(void); +extern void exit_ext3_acl(void); + +#else /* CONFIG_EXT3_FS_POSIX_ACL */ +#include +#define ext3_permission NULL + +static inline int +ext3_acl_chmod(struct inode *inode) +{ + return 0; +} + +static inline int +ext3_init_acl(handle_t *handle, struct inode *inode, struct inode *dir) +{ + inode->i_mode &= ~current->fs->umask; + return 0; +} +#endif /* CONFIG_EXT3_FS_POSIX_ACL */ + diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/ext3/file.c linux-2.5-bk/fs/ext3/file.c --- linux-2.5.45/fs/ext3/file.c Wed Oct 30 16:43:44 2002 +++ linux-2.5-bk/fs/ext3/file.c Thu Oct 31 22:12:58 2002 @@ -23,7 +23,8 @@ #include #include #include -#include +#include "xattr.h" +#include "acl.h" /* * Called when an inode is released. Note that this is different @@ -98,5 +99,10 @@ struct inode_operations ext3_file_inode_operations = { .truncate = ext3_truncate, .setattr = ext3_setattr, + .setxattr = ext3_setxattr, + .getxattr = ext3_getxattr, + .listxattr = ext3_listxattr, + .removexattr = ext3_removexattr, + .permission = ext3_permission, }; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/ext3/ialloc.c linux-2.5-bk/fs/ext3/ialloc.c --- linux-2.5.45/fs/ext3/ialloc.c Wed Oct 30 16:42:56 2002 +++ linux-2.5-bk/fs/ext3/ialloc.c Thu Oct 31 22:12:58 2002 @@ -25,6 +25,9 @@ #include #include +#include "xattr.h" +#include "acl.h" + /* * ialloc.c contains the inodes allocation and deallocation routines */ @@ -118,6 +121,7 @@ * as writing the quota to disk may need the lock as well. */ DQUOT_INIT(inode); + ext3_xattr_delete_inode(handle, inode); DQUOT_FREE_INODE(inode); DQUOT_DROP(inode); @@ -420,20 +424,27 @@ inode->i_generation = EXT3_SB(sb)->s_next_generation++; ei->i_state = EXT3_STATE_NEW; - err = ext3_mark_inode_dirty(handle, inode); - if (err) goto fail; - + unlock_super(sb); ret = inode; if(DQUOT_ALLOC_INODE(inode)) { DQUOT_DROP(inode); - inode->i_flags |= S_NOQUOTA; - inode->i_nlink = 0; - iput(inode); - ret = ERR_PTR(-EDQUOT); - } else { - ext3_debug("allocating inode %lu\n", inode->i_ino); + err = -EDQUOT; + goto fail2; } + err = ext3_init_acl(handle, inode, dir); + if (err) { + DQUOT_FREE_INODE(inode); + goto fail2; + } + err = ext3_mark_inode_dirty(handle, inode); + if (err) { + ext3_std_error(sb, err); + DQUOT_FREE_INODE(inode); + goto fail2; + } + + ext3_debug("allocating inode %lu\n", inode->i_ino); goto really_out; fail: ext3_std_error(sb, err); @@ -444,6 +455,12 @@ really_out: brelse(bitmap_bh); return ret; + +fail2: + inode->i_flags |= S_NOQUOTA; + inode->i_nlink = 0; + iput(inode); + return ERR_PTR(err); } /* Verify that we are loading a valid orphan from disk */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/ext3/inode.c linux-2.5-bk/fs/ext3/inode.c --- linux-2.5.45/fs/ext3/inode.c Wed Oct 30 16:43:07 2002 +++ linux-2.5-bk/fs/ext3/inode.c Thu Oct 31 22:12:58 2002 @@ -34,6 +34,8 @@ #include #include #include +#include "xattr.h" +#include "acl.h" /* * SEARCH_FROM_ZERO forces each block allocation to search from the start @@ -42,6 +44,18 @@ */ #undef SEARCH_FROM_ZERO +/* + * Test whether an inode is a fast symlink. + */ +static inline int ext3_inode_is_fast_symlink(struct inode *inode) +{ + int ea_blocks = EXT3_I(inode)->i_file_acl ? + (inode->i_sb->s_blocksize >> 9) : 0; + + return (S_ISLNK(inode->i_mode) && + inode->i_blocks - ea_blocks == 0); +} + /* The ext3 forget function must perform a revoke if we are freeing data * which has been journaled. Metadata (eg. indirect blocks) must be * revoked in all cases. @@ -51,7 +65,7 @@ * still needs to be revoked. */ -static int ext3_forget(handle_t *handle, int is_metadata, +int ext3_forget(handle_t *handle, int is_metadata, struct inode *inode, struct buffer_head *bh, int blocknr) { @@ -167,9 +181,7 @@ { handle_t *handle; - if (is_bad_inode(inode) || - inode->i_ino == EXT3_ACL_IDX_INO || - inode->i_ino == EXT3_ACL_DATA_INO) + if (is_bad_inode(inode)) goto no_delete; lock_kernel(); @@ -1979,6 +1991,8 @@ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) return; + if (ext3_inode_is_fast_symlink(inode)) + return; if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) return; @@ -2130,8 +2144,6 @@ struct ext3_group_desc * gdp; if ((inode->i_ino != EXT3_ROOT_INO && - inode->i_ino != EXT3_ACL_IDX_INO && - inode->i_ino != EXT3_ACL_DATA_INO && inode->i_ino != EXT3_JOURNAL_INO && inode->i_ino < EXT3_FIRST_INO(inode->i_sb)) || inode->i_ino > le32_to_cpu( @@ -2189,7 +2201,11 @@ struct buffer_head *bh; int block; - if(ext3_get_inode_loc(inode, &iloc)) +#ifdef CONFIG_EXT3_FS_POSIX_ACL + ei->i_acl = EXT3_ACL_NOT_CACHED; + ei->i_default_acl = EXT3_ACL_NOT_CACHED; +#endif + if (ext3_get_inode_loc(inode, &iloc)) goto bad_inode; bh = iloc.bh; raw_inode = iloc.raw_inode; @@ -2263,10 +2279,7 @@ brelse (iloc.bh); - if (inode->i_ino == EXT3_ACL_IDX_INO || - inode->i_ino == EXT3_ACL_DATA_INO) - /* Nothing to do */ ; - else if (S_ISREG(inode->i_mode)) { + if (S_ISREG(inode->i_mode)) { inode->i_op = &ext3_file_inode_operations; inode->i_fop = &ext3_file_operations; if (ext3_should_writeback_data(inode)) @@ -2277,18 +2290,20 @@ inode->i_op = &ext3_dir_inode_operations; inode->i_fop = &ext3_dir_operations; } else if (S_ISLNK(inode->i_mode)) { - if (!inode->i_blocks) + if (ext3_inode_is_fast_symlink(inode)) inode->i_op = &ext3_fast_symlink_inode_operations; else { - inode->i_op = &page_symlink_inode_operations; + inode->i_op = &ext3_symlink_inode_operations; if (ext3_should_writeback_data(inode)) inode->i_mapping->a_ops = &ext3_writeback_aops; else inode->i_mapping->a_ops = &ext3_aops; } - } else + } else { + inode->i_op = &ext3_special_inode_operations; init_special_inode(inode, inode->i_mode, le32_to_cpu(iloc.raw_inode->i_block[0])); + } if (ei->i_flags & EXT3_SYNC_FL) inode->i_flags |= S_SYNC; if (ei->i_flags & EXT3_APPEND_FL) @@ -2325,6 +2340,11 @@ if (err) goto out_brelse; } + /* For fields not not tracking in the in-memory inode, + * initialise them to zero for new inodes. */ + if (ei->i_state & EXT3_STATE_NEW) + memset(raw_inode, 0, EXT3_SB(inode->i_sb)->s_inode_size); + raw_inode->i_mode = cpu_to_le16(inode->i_mode); if(!(test_opt(inode->i_sb, NO_UID32))) { raw_inode->i_uid_low = cpu_to_le16(low_16_bits(inode->i_uid)); @@ -2362,15 +2382,6 @@ raw_inode->i_faddr = cpu_to_le32(ei->i_faddr); raw_inode->i_frag = ei->i_frag_no; raw_inode->i_fsize = ei->i_frag_size; -#else - /* If we are not tracking these fields in the in-memory inode, - * then preserve them on disk, but still initialise them to zero - * for new inodes. */ - if (ei->i_state & EXT3_STATE_NEW) { - raw_inode->i_faddr = 0; - raw_inode->i_frag = 0; - raw_inode->i_fsize = 0; - } #endif raw_inode->i_file_acl = cpu_to_le32(ei->i_file_acl); if (!S_ISREG(inode->i_mode)) { @@ -2486,13 +2497,8 @@ * be freed, so we have a strong guarantee that no future commit will * leave these blocks visible to the user.) * - * This is only needed for regular files. rmdir() has its own path, and - * we can never truncate a direcory except on final unlink (at which - * point i_nlink is zero so recovery is easy.) - * - * Called with the BKL. + * Called with inode->sem down. */ - int ext3_setattr(struct dentry *dentry, struct iattr *attr) { struct inode *inode = dentry->d_inode; @@ -2512,7 +2518,8 @@ lock_kernel(); - if (attr->ia_valid & ATTR_SIZE && attr->ia_size < inode->i_size) { + if (S_ISREG(inode->i_mode) && + attr->ia_valid & ATTR_SIZE && attr->ia_size < inode->i_size) { handle_t *handle; handle = ext3_journal_start(inode, 3); @@ -2536,6 +2543,9 @@ * orphan list manually. */ if (inode->i_nlink) ext3_orphan_del(NULL, inode); + + if (!rc && (ia_valid & ATTR_MODE)) + rc = ext3_acl_chmod(inode); err_out: ext3_std_error(inode->i_sb, error); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/ext3/namei.c linux-2.5-bk/fs/ext3/namei.c --- linux-2.5.45/fs/ext3/namei.c Wed Oct 30 16:43:00 2002 +++ linux-2.5-bk/fs/ext3/namei.c Thu Oct 31 22:12:58 2002 @@ -36,7 +36,8 @@ #include #include #include - +#include "xattr.h" +#include "acl.h" /* * define how far ahead to read directories while searching them. @@ -1623,7 +1624,10 @@ inode = ext3_new_inode (handle, dir, mode); err = PTR_ERR(inode); if (!IS_ERR(inode)) { - init_special_inode(inode, mode, rdev); + init_special_inode(inode, inode->i_mode, rdev); +#ifdef CONFIG_EXT3_FS_XATTR + inode->i_op = &ext3_special_inode_operations; +#endif err = ext3_add_nondir(handle, dentry, inode); ext3_mark_inode_dirty(handle, inode); } @@ -1654,7 +1658,7 @@ if (IS_DIRSYNC(dir)) handle->h_sync = 1; - inode = ext3_new_inode (handle, dir, S_IFDIR); + inode = ext3_new_inode (handle, dir, S_IFDIR | mode); err = PTR_ERR(inode); if (IS_ERR(inode)) goto out_stop; @@ -1662,7 +1666,6 @@ inode->i_op = &ext3_dir_inode_operations; inode->i_fop = &ext3_dir_operations; inode->i_size = EXT3_I(inode)->i_disksize = inode->i_sb->s_blocksize; - inode->i_blocks = 0; dir_block = ext3_bread (handle, inode, 0, 1, &err); if (!dir_block) { inode->i_nlink--; /* is this nlink == 0? */ @@ -1689,9 +1692,6 @@ BUFFER_TRACE(dir_block, "call ext3_journal_dirty_metadata"); ext3_journal_dirty_metadata(handle, dir_block); brelse (dir_block); - inode->i_mode = S_IFDIR | mode; - if (dir->i_mode & S_ISGID) - inode->i_mode |= S_ISGID; ext3_mark_inode_dirty(handle, inode); err = ext3_add_entry (handle, dentry, inode); if (err) { @@ -2068,7 +2068,7 @@ goto out_stop; if (l > sizeof (EXT3_I(inode)->i_data)) { - inode->i_op = &page_symlink_inode_operations; + inode->i_op = &ext3_symlink_inode_operations; if (ext3_should_writeback_data(inode)) inode->i_mapping->a_ops = &ext3_writeback_aops; else @@ -2284,4 +2284,21 @@ .rmdir = ext3_rmdir, .mknod = ext3_mknod, .rename = ext3_rename, + .setattr = ext3_setattr, + .setxattr = ext3_setxattr, + .getxattr = ext3_getxattr, + .listxattr = ext3_listxattr, + .removexattr = ext3_removexattr, + .permission = ext3_permission, }; + +struct inode_operations ext3_special_inode_operations = { + .setattr = ext3_setattr, + .setxattr = ext3_setxattr, + .getxattr = ext3_getxattr, + .listxattr = ext3_listxattr, + .removexattr = ext3_removexattr, + .permission = ext3_permission, +}; + + diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/ext3/super.c linux-2.5-bk/fs/ext3/super.c --- linux-2.5.45/fs/ext3/super.c Wed Oct 30 16:43:40 2002 +++ linux-2.5-bk/fs/ext3/super.c Thu Oct 31 22:12:58 2002 @@ -30,6 +30,8 @@ #include #include #include +#include "xattr.h" +#include "acl.h" #ifdef CONFIG_JBD_DEBUG static int ext3_ro_after; /* Make fs read-only after this many jiffies */ @@ -105,32 +107,6 @@ static char error_buf[1024]; -/* Determine the appropriate response to ext3_error on a given filesystem */ - -static int ext3_error_behaviour(struct super_block *sb) -{ - /* First check for mount-time options */ - if (test_opt (sb, ERRORS_PANIC)) - return EXT3_ERRORS_PANIC; - if (test_opt (sb, ERRORS_RO)) - return EXT3_ERRORS_RO; - if (test_opt (sb, ERRORS_CONT)) - return EXT3_ERRORS_CONTINUE; - - /* If no overrides were specified on the mount, then fall back - * to the default behaviour set in the filesystem's superblock - * on disk. */ - switch (le16_to_cpu(EXT3_SB(sb)->s_es->s_errors)) { - case EXT3_ERRORS_PANIC: - return EXT3_ERRORS_PANIC; - case EXT3_ERRORS_RO: - return EXT3_ERRORS_RO; - default: - break; - } - return EXT3_ERRORS_CONTINUE; -} - /* Deal with the reporting of failure conditions on a filesystem such as * inconsistencies detected or read IO failures. * @@ -156,20 +132,16 @@ if (sb->s_flags & MS_RDONLY) return; - if (ext3_error_behaviour(sb) != EXT3_ERRORS_CONTINUE) { - EXT3_SB(sb)->s_mount_opt |= EXT3_MOUNT_ABORT; - journal_abort(EXT3_SB(sb)->s_journal, -EIO); - } - - if (ext3_error_behaviour(sb) == EXT3_ERRORS_PANIC) + if (test_opt (sb, ERRORS_PANIC)) panic ("EXT3-fs (device %s): panic forced after error\n", sb->s_id); - - if (ext3_error_behaviour(sb) == EXT3_ERRORS_RO) { + if (test_opt (sb, ERRORS_RO)) { printk (KERN_CRIT "Remounting filesystem read-only\n"); sb->s_flags |= MS_RDONLY; + } else { + EXT3_SB(sb)->s_mount_opt |= EXT3_MOUNT_ABORT; + journal_abort(EXT3_SB(sb)->s_journal, -EIO); } - ext3_commit_super(sb, es, 1); } @@ -257,7 +229,7 @@ vsprintf (error_buf, fmt, args); va_end (args); - if (ext3_error_behaviour(sb) == EXT3_ERRORS_PANIC) + if (test_opt (sb, ERRORS_PANIC)) panic ("EXT3-fs panic (device %s): %s: %s\n", sb->s_id, function, error_buf); @@ -405,6 +377,7 @@ struct ext3_super_block *es = sbi->s_es; int i; + ext3_xattr_put_super(sb); journal_destroy(sbi->s_journal); if (!(sb->s_flags & MS_RDONLY)) { EXT3_CLEAR_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_RECOVER); @@ -456,6 +429,10 @@ ei = kmem_cache_alloc(ext3_inode_cachep, SLAB_NOFS); if (!ei) return NULL; +#ifdef CONFIG_EXT3_FS_POSIX_ACL + ei->i_acl = EXT3_ACL_NOT_CACHED; + ei->i_default_acl = EXT3_ACL_NOT_CACHED; +#endif return &ei->vfs_inode; } @@ -493,6 +470,26 @@ printk(KERN_INFO "ext3_inode_cache: not all structures were freed\n"); } +#ifdef CONFIG_EXT3_FS_POSIX_ACL + +static void ext3_clear_inode(struct inode *inode) +{ + if (EXT3_I(inode)->i_acl && + EXT3_I(inode)->i_acl != EXT3_ACL_NOT_CACHED) { + posix_acl_release(EXT3_I(inode)->i_acl); + EXT3_I(inode)->i_acl = EXT3_ACL_NOT_CACHED; + } + if (EXT3_I(inode)->i_default_acl && + EXT3_I(inode)->i_default_acl != EXT3_ACL_NOT_CACHED) { + posix_acl_release(EXT3_I(inode)->i_default_acl); + EXT3_I(inode)->i_default_acl = EXT3_ACL_NOT_CACHED; + } +} + +#else +# define ext3_clear_inode NULL +#endif + static struct super_operations ext3_sops = { .alloc_inode = ext3_alloc_inode, .destroy_inode = ext3_destroy_inode, @@ -507,6 +504,7 @@ .unlockfs = ext3_unlockfs, /* BKL not held. We take it */ .statfs = ext3_statfs, /* BKL not held. */ .remount_fs = ext3_remount, /* BKL held */ + .clear_inode = ext3_clear_inode, /* BKL not needed. */ }; struct dentry *ext3_get_parent(struct dentry *child); @@ -545,17 +543,32 @@ return 0; } +static unsigned long get_sb_block(void **data) +{ + unsigned long sb_block; + char *options = (char *) *data; + + if (!options || strncmp(options, "sb=", 3) != 0) + return 1; /* Default location */ + options += 3; + sb_block = simple_strtoul(options, &options, 0); + if (*options && *options != ',') { + printk("EXT3-fs: Invalid sb specification: %s\n", + (char *) *data); + return 1; + } + if (*options == ',') + options++; + *data = (void *) options; + return sb_block; +} + /* * This function has been shamelessly adapted from the msdos fs */ -static int parse_options (char * options, unsigned long * sb_block, - struct ext3_sb_info *sbi, - unsigned long * inum, - int is_remount) -{ - unsigned long *mount_options = &sbi->s_mount_opt; - uid_t *resuid = &sbi->s_resuid; - gid_t *resgid = &sbi->s_resgid; +static int parse_options (char * options, struct ext3_sb_info *sbi, + unsigned long * inum, int is_remount) +{ char * this_char; char * value; @@ -566,43 +579,57 @@ continue; if ((value = strchr (this_char, '=')) != NULL) *value++ = 0; +#ifdef CONFIG_EXT3_FS_XATTR + if (!strcmp (this_char, "user_xattr")) + set_opt (sbi->s_mount_opt, XATTR_USER); + else if (!strcmp (this_char, "nouser_xattr")) + clear_opt (sbi->s_mount_opt, XATTR_USER); + else +#endif +#ifdef CONFIG_EXT3_FS_POSIX_ACL + if (!strcmp(this_char, "acl")) + set_opt (sbi->s_mount_opt, POSIX_ACL); + else if (!strcmp(this_char, "noacl")) + clear_opt (sbi->s_mount_opt, POSIX_ACL); + else +#endif if (!strcmp (this_char, "bsddf")) - clear_opt (*mount_options, MINIX_DF); + clear_opt (sbi->s_mount_opt, MINIX_DF); else if (!strcmp (this_char, "nouid32")) { - set_opt (*mount_options, NO_UID32); + set_opt (sbi->s_mount_opt, NO_UID32); } else if (!strcmp (this_char, "abort")) - set_opt (*mount_options, ABORT); + set_opt (sbi->s_mount_opt, ABORT); else if (!strcmp (this_char, "check")) { if (!value || !*value || !strcmp (value, "none")) - clear_opt (*mount_options, CHECK); + clear_opt (sbi->s_mount_opt, CHECK); else #ifdef CONFIG_EXT3_CHECK - set_opt (*mount_options, CHECK); + set_opt (sbi->s_mount_opt, CHECK); #else printk(KERN_ERR "EXT3 Check option not supported\n"); #endif } else if (!strcmp (this_char, "debug")) - set_opt (*mount_options, DEBUG); + set_opt (sbi->s_mount_opt, DEBUG); else if (!strcmp (this_char, "errors")) { if (want_value(value, "errors")) return 0; if (!strcmp (value, "continue")) { - clear_opt (*mount_options, ERRORS_RO); - clear_opt (*mount_options, ERRORS_PANIC); - set_opt (*mount_options, ERRORS_CONT); + clear_opt (sbi->s_mount_opt, ERRORS_RO); + clear_opt (sbi->s_mount_opt, ERRORS_PANIC); + set_opt (sbi->s_mount_opt, ERRORS_CONT); } else if (!strcmp (value, "remount-ro")) { - clear_opt (*mount_options, ERRORS_CONT); - clear_opt (*mount_options, ERRORS_PANIC); - set_opt (*mount_options, ERRORS_RO); + clear_opt (sbi->s_mount_opt, ERRORS_CONT); + clear_opt (sbi->s_mount_opt, ERRORS_PANIC); + set_opt (sbi->s_mount_opt, ERRORS_RO); } else if (!strcmp (value, "panic")) { - clear_opt (*mount_options, ERRORS_CONT); - clear_opt (*mount_options, ERRORS_RO); - set_opt (*mount_options, ERRORS_PANIC); + clear_opt (sbi->s_mount_opt, ERRORS_CONT); + clear_opt (sbi->s_mount_opt, ERRORS_RO); + set_opt (sbi->s_mount_opt, ERRORS_PANIC); } else { printk (KERN_ERR @@ -613,29 +640,25 @@ } else if (!strcmp (this_char, "grpid") || !strcmp (this_char, "bsdgroups")) - set_opt (*mount_options, GRPID); + set_opt (sbi->s_mount_opt, GRPID); else if (!strcmp (this_char, "minixdf")) - set_opt (*mount_options, MINIX_DF); + set_opt (sbi->s_mount_opt, MINIX_DF); else if (!strcmp (this_char, "nocheck")) - clear_opt (*mount_options, CHECK); + clear_opt (sbi->s_mount_opt, CHECK); else if (!strcmp (this_char, "nogrpid") || !strcmp (this_char, "sysvgroups")) - clear_opt (*mount_options, GRPID); + clear_opt (sbi->s_mount_opt, GRPID); else if (!strcmp (this_char, "resgid")) { unsigned long v; if (want_numeric(value, "resgid", &v)) return 0; - *resgid = v; + sbi->s_resgid = v; } else if (!strcmp (this_char, "resuid")) { unsigned long v; if (want_numeric(value, "resuid", &v)) return 0; - *resuid = v; - } - else if (!strcmp (this_char, "sb")) { - if (want_numeric(value, "sb", sb_block)) - return 0; + sbi->s_resuid = v; } #ifdef CONFIG_JBD_DEBUG else if (!strcmp (this_char, "ro-after")) { @@ -666,12 +689,12 @@ if (want_value(value, "journal")) return 0; if (!strcmp (value, "update")) - set_opt (*mount_options, UPDATE_JOURNAL); + set_opt (sbi->s_mount_opt, UPDATE_JOURNAL); else if (want_numeric(value, "journal", inum)) return 0; } else if (!strcmp (this_char, "noload")) - set_opt (*mount_options, NOLOAD); + set_opt (sbi->s_mount_opt, NOLOAD); else if (!strcmp (this_char, "data")) { int data_opt = 0; @@ -690,7 +713,7 @@ return 0; } if (is_remount) { - if ((*mount_options & EXT3_MOUNT_DATA_FLAGS) != + if ((sbi->s_mount_opt & EXT3_MOUNT_DATA_FLAGS) != data_opt) { printk(KERN_ERR "EXT3-fs: cannot change data " @@ -698,8 +721,8 @@ return 0; } } else { - *mount_options &= ~EXT3_MOUNT_DATA_FLAGS; - *mount_options |= data_opt; + sbi->s_mount_opt &= ~EXT3_MOUNT_DATA_FLAGS; + sbi->s_mount_opt |= data_opt; } } else if (!strcmp (this_char, "commit")) { unsigned long v; @@ -948,16 +971,34 @@ return res; } +static unsigned long descriptor_loc(struct super_block *sb, + unsigned long logic_sb_block, + int nr) +{ + struct ext3_sb_info *sbi = EXT3_SB(sb); + unsigned long bg, first_data_block, first_meta_bg; + + first_data_block = le32_to_cpu(sbi->s_es->s_first_data_block); + first_meta_bg = le32_to_cpu(sbi->s_es->s_first_meta_bg); + + if (!EXT3_HAS_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_META_BG) || + nr < first_meta_bg) + return (logic_sb_block + nr + 1); + bg = sbi->s_desc_per_block * nr; + return (first_data_block + 1 + (bg * sbi->s_blocks_per_group)); +} + static int ext3_fill_super (struct super_block *sb, void *data, int silent) { struct buffer_head * bh; struct ext3_super_block *es = 0; struct ext3_sb_info *sbi; - unsigned long sb_block = 1; - unsigned long logic_sb_block = 1; + unsigned long sb_block = get_sb_block(&data); + unsigned long block, logic_sb_block = 1; unsigned long offset = 0; unsigned long journal_inum = 0; + unsigned long def_mount_opts; int blocksize; int hblock; int db_count; @@ -967,13 +1008,6 @@ #ifdef CONFIG_JBD_DEBUG ext3_ro_after = 0; #endif - /* - * See what the current blocksize for the device is, and - * use that as the blocksize. Otherwise (or if the blocksize - * is smaller than the default) use the default. - * This is important for devices that have a hardware - * sectorsize that is larger than the default. - */ sbi = kmalloc(sizeof(*sbi), GFP_KERNEL); if (!sbi) return -ENOMEM; @@ -982,8 +1016,6 @@ sbi->s_mount_opt = 0; sbi->s_resuid = EXT3_DEF_RESUID; sbi->s_resgid = EXT3_DEF_RESGID; - if (!parse_options ((char *) data, &sb_block, sbi, &journal_inum, 0)) - goto out_fail; blocksize = sb_min_blocksize(sb, EXT3_MIN_BLOCK_SIZE); if (!blocksize) { @@ -1018,6 +1050,40 @@ sb->s_id); goto failed_mount; } + + /* Set defaults before we parse the mount options */ + def_mount_opts = le32_to_cpu(es->s_default_mount_opts); + if (def_mount_opts & EXT3_DEFM_DEBUG) + set_opt(sbi->s_mount_opt, DEBUG); + if (def_mount_opts & EXT3_DEFM_BSDGROUPS) + set_opt(sbi->s_mount_opt, GRPID); + if (def_mount_opts & EXT3_DEFM_UID16) + set_opt(sbi->s_mount_opt, NO_UID32); + if (def_mount_opts & EXT3_DEFM_XATTR_USER) + set_opt(sbi->s_mount_opt, XATTR_USER); + if (def_mount_opts & EXT3_DEFM_ACL) + set_opt(sbi->s_mount_opt, POSIX_ACL); + if ((def_mount_opts & EXT3_DEFM_JMODE) == EXT3_DEFM_JMODE_DATA) + sbi->s_mount_opt |= EXT3_MOUNT_JOURNAL_DATA; + else if ((def_mount_opts & EXT3_DEFM_JMODE) == EXT3_DEFM_JMODE_ORDERED) + sbi->s_mount_opt |= EXT3_MOUNT_ORDERED_DATA; + else if ((def_mount_opts & EXT3_DEFM_JMODE) == EXT3_DEFM_JMODE_WBACK) + sbi->s_mount_opt |= EXT3_MOUNT_WRITEBACK_DATA; + + if (le16_to_cpu(sbi->s_es->s_errors) == EXT3_ERRORS_PANIC) + set_opt(sbi->s_mount_opt, ERRORS_PANIC); + else if (le16_to_cpu(sbi->s_es->s_errors) == EXT3_ERRORS_RO) + set_opt(sbi->s_mount_opt, ERRORS_RO); + + sbi->s_resuid = le16_to_cpu(es->s_def_resuid); + sbi->s_resgid = le16_to_cpu(es->s_def_resgid); + + if (!parse_options ((char *) data, sbi, &journal_inum, 0)) + goto failed_mount; + + sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | + ((sbi->s_mount_opt & EXT3_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0); + if (le32_to_cpu(es->s_rev_level) == EXT3_GOOD_OLD_REV && (EXT3_HAS_COMPAT_FEATURE(sb, ~0U) || EXT3_HAS_RO_COMPAT_FEATURE(sb, ~0U) || @@ -1092,7 +1158,9 @@ } else { sbi->s_inode_size = le16_to_cpu(es->s_inode_size); sbi->s_first_ino = le32_to_cpu(es->s_first_ino); - if (sbi->s_inode_size != EXT3_GOOD_OLD_INODE_SIZE) { + if ((sbi->s_inode_size < EXT3_GOOD_OLD_INODE_SIZE) || + (sbi->s_inode_size & (sbi->s_inode_size - 1)) || + (sbi->s_inode_size > blocksize)) { printk (KERN_ERR "EXT3-fs: unsupported inode size: %d\n", sbi->s_inode_size); @@ -1115,10 +1183,6 @@ sbi->s_itb_per_group = sbi->s_inodes_per_group /sbi->s_inodes_per_block; sbi->s_desc_per_block = blocksize / sizeof(struct ext3_group_desc); sbi->s_sbh = bh; - if (sbi->s_resuid == EXT3_DEF_RESUID) - sbi->s_resuid = le16_to_cpu(es->s_def_resuid); - if (sbi->s_resgid == EXT3_DEF_RESGID) - sbi->s_resgid = le16_to_cpu(es->s_def_resgid); sbi->s_mount_state = le16_to_cpu(es->s_state); sbi->s_addr_per_block_bits = log2(EXT3_ADDR_PER_BLOCK(sb)); sbi->s_desc_per_block_bits = log2(EXT3_DESC_PER_BLOCK(sb)); @@ -1158,7 +1222,8 @@ goto failed_mount; } for (i = 0; i < db_count; i++) { - sbi->s_group_desc[i] = sb_bread(sb, logic_sb_block + i + 1); + block = descriptor_loc(sb, logic_sb_block, i); + sbi->s_group_desc[i] = sb_bread(sb, block); if (!sbi->s_group_desc[i]) { printk (KERN_ERR "EXT3-fs: " "can't read group descriptor %d\n", i); @@ -1702,12 +1767,15 @@ /* * Allow the "check" option to be passed as a remount option. */ - if (!parse_options(data, &tmp, sbi, &tmp, 1)) + if (!parse_options(data, sbi, &tmp, 1)) return -EINVAL; if (sbi->s_mount_opt & EXT3_MOUNT_ABORT) ext3_abort(sb, __FUNCTION__, "Abort forced by user"); + sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | + ((sbi->s_mount_opt & EXT3_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0); + es = sbi->s_es; ext3_init_journal_params(sbi, sbi->s_journal); @@ -1824,7 +1892,10 @@ static int __init init_ext3_fs(void) { - int err = init_inodecache(); + int err = init_ext3_xattr(); + if (err) + return err; + err = init_inodecache(); if (err) goto out1; err = register_filesystem(&ext3_fs_type); @@ -1834,6 +1905,7 @@ out: destroy_inodecache(); out1: + exit_ext3_xattr(); return err; } @@ -1841,6 +1913,7 @@ { unregister_filesystem(&ext3_fs_type); destroy_inodecache(); + exit_ext3_xattr(); } MODULE_AUTHOR("Remy Card, Stephen Tweedie, Andrew Morton, Andreas Dilger, Theodore Ts'o and others"); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/ext3/symlink.c linux-2.5-bk/fs/ext3/symlink.c --- linux-2.5.45/fs/ext3/symlink.c Wed Oct 30 16:42:57 2002 +++ linux-2.5-bk/fs/ext3/symlink.c Thu Oct 31 22:12:58 2002 @@ -20,6 +20,7 @@ #include #include #include +#include "xattr.h" static int ext3_readlink(struct dentry *dentry, char *buffer, int buflen) { @@ -33,7 +34,20 @@ return vfs_follow_link(nd, (char*)ei->i_data); } +struct inode_operations ext3_symlink_inode_operations = { + .readlink = page_readlink, + .follow_link = page_follow_link, + .setxattr = ext3_setxattr, + .getxattr = ext3_getxattr, + .listxattr = ext3_listxattr, + .removexattr = ext3_removexattr, +}; + struct inode_operations ext3_fast_symlink_inode_operations = { - .readlink = ext3_readlink, /* BKL not held. Don't need */ + .readlink = ext3_readlink, /* BKL not held. Don't need */ .follow_link = ext3_follow_link, /* BKL not held. Don't need */ + .setxattr = ext3_setxattr, + .getxattr = ext3_getxattr, + .listxattr = ext3_listxattr, + .removexattr = ext3_removexattr, }; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/ext3/xattr.c linux-2.5-bk/fs/ext3/xattr.c --- linux-2.5.45/fs/ext3/xattr.c Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/fs/ext3/xattr.c Thu Oct 31 22:12:58 2002 @@ -0,0 +1,1142 @@ +/* + * linux/fs/ext3/xattr.c + * + * Copyright (C) 2001 by Andreas Gruenbacher, + * + * Fix by Harrison Xing . + * Ext3 code with a lot of help from Eric Jarman . + * Extended attributes for symlinks and special files added per + * suggestion of Luka Renko . + */ + +/* + * Extended attributes are stored on disk blocks allocated outside of + * any inode. The i_file_acl field is then made to point to this allocated + * block. If all extended attributes of an inode are identical, these + * inodes may share the same extended attribute block. Such situations + * are automatically detected by keeping a cache of recent attribute block + * numbers and hashes over the block's contents in memory. + * + * + * Extended attribute block layout: + * + * +------------------+ + * | header | + * ¦ entry 1 | | + * | entry 2 | | growing downwards + * | entry 3 | v + * | four null bytes | + * | . . . | + * | value 1 | ^ + * | value 3 | | growing upwards + * | value 2 | | + * +------------------+ + * + * The block header is followed by multiple entry descriptors. These entry + * descriptors are variable in size, and alligned to EXT3_XATTR_PAD + * byte boundaries. The entry descriptors are sorted by attribute name, + * so that two extended attribute blocks can be compared efficiently. + * + * Attribute values are aligned to the end of the block, stored in + * no specific order. They are also padded to EXT3_XATTR_PAD byte + * boundaries. No additional gaps are left between them. + * + * Locking strategy + * ---------------- + * The VFS holdsinode->i_sem semaphore when any of the xattr inode + * operations are called, so we are guaranteed that only one + * processes accesses extended attributes of an inode at any time. + * + * For writing we also grab the ext3_xattr_sem semaphore. This ensures that + * only a single process is modifying an extended attribute block, even + * if the block is shared among inodes. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "xattr.h" +#include "acl.h" + +#define EXT3_EA_USER "user." + +#define HDR(bh) ((struct ext3_xattr_header *)((bh)->b_data)) +#define ENTRY(ptr) ((struct ext3_xattr_entry *)(ptr)) +#define FIRST_ENTRY(bh) ENTRY(HDR(bh)+1) +#define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0) + +#ifdef EXT3_XATTR_DEBUG +# define ea_idebug(inode, f...) do { \ + printk(KERN_DEBUG "inode %s:%ld: ", \ + kdevname(inode->i_dev), inode->i_ino); \ + printk(f); \ + printk("\n"); \ + } while (0) +# define ea_bdebug(bh, f...) do { \ + printk(KERN_DEBUG "block %s:%ld: ", \ + kdevname(bh->b_dev), bh->b_blocknr); \ + printk(f); \ + printk("\n"); \ + } while (0) +#else +# define ea_idebug(f...) +# define ea_bdebug(f...) +#endif + +static int ext3_xattr_set2(handle_t *, struct inode *, struct buffer_head *, + struct ext3_xattr_header *); + +static int ext3_xattr_cache_insert(struct buffer_head *); +static struct buffer_head *ext3_xattr_cache_find(struct inode *, + struct ext3_xattr_header *); +static void ext3_xattr_cache_remove(struct buffer_head *); +static void ext3_xattr_rehash(struct ext3_xattr_header *, + struct ext3_xattr_entry *); + +static struct mb_cache *ext3_xattr_cache; + +/* + * If a file system does not share extended attributes among inodes, + * we should not need the ext3_xattr_sem semaphore. However, the + * filesystem may still contain shared blocks, so we always take + * the lock. + */ + +static DECLARE_MUTEX(ext3_xattr_sem); +static struct ext3_xattr_handler *ext3_xattr_handlers[EXT3_XATTR_INDEX_MAX]; +static rwlock_t ext3_handler_lock = RW_LOCK_UNLOCKED; + +int +ext3_xattr_register(int name_index, struct ext3_xattr_handler *handler) +{ + int error = -EINVAL; + + if (name_index > 0 && name_index <= EXT3_XATTR_INDEX_MAX) { + write_lock(&ext3_handler_lock); + if (!ext3_xattr_handlers[name_index-1]) { + ext3_xattr_handlers[name_index-1] = handler; + error = 0; + } + write_unlock(&ext3_handler_lock); + } + return error; +} + +void +ext3_xattr_unregister(int name_index, struct ext3_xattr_handler *handler) +{ + if (name_index > 0 || name_index <= EXT3_XATTR_INDEX_MAX) { + write_lock(&ext3_handler_lock); + ext3_xattr_handlers[name_index-1] = NULL; + write_unlock(&ext3_handler_lock); + } +} + +static inline const char * +strcmp_prefix(const char *a, const char *a_prefix) +{ + while (*a_prefix && *a == *a_prefix) { + a++; + a_prefix++; + } + return *a_prefix ? NULL : a; +} + +/* + * Decode the extended attribute name, and translate it into + * the name_index and name suffix. + */ +static inline struct ext3_xattr_handler * +ext3_xattr_resolve_name(const char **name) +{ + struct ext3_xattr_handler *handler = NULL; + int i; + + if (!*name) + return NULL; + read_lock(&ext3_handler_lock); + for (i=0; iprefix); + if (n) { + handler = ext3_xattr_handlers[i]; + *name = n; + break; + } + } + } + read_unlock(&ext3_handler_lock); + return handler; +} + +static inline struct ext3_xattr_handler * +ext3_xattr_handler(int name_index) +{ + struct ext3_xattr_handler *handler = NULL; + if (name_index > 0 && name_index <= EXT3_XATTR_INDEX_MAX) { + read_lock(&ext3_handler_lock); + handler = ext3_xattr_handlers[name_index-1]; + read_unlock(&ext3_handler_lock); + } + return handler; +} + +/* + * Inode operation getxattr() + * + * dentry->d_inode->i_sem down + */ +ssize_t +ext3_getxattr(struct dentry *dentry, const char *name, + void *buffer, size_t size) +{ + struct ext3_xattr_handler *handler; + struct inode *inode = dentry->d_inode; + + handler = ext3_xattr_resolve_name(&name); + if (!handler) + return -EOPNOTSUPP; + return handler->get(inode, name, buffer, size); +} + +/* + * Inode operation listxattr() + * + * dentry->d_inode->i_sem down + */ +ssize_t +ext3_listxattr(struct dentry *dentry, char *buffer, size_t size) +{ + return ext3_xattr_list(dentry->d_inode, buffer, size); +} + +/* + * Inode operation setxattr() + * + * dentry->d_inode->i_sem down + */ +int +ext3_setxattr(struct dentry *dentry, const char *name, + void *value, size_t size, int flags) +{ + struct ext3_xattr_handler *handler; + struct inode *inode = dentry->d_inode; + + if (size == 0) + value = ""; /* empty EA, do not remove */ + handler = ext3_xattr_resolve_name(&name); + if (!handler) + return -EOPNOTSUPP; + return handler->set(inode, name, value, size, flags); +} + +/* + * Inode operation removexattr() + * + * dentry->d_inode->i_sem down + */ +int +ext3_removexattr(struct dentry *dentry, const char *name) +{ + struct ext3_xattr_handler *handler; + struct inode *inode = dentry->d_inode; + + handler = ext3_xattr_resolve_name(&name); + if (!handler) + return -EOPNOTSUPP; + return handler->set(inode, name, NULL, 0, XATTR_REPLACE); +} + +/* + * ext3_xattr_get() + * + * Copy an extended attribute into the buffer + * provided, or compute the buffer size required. + * Buffer is NULL to compute the size of the buffer required. + * + * Returns a negative error number on failure, or the number of bytes + * used / required on success. + */ +int +ext3_xattr_get(struct inode *inode, int name_index, const char *name, + void *buffer, size_t buffer_size) +{ + struct buffer_head *bh = NULL; + struct ext3_xattr_entry *entry; + unsigned int size; + char *end; + int name_len, error; + + ea_idebug(inode, "name=%d.%s, buffer=%p, buffer_size=%ld", + name_index, name, buffer, (long)buffer_size); + + if (name == NULL) + return -EINVAL; + if (!EXT3_I(inode)->i_file_acl) + return -ENODATA; + ea_idebug(inode, "reading block %d", EXT3_I(inode)->i_file_acl); + bh = sb_bread(inode->i_sb, EXT3_I(inode)->i_file_acl); + if (!bh) + return -EIO; + ea_bdebug(bh, "b_count=%d, refcount=%d", + atomic_read(&(bh->b_count)), le32_to_cpu(HDR(bh)->h_refcount)); + end = bh->b_data + bh->b_size; + if (HDR(bh)->h_magic != cpu_to_le32(EXT3_XATTR_MAGIC) || + HDR(bh)->h_blocks != cpu_to_le32(1)) { +bad_block: ext3_error(inode->i_sb, "ext3_xattr_get", + "inode %ld: bad block %d", inode->i_ino, + EXT3_I(inode)->i_file_acl); + error = -EIO; + goto cleanup; + } + /* find named attribute */ + name_len = strlen(name); + + error = -ERANGE; + if (name_len > 255) + goto cleanup; + entry = FIRST_ENTRY(bh); + while (!IS_LAST_ENTRY(entry)) { + struct ext3_xattr_entry *next = + EXT3_XATTR_NEXT(entry); + if ((char *)next >= end) + goto bad_block; + if (name_index == entry->e_name_index && + name_len == entry->e_name_len && + memcmp(name, entry->e_name, name_len) == 0) + goto found; + entry = next; + } + /* Check the remaining name entries */ + while (!IS_LAST_ENTRY(entry)) { + struct ext3_xattr_entry *next = + EXT3_XATTR_NEXT(entry); + if ((char *)next >= end) + goto bad_block; + entry = next; + } + if (ext3_xattr_cache_insert(bh)) + ea_idebug(inode, "cache insert failed"); + error = -ENODATA; + goto cleanup; +found: + /* check the buffer size */ + if (entry->e_value_block != 0) + goto bad_block; + size = le32_to_cpu(entry->e_value_size); + if (size > inode->i_sb->s_blocksize || + le16_to_cpu(entry->e_value_offs) + size > inode->i_sb->s_blocksize) + goto bad_block; + + if (ext3_xattr_cache_insert(bh)) + ea_idebug(inode, "cache insert failed"); + if (buffer) { + error = -ERANGE; + if (size > buffer_size) + goto cleanup; + /* return value of attribute */ + memcpy(buffer, bh->b_data + le16_to_cpu(entry->e_value_offs), + size); + } + error = size; + +cleanup: + brelse(bh); + + return error; +} + +/* + * ext3_xattr_list() + * + * Copy a list of attribute names into the buffer + * provided, or compute the buffer size required. + * Buffer is NULL to compute the size of the buffer required. + * + * Returns a negative error number on failure, or the number of bytes + * used / required on success. + */ +int +ext3_xattr_list(struct inode *inode, char *buffer, size_t buffer_size) +{ + struct buffer_head *bh = NULL; + struct ext3_xattr_entry *entry; + unsigned int size = 0; + char *buf, *end; + int error; + + ea_idebug(inode, "buffer=%p, buffer_size=%ld", + buffer, (long)buffer_size); + + if (!EXT3_I(inode)->i_file_acl) + return 0; + ea_idebug(inode, "reading block %d", EXT3_I(inode)->i_file_acl); + bh = sb_bread(inode->i_sb, EXT3_I(inode)->i_file_acl); + if (!bh) + return -EIO; + ea_bdebug(bh, "b_count=%d, refcount=%d", + atomic_read(&(bh->b_count)), le32_to_cpu(HDR(bh)->h_refcount)); + end = bh->b_data + bh->b_size; + if (HDR(bh)->h_magic != cpu_to_le32(EXT3_XATTR_MAGIC) || + HDR(bh)->h_blocks != cpu_to_le32(1)) { +bad_block: ext3_error(inode->i_sb, "ext3_xattr_list", + "inode %ld: bad block %d", inode->i_ino, + EXT3_I(inode)->i_file_acl); + error = -EIO; + goto cleanup; + } + /* compute the size required for the list of attribute names */ + for (entry = FIRST_ENTRY(bh); !IS_LAST_ENTRY(entry); + entry = EXT3_XATTR_NEXT(entry)) { + struct ext3_xattr_handler *handler; + struct ext3_xattr_entry *next = + EXT3_XATTR_NEXT(entry); + if ((char *)next >= end) + goto bad_block; + + handler = ext3_xattr_handler(entry->e_name_index); + if (handler) { + size += handler->list(NULL, inode, entry->e_name, + entry->e_name_len) + 1; + } + } + + if (ext3_xattr_cache_insert(bh)) + ea_idebug(inode, "cache insert failed"); + if (!buffer) { + error = size; + goto cleanup; + } else { + error = -ERANGE; + if (size > buffer_size) + goto cleanup; + } + + /* list the attribute names */ + buf = buffer; + for (entry = FIRST_ENTRY(bh); !IS_LAST_ENTRY(entry); + entry = EXT3_XATTR_NEXT(entry)) { + struct ext3_xattr_handler *handler; + + handler = ext3_xattr_handler(entry->e_name_index); + if (handler) { + buf += handler->list(buf, inode, entry->e_name, + entry->e_name_len); + *buf++ = '\0'; + } + } + error = size; + +cleanup: + brelse(bh); + + return error; +} + +/* + * If the EXT3_FEATURE_COMPAT_EXT_ATTR feature of this file system is + * not set, set it. + */ +static void ext3_xattr_update_super_block(handle_t *handle, + struct super_block *sb) +{ + if (EXT3_HAS_COMPAT_FEATURE(sb, EXT3_FEATURE_COMPAT_EXT_ATTR)) + return; + + lock_super(sb); + ext3_journal_get_write_access(handle, EXT3_SB(sb)->s_sbh); + EXT3_SB(sb)->s_es->s_feature_compat |= + cpu_to_le32(EXT3_FEATURE_COMPAT_EXT_ATTR); + sb->s_dirt = 1; + ext3_journal_dirty_metadata(handle, EXT3_SB(sb)->s_sbh); + unlock_super(sb); +} + +/* + * ext3_xattr_set() + * + * Create, replace or remove an extended attribute for this inode. Buffer + * is NULL to remove an existing extended attribute, and non-NULL to + * either replace an existing extended attribute, or create a new extended + * attribute. The flags XATTR_REPLACE and XATTR_CREATE + * specify that an extended attribute must exist and must not exist + * previous to the call, respectively. + * + * Returns 0, or a negative error number on failure. + */ +int +ext3_xattr_set(handle_t *handle, struct inode *inode, int name_index, + const char *name, const void *value, size_t value_len, int flags) +{ + struct super_block *sb = inode->i_sb; + struct buffer_head *bh = NULL; + struct ext3_xattr_header *header = NULL; + struct ext3_xattr_entry *here, *last; + unsigned int name_len; + int min_offs = sb->s_blocksize, not_found = 1, free, error; + char *end; + + /* + * header -- Points either into bh, or to a temporarily + * allocated buffer. + * here -- The named entry found, or the place for inserting, within + * the block pointed to by header. + * last -- Points right after the last named entry within the block + * pointed to by header. + * min_offs -- The offset of the first value (values are aligned + * towards the end of the block). + * end -- Points right after the block pointed to by header. + */ + + ea_idebug(inode, "name=%d.%s, value=%p, value_len=%ld", + name_index, name, value, (long)value_len); + + if (IS_RDONLY(inode)) + return -EROFS; + if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) + return -EPERM; + if (value == NULL) + value_len = 0; + if (name == NULL) + return -EINVAL; + name_len = strlen(name); + if (name_len > 255 || value_len > sb->s_blocksize) + return -ERANGE; + down(&ext3_xattr_sem); + + if (EXT3_I(inode)->i_file_acl) { + /* The inode already has an extended attribute block. */ + bh = sb_bread(sb, EXT3_I(inode)->i_file_acl); + error = -EIO; + if (!bh) + goto cleanup; + ea_bdebug(bh, "b_count=%d, refcount=%d", + atomic_read(&(bh->b_count)), + le32_to_cpu(HDR(bh)->h_refcount)); + header = HDR(bh); + end = bh->b_data + bh->b_size; + if (header->h_magic != cpu_to_le32(EXT3_XATTR_MAGIC) || + header->h_blocks != cpu_to_le32(1)) { +bad_block: ext3_error(sb, "ext3_xattr_set", + "inode %ld: bad block %d", inode->i_ino, + EXT3_I(inode)->i_file_acl); + error = -EIO; + goto cleanup; + } + /* Find the named attribute. */ + here = FIRST_ENTRY(bh); + while (!IS_LAST_ENTRY(here)) { + struct ext3_xattr_entry *next = EXT3_XATTR_NEXT(here); + if ((char *)next >= end) + goto bad_block; + if (!here->e_value_block && here->e_value_size) { + int offs = le16_to_cpu(here->e_value_offs); + if (offs < min_offs) + min_offs = offs; + } + not_found = name_index - here->e_name_index; + if (!not_found) + not_found = name_len - here->e_name_len; + if (!not_found) + not_found = memcmp(name, here->e_name,name_len); + if (not_found <= 0) + break; + here = next; + } + last = here; + /* We still need to compute min_offs and last. */ + while (!IS_LAST_ENTRY(last)) { + struct ext3_xattr_entry *next = EXT3_XATTR_NEXT(last); + if ((char *)next >= end) + goto bad_block; + if (!last->e_value_block && last->e_value_size) { + int offs = le16_to_cpu(last->e_value_offs); + if (offs < min_offs) + min_offs = offs; + } + last = next; + } + + /* Check whether we have enough space left. */ + free = min_offs - ((char*)last - (char*)header) - sizeof(__u32); + } else { + /* We will use a new extended attribute block. */ + free = sb->s_blocksize - + sizeof(struct ext3_xattr_header) - sizeof(__u32); + here = last = NULL; /* avoid gcc uninitialized warning. */ + } + + if (not_found) { + /* Request to remove a nonexistent attribute? */ + error = -ENODATA; + if (flags & XATTR_REPLACE) + goto cleanup; + error = 0; + if (value == NULL) + goto cleanup; + else + free -= EXT3_XATTR_LEN(name_len); + } else { + /* Request to create an existing attribute? */ + error = -EEXIST; + if (flags & XATTR_CREATE) + goto cleanup; + if (!here->e_value_block && here->e_value_size) { + unsigned int size = le32_to_cpu(here->e_value_size); + + if (le16_to_cpu(here->e_value_offs) + size > + sb->s_blocksize || size > sb->s_blocksize) + goto bad_block; + free += EXT3_XATTR_SIZE(size); + } + } + free -= EXT3_XATTR_SIZE(value_len); + error = -ENOSPC; + if (free < 0) + goto cleanup; + + /* Here we know that we can set the new attribute. */ + + if (header) { + if (header->h_refcount == cpu_to_le32(1)) { + ea_bdebug(bh, "modifying in-place"); + ext3_xattr_cache_remove(bh); + error = ext3_journal_get_write_access(handle, bh); + if (error) + goto cleanup; + } else { + int offset; + + ea_bdebug(bh, "cloning"); + header = kmalloc(bh->b_size, GFP_KERNEL); + error = -ENOMEM; + if (header == NULL) + goto cleanup; + memcpy(header, HDR(bh), bh->b_size); + header->h_refcount = cpu_to_le32(1); + offset = (char *)header - bh->b_data; + here = ENTRY((char *)here + offset); + last = ENTRY((char *)last + offset); + } + } else { + /* Allocate a buffer where we construct the new block. */ + header = kmalloc(sb->s_blocksize, GFP_KERNEL); + error = -ENOMEM; + if (header == NULL) + goto cleanup; + memset(header, 0, sb->s_blocksize); + end = (char *)header + sb->s_blocksize; + header->h_magic = cpu_to_le32(EXT3_XATTR_MAGIC); + header->h_blocks = header->h_refcount = cpu_to_le32(1); + last = here = ENTRY(header+1); + } + + if (not_found) { + /* Insert the new name. */ + int size = EXT3_XATTR_LEN(name_len); + int rest = (char *)last - (char *)here; + memmove((char *)here + size, here, rest); + memset(here, 0, size); + here->e_name_index = name_index; + here->e_name_len = name_len; + memcpy(here->e_name, name, name_len); + } else { + /* Remove the old value. */ + if (!here->e_value_block && here->e_value_size) { + char *first_val = (char *)header + min_offs; + int offs = le16_to_cpu(here->e_value_offs); + char *val = (char *)header + offs; + size_t size = EXT3_XATTR_SIZE( + le32_to_cpu(here->e_value_size)); + memmove(first_val + size, first_val, val - first_val); + memset(first_val, 0, size); + here->e_value_offs = 0; + min_offs += size; + + /* Adjust all value offsets. */ + last = ENTRY(header+1); + while (!IS_LAST_ENTRY(last)) { + int o = le16_to_cpu(last->e_value_offs); + if (!last->e_value_block && o < offs) + last->e_value_offs = + cpu_to_le16(o + size); + last = EXT3_XATTR_NEXT(last); + } + } + if (value == NULL) { + /* Remove this attribute. */ + if (EXT3_XATTR_NEXT(ENTRY(header+1)) == last) { + /* This block is now empty. */ + error = ext3_xattr_set2(handle, inode, bh,NULL); + goto cleanup; + } else { + /* Remove the old name. */ + int size = EXT3_XATTR_LEN(name_len); + last = ENTRY((char *)last - size); + memmove(here, (char*)here + size, + (char*)last - (char*)here); + memset(last, 0, size); + } + } + } + + if (value != NULL) { + /* Insert the new value. */ + here->e_value_size = cpu_to_le32(value_len); + if (value_len) { + size_t size = EXT3_XATTR_SIZE(value_len); + char *val = (char *)header + min_offs - size; + here->e_value_offs = + cpu_to_le16((char *)val - (char *)header); + memset(val + size - EXT3_XATTR_PAD, 0, + EXT3_XATTR_PAD); /* Clear the pad bytes. */ + memcpy(val, value, value_len); + } + } + ext3_xattr_rehash(header, here); + + error = ext3_xattr_set2(handle, inode, bh, header); + +cleanup: + brelse(bh); + if (!(bh && header == HDR(bh))) + kfree(header); + up(&ext3_xattr_sem); + + return error; +} + +/* + * Second half of ext3_xattr_set(): Update the file system. + */ +static int +ext3_xattr_set2(handle_t *handle, struct inode *inode, + struct buffer_head *old_bh, struct ext3_xattr_header *header) +{ + struct super_block *sb = inode->i_sb; + struct buffer_head *new_bh = NULL; + int error; + + if (header) { + new_bh = ext3_xattr_cache_find(inode, header); + if (new_bh) { + /* + * We found an identical block in the cache. + * The old block will be released after updating + * the inode. + */ + ea_bdebug(old_bh, "reusing block %ld", + new_bh->b_blocknr); + + error = -EDQUOT; + if (DQUOT_ALLOC_BLOCK(inode, 1)) + goto cleanup; + + error = ext3_journal_get_write_access(handle, new_bh); + if (error) + goto cleanup; + HDR(new_bh)->h_refcount = cpu_to_le32( + le32_to_cpu(HDR(new_bh)->h_refcount) + 1); + ea_bdebug(new_bh, "refcount now=%d", + le32_to_cpu(HDR(new_bh)->h_refcount)); + } else if (old_bh && header == HDR(old_bh)) { + /* Keep this block. */ + new_bh = old_bh; + ext3_xattr_cache_insert(new_bh); + } else { + /* We need to allocate a new block */ + int goal = le32_to_cpu( + EXT3_SB(sb)->s_es->s_first_data_block) + + EXT3_I(inode)->i_block_group * + EXT3_BLOCKS_PER_GROUP(sb); + int block = ext3_new_block(handle, + inode, goal, 0, 0, &error); + if (error) + goto cleanup; + ea_idebug(inode, "creating block %d", block); + + new_bh = sb_getblk(sb, block); + if (!new_bh) { +getblk_failed: + ext3_free_blocks(handle, inode, block, 1); + error = -EIO; + goto cleanup; + } + lock_buffer(new_bh); + error = ext3_journal_get_create_access(handle, new_bh); + if (error) { + unlock_buffer(new_bh); + goto getblk_failed; + } + memcpy(new_bh->b_data, header, new_bh->b_size); + set_buffer_uptodate(new_bh); + unlock_buffer(new_bh); + ext3_xattr_cache_insert(new_bh); + + ext3_xattr_update_super_block(handle, sb); + } + error = ext3_journal_dirty_metadata(handle, new_bh); + if (error) + goto cleanup; + } + + /* Update the inode. */ + EXT3_I(inode)->i_file_acl = new_bh ? new_bh->b_blocknr : 0; + inode->i_ctime = CURRENT_TIME; + ext3_mark_inode_dirty(handle, inode); + if (IS_SYNC(inode)) + handle->h_sync = 1; + + error = 0; + if (old_bh && old_bh != new_bh) { + /* + * If there was an old block, and we are not still using it, + * we now release the old block. + */ + unsigned int refcount = le32_to_cpu(HDR(old_bh)->h_refcount); + + error = ext3_journal_get_write_access(handle, old_bh); + if (error) + goto cleanup; + if (refcount == 1) { + /* Free the old block. */ + ea_bdebug(old_bh, "freeing"); + ext3_free_blocks(handle, inode, old_bh->b_blocknr, 1); + + /* ext3_forget() calls bforget() for us, but we + let our caller release old_bh, so we need to + duplicate the handle before. */ + get_bh(old_bh); + ext3_forget(handle, 1, inode, old_bh,old_bh->b_blocknr); + } else { + /* Decrement the refcount only. */ + refcount--; + HDR(old_bh)->h_refcount = cpu_to_le32(refcount); + DQUOT_FREE_BLOCK(inode, 1); + ext3_journal_dirty_metadata(handle, old_bh); + ea_bdebug(old_bh, "refcount now=%d", refcount); + } + } + +cleanup: + if (old_bh != new_bh) + brelse(new_bh); + + return error; +} + +/* + * ext3_xattr_delete_inode() + * + * Free extended attribute resources associated with this inode. This + * is called immediately before an inode is freed. + */ +void +ext3_xattr_delete_inode(handle_t *handle, struct inode *inode) +{ + struct buffer_head *bh; + + if (!EXT3_I(inode)->i_file_acl) + return; + down(&ext3_xattr_sem); + + bh = sb_bread(inode->i_sb, EXT3_I(inode)->i_file_acl); + if (!bh) { + ext3_error(inode->i_sb, "ext3_xattr_delete_inode", + "inode %ld: block %d read error", inode->i_ino, + EXT3_I(inode)->i_file_acl); + goto cleanup; + } + ea_bdebug(bh, "b_count=%d", atomic_read(&(bh->b_count))); + if (HDR(bh)->h_magic != cpu_to_le32(EXT3_XATTR_MAGIC) || + HDR(bh)->h_blocks != cpu_to_le32(1)) { + ext3_error(inode->i_sb, "ext3_xattr_delete_inode", + "inode %ld: bad block %d", inode->i_ino, + EXT3_I(inode)->i_file_acl); + goto cleanup; + } + ext3_journal_get_write_access(handle, bh); + ea_bdebug(bh, "refcount now=%d", le32_to_cpu(HDR(bh)->h_refcount) - 1); + if (HDR(bh)->h_refcount == cpu_to_le32(1)) { + ext3_xattr_cache_remove(bh); + ext3_free_blocks(handle, inode, EXT3_I(inode)->i_file_acl, 1); + ext3_forget(handle, 1, inode, bh, EXT3_I(inode)->i_file_acl); + bh = NULL; + } else { + HDR(bh)->h_refcount = cpu_to_le32( + le32_to_cpu(HDR(bh)->h_refcount) - 1); + ext3_journal_dirty_metadata(handle, bh); + if (IS_SYNC(inode)) + handle->h_sync = 1; + DQUOT_FREE_BLOCK(inode, 1); + } + EXT3_I(inode)->i_file_acl = 0; + +cleanup: + brelse(bh); + up(&ext3_xattr_sem); +} + +/* + * ext3_xattr_put_super() + * + * This is called when a file system is unmounted. + */ +void +ext3_xattr_put_super(struct super_block *sb) +{ + mb_cache_shrink(ext3_xattr_cache, sb->s_bdev); +} + +/* + * ext3_xattr_cache_insert() + * + * Create a new entry in the extended attribute cache, and insert + * it unless such an entry is already in the cache. + * + * Returns 0, or a negative error number on failure. + */ +static int +ext3_xattr_cache_insert(struct buffer_head *bh) +{ + __u32 hash = le32_to_cpu(HDR(bh)->h_hash); + struct mb_cache_entry *ce; + int error; + + ce = mb_cache_entry_alloc(ext3_xattr_cache); + if (!ce) + return -ENOMEM; + error = mb_cache_entry_insert(ce, bh->b_bdev, bh->b_blocknr, &hash); + if (error) { + mb_cache_entry_free(ce); + if (error == -EBUSY) { + ea_bdebug(bh, "already in cache (%d cache entries)", + atomic_read(&ext3_xattr_cache->c_entry_count)); + error = 0; + } + } else { + ea_bdebug(bh, "inserting [%x] (%d cache entries)", (int)hash, + atomic_read(&ext3_xattr_cache->c_entry_count)); + mb_cache_entry_release(ce); + } + return error; +} + +/* + * ext3_xattr_cmp() + * + * Compare two extended attribute blocks for equality. + * + * Returns 0 if the blocks are equal, 1 if they differ, and + * a negative error number on errors. + */ +static int +ext3_xattr_cmp(struct ext3_xattr_header *header1, + struct ext3_xattr_header *header2) +{ + struct ext3_xattr_entry *entry1, *entry2; + + entry1 = ENTRY(header1+1); + entry2 = ENTRY(header2+1); + while (!IS_LAST_ENTRY(entry1)) { + if (IS_LAST_ENTRY(entry2)) + return 1; + if (entry1->e_hash != entry2->e_hash || + entry1->e_name_len != entry2->e_name_len || + entry1->e_value_size != entry2->e_value_size || + memcmp(entry1->e_name, entry2->e_name, entry1->e_name_len)) + return 1; + if (entry1->e_value_block != 0 || entry2->e_value_block != 0) + return -EIO; + if (memcmp((char *)header1 + le16_to_cpu(entry1->e_value_offs), + (char *)header2 + le16_to_cpu(entry2->e_value_offs), + le32_to_cpu(entry1->e_value_size))) + return 1; + + entry1 = EXT3_XATTR_NEXT(entry1); + entry2 = EXT3_XATTR_NEXT(entry2); + } + if (!IS_LAST_ENTRY(entry2)) + return 1; + return 0; +} + +/* + * ext3_xattr_cache_find() + * + * Find an identical extended attribute block. + * + * Returns a pointer to the block found, or NULL if such a block was + * not found or an error occurred. + */ +static struct buffer_head * +ext3_xattr_cache_find(struct inode *inode, struct ext3_xattr_header *header) +{ + __u32 hash = le32_to_cpu(header->h_hash); + struct mb_cache_entry *ce; + + if (!header->h_hash) + return NULL; /* never share */ + ea_idebug(inode, "looking for cached blocks [%x]", (int)hash); + ce = mb_cache_entry_find_first(ext3_xattr_cache, 0, inode->i_bdev, hash); + while (ce) { + struct buffer_head *bh = sb_bread(inode->i_sb, ce->e_block); + + if (!bh) { + ext3_error(inode->i_sb, "ext3_xattr_cache_find", + "inode %ld: block %ld read error", + inode->i_ino, (unsigned long) ce->e_block); + } else if (le32_to_cpu(HDR(bh)->h_refcount) > + EXT3_XATTR_REFCOUNT_MAX) { + ea_idebug(inode, "block %ld refcount %d>%d", + (unsigned long) ce->e_block, + le32_to_cpu(HDR(bh)->h_refcount), + EXT3_XATTR_REFCOUNT_MAX); + } else if (!ext3_xattr_cmp(header, HDR(bh))) { + ea_bdebug(bh, "b_count=%d",atomic_read(&(bh->b_count))); + mb_cache_entry_release(ce); + return bh; + } + brelse(bh); + ce = mb_cache_entry_find_next(ce, 0, inode->i_bdev, hash); + } + return NULL; +} + +/* + * ext3_xattr_cache_remove() + * + * Remove the cache entry of a block from the cache. Called when a + * block becomes invalid. + */ +static void +ext3_xattr_cache_remove(struct buffer_head *bh) +{ + struct mb_cache_entry *ce; + + ce = mb_cache_entry_get(ext3_xattr_cache, bh->b_bdev, + bh->b_blocknr); + if (ce) { + ea_bdebug(bh, "removing (%d cache entries remaining)", + atomic_read(&ext3_xattr_cache->c_entry_count)-1); + mb_cache_entry_free(ce); + } else + ea_bdebug(bh, "no cache entry"); +} + +#define NAME_HASH_SHIFT 5 +#define VALUE_HASH_SHIFT 16 + +/* + * ext3_xattr_hash_entry() + * + * Compute the hash of an extended attribute. + */ +static inline void ext3_xattr_hash_entry(struct ext3_xattr_header *header, + struct ext3_xattr_entry *entry) +{ + __u32 hash = 0; + char *name = entry->e_name; + int n; + + for (n=0; n < entry->e_name_len; n++) { + hash = (hash << NAME_HASH_SHIFT) ^ + (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^ + *name++; + } + + if (entry->e_value_block == 0 && entry->e_value_size != 0) { + __u32 *value = (__u32 *)((char *)header + + le16_to_cpu(entry->e_value_offs)); + for (n = (le32_to_cpu(entry->e_value_size) + + EXT3_XATTR_ROUND) >> EXT3_XATTR_PAD_BITS; n; n--) { + hash = (hash << VALUE_HASH_SHIFT) ^ + (hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^ + le32_to_cpu(*value++); + } + } + entry->e_hash = cpu_to_le32(hash); +} + +#undef NAME_HASH_SHIFT +#undef VALUE_HASH_SHIFT + +#define BLOCK_HASH_SHIFT 16 + +/* + * ext3_xattr_rehash() + * + * Re-compute the extended attribute hash value after an entry has changed. + */ +static void ext3_xattr_rehash(struct ext3_xattr_header *header, + struct ext3_xattr_entry *entry) +{ + struct ext3_xattr_entry *here; + __u32 hash = 0; + + ext3_xattr_hash_entry(header, entry); + here = ENTRY(header+1); + while (!IS_LAST_ENTRY(here)) { + if (!here->e_hash) { + /* Block is not shared if an entry's hash value == 0 */ + hash = 0; + break; + } + hash = (hash << BLOCK_HASH_SHIFT) ^ + (hash >> (8*sizeof(hash) - BLOCK_HASH_SHIFT)) ^ + le32_to_cpu(here->e_hash); + here = EXT3_XATTR_NEXT(here); + } + header->h_hash = cpu_to_le32(hash); +} + +#undef BLOCK_HASH_SHIFT + +int __init +init_ext3_xattr(void) +{ + int err; + + err = ext3_xattr_register(EXT3_XATTR_INDEX_USER, &ext3_xattr_user_handler); + if (err) + return err; +#ifdef CONFIG_EXT3_FS_POSIX_ACL + err = init_ext3_acl(); + if (err) + goto out; +#endif + ext3_xattr_cache = mb_cache_create("ext3_xattr", NULL, + sizeof(struct mb_cache_entry) + + sizeof(struct mb_cache_entry_index), 1, 6); + if (!ext3_xattr_cache) { + err = -ENOMEM; + goto out1; + } + return 0; +out1: +#ifdef CONFIG_EXT3_FS_POSIX_ACL + exit_ext3_acl(); +out: +#endif + ext3_xattr_unregister(EXT3_XATTR_INDEX_USER, + &ext3_xattr_user_handler); + return err; +} + +void +exit_ext3_xattr(void) +{ + if (ext3_xattr_cache) + mb_cache_destroy(ext3_xattr_cache); + ext3_xattr_cache = NULL; +#ifdef CONFIG_EXT3_FS_POSIX_ACL + exit_ext3_acl(); +#endif + ext3_xattr_unregister(EXT3_XATTR_INDEX_USER, &ext3_xattr_user_handler); +} diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/ext3/xattr.h linux-2.5-bk/fs/ext3/xattr.h --- linux-2.5.45/fs/ext3/xattr.h Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/fs/ext3/xattr.h Thu Oct 31 22:12:58 2002 @@ -0,0 +1,133 @@ +/* + File: fs/ext3/xattr.h + + On-disk format of extended attributes for the ext3 filesystem. + + (C) 2001 Andreas Gruenbacher, +*/ + +#include +#include + +/* Magic value in attribute blocks */ +#define EXT3_XATTR_MAGIC 0xEA020000 + +/* Maximum number of references to one attribute block */ +#define EXT3_XATTR_REFCOUNT_MAX 1024 + +/* Name indexes */ +#define EXT3_XATTR_INDEX_MAX 10 +#define EXT3_XATTR_INDEX_USER 1 +#define EXT3_XATTR_INDEX_POSIX_ACL_ACCESS 2 +#define EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT 3 + +struct ext3_xattr_header { + __u32 h_magic; /* magic number for identification */ + __u32 h_refcount; /* reference count */ + __u32 h_blocks; /* number of disk blocks used */ + __u32 h_hash; /* hash value of all attributes */ + __u32 h_reserved[4]; /* zero right now */ +}; + +struct ext3_xattr_entry { + __u8 e_name_len; /* length of name */ + __u8 e_name_index; /* attribute name index */ + __u16 e_value_offs; /* offset in disk block of value */ + __u32 e_value_block; /* disk block attribute is stored on (n/i) */ + __u32 e_value_size; /* size of attribute value */ + __u32 e_hash; /* hash value of name and value */ + char e_name[0]; /* attribute name */ +}; + +#define EXT3_XATTR_PAD_BITS 2 +#define EXT3_XATTR_PAD (1<e_name_len)) ) +#define EXT3_XATTR_SIZE(size) \ + (((size) + EXT3_XATTR_ROUND) & ~EXT3_XATTR_ROUND) + +# ifdef CONFIG_EXT3_FS_XATTR + +struct ext3_xattr_handler { + char *prefix; + size_t (*list)(char *list, struct inode *inode, const char *name, + int name_len); + int (*get)(struct inode *inode, const char *name, void *buffer, + size_t size); + int (*set)(struct inode *inode, const char *name, const void *buffer, + size_t size, int flags); +}; + +extern int ext3_xattr_register(int, struct ext3_xattr_handler *); +extern void ext3_xattr_unregister(int, struct ext3_xattr_handler *); + +extern int ext3_setxattr(struct dentry *, const char *, void *, size_t, int); +extern ssize_t ext3_getxattr(struct dentry *, const char *, void *, size_t); +extern ssize_t ext3_listxattr(struct dentry *, char *, size_t); +extern int ext3_removexattr(struct dentry *, const char *); + +extern int ext3_xattr_get(struct inode *, int, const char *, void *, size_t); +extern int ext3_xattr_list(struct inode *, char *, size_t); +extern int ext3_xattr_set(handle_t *handle, struct inode *, int, const char *, const void *, size_t, int); + +extern void ext3_xattr_delete_inode(handle_t *, struct inode *); +extern void ext3_xattr_put_super(struct super_block *); + +extern int init_ext3_xattr(void); +extern void exit_ext3_xattr(void); + +# else /* CONFIG_EXT3_FS_XATTR */ +# define ext3_setxattr NULL +# define ext3_getxattr NULL +# define ext3_listxattr NULL +# define ext3_removexattr NULL + +static inline int +ext3_xattr_get(struct inode *inode, int name_index, const char *name, + void *buffer, size_t size, int flags) +{ + return -EOPNOTSUPP; +} + +static inline int +ext3_xattr_list(struct inode *inode, void *buffer, size_t size, int flags) +{ + return -EOPNOTSUPP; +} + +static inline int +ext3_xattr_set(handle_t *handle, struct inode *inode, int name_index, + const char *name, const void *value, size_t size, int flags) +{ + return -EOPNOTSUPP; +} + +static inline void +ext3_xattr_delete_inode(handle_t *handle, struct inode *inode) +{ +} + +static inline void +ext3_xattr_put_super(struct super_block *sb) +{ +} + +static inline int +init_ext3_xattr(void) +{ + return 0; +} + +static inline void +exit_ext3_xattr(void) +{ +} + +# endif /* CONFIG_EXT3_FS_XATTR */ + +extern struct ext3_xattr_handler ext3_xattr_user_handler; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/ext3/xattr_user.c linux-2.5-bk/fs/ext3/xattr_user.c --- linux-2.5.45/fs/ext3/xattr_user.c Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/fs/ext3/xattr_user.c Thu Oct 31 22:12:58 2002 @@ -0,0 +1,99 @@ +/* + * linux/fs/ext3/xattr_user.c + * Handler for extended user attributes. + * + * Copyright (C) 2001 by Andreas Gruenbacher, + */ + +#include +#include +#include +#include +#include +#include +#include "xattr.h" + +#ifdef CONFIG_EXT3_FS_POSIX_ACL +# include "acl.h" +#endif + +#define XATTR_USER_PREFIX "user." + +static size_t +ext3_xattr_user_list(char *list, struct inode *inode, + const char *name, int name_len) +{ + const int prefix_len = sizeof(XATTR_USER_PREFIX)-1; + + if (!test_opt(inode->i_sb, XATTR_USER)) + return 0; + + if (list) { + memcpy(list, XATTR_USER_PREFIX, prefix_len); + memcpy(list+prefix_len, name, name_len); + } + return prefix_len + name_len; +} + +static int +ext3_xattr_user_get(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + int error; + + if (strcmp(name, "") == 0) + return -EINVAL; + if (!test_opt(inode->i_sb, XATTR_USER)) + return -EOPNOTSUPP; +#ifdef CONFIG_EXT3_FS_POSIX_ACL + error = ext3_permission_locked(inode, MAY_READ); +#else + error = permission(inode, MAY_READ); +#endif + if (error) + return error; + + return ext3_xattr_get(inode, EXT3_XATTR_INDEX_USER, name, + buffer, size); +} + +static int +ext3_xattr_user_set(struct inode *inode, const char *name, + const void *value, size_t size, int flags) +{ + handle_t *handle; + int error; + + if (strcmp(name, "") == 0) + return -EINVAL; + if (!test_opt(inode->i_sb, XATTR_USER)) + return -EOPNOTSUPP; + if ( !S_ISREG(inode->i_mode) && + (!S_ISDIR(inode->i_mode) || inode->i_mode & S_ISVTX)) + return -EPERM; +#ifdef CONFIG_EXT3_FS_POSIX_ACL + error = ext3_permission_locked(inode, MAY_WRITE); +#else + error = permission(inode, MAY_WRITE); +#endif + if (error) + return error; + + lock_kernel(); + handle = ext3_journal_start(inode, EXT3_XATTR_TRANS_BLOCKS); + if (IS_ERR(handle)) + return PTR_ERR(handle); + error = ext3_xattr_set(handle, inode, EXT3_XATTR_INDEX_USER, name, + value, size, flags); + ext3_journal_stop(handle, inode); + unlock_kernel(); + + return error; +} + +struct ext3_xattr_handler ext3_xattr_user_handler = { + prefix: XATTR_USER_PREFIX, + list: ext3_xattr_user_list, + get: ext3_xattr_user_get, + set: ext3_xattr_user_set, +}; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/hugetlbfs/Makefile linux-2.5-bk/fs/hugetlbfs/Makefile --- linux-2.5.45/fs/hugetlbfs/Makefile Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/fs/hugetlbfs/Makefile Thu Oct 31 22:12:58 2002 @@ -0,0 +1,9 @@ +# +# Makefile for the linux ramfs routines. +# + +obj-$(CONFIG_HUGETLBFS) += hugetlbfs.o + +hugetlbfs-objs := inode.o + +include $(TOPDIR)/Rules.make diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/hugetlbfs/inode.c linux-2.5-bk/fs/hugetlbfs/inode.c --- linux-2.5.45/fs/hugetlbfs/inode.c Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/fs/hugetlbfs/inode.c Thu Oct 31 22:12:58 2002 @@ -0,0 +1,594 @@ +/* + * hugetlbpage-backed filesystem. Based on ramfs. + * + * William Irwin, 2002 + * + * Copyright (C) 2002 Linus Torvalds. + */ + +#include +#include +#include +#include /* remove ASAP */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* some random number */ +#define HUGETLBFS_MAGIC 0x958458f6 + +static struct super_operations hugetlbfs_ops; +static struct address_space_operations hugetlbfs_aops; +struct file_operations hugetlbfs_file_operations; +static struct inode_operations hugetlbfs_dir_inode_operations; + +static struct backing_dev_info hugetlbfs_backing_dev_info = { + .ra_pages = 0, /* No readahead */ + .memory_backed = 1, /* Does not contribute to dirty memory */ +}; + +static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct inode *inode =file->f_dentry->d_inode; + struct address_space *mapping = inode->i_mapping; + int ret; + + if (!capable(CAP_IPC_LOCK)) + return -EPERM; + + if (vma->vm_start & ~HPAGE_MASK) + return -EINVAL; + + if (vma->vm_end & ~HPAGE_MASK) + return -EINVAL; + + if (vma->vm_end - vma->vm_start < HPAGE_SIZE) + return -EINVAL; + + down(&inode->i_sem); + + UPDATE_ATIME(inode); + vma->vm_flags |= VM_HUGETLB | VM_RESERVED; + vma->vm_ops = &hugetlb_vm_ops; + ret = hugetlb_prefault(mapping, vma); + + up(&inode->i_sem); + + return ret; +} + +/* + * Read a page. Again trivial. If it didn't already exist + * in the page cache, it is zero-filled. + */ +static int hugetlbfs_readpage(struct file *file, struct page * page) +{ + return -EINVAL; +} + +static int hugetlbfs_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to) +{ + return -EINVAL; +} + +static int hugetlbfs_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to) +{ + return -EINVAL; +} + +void huge_pagevec_release(struct pagevec *pvec) +{ + int i; + + for (i = 0; i < pagevec_count(pvec); ++i) + huge_page_release(pvec->pages[i]); + + pagevec_reinit(pvec); +} + +void truncate_partial_hugepage(struct page *page, unsigned partial) +{ + int i; + const unsigned piece = partial & (PAGE_SIZE - 1); + const unsigned tailstart = PAGE_SIZE - piece; + const unsigned whole_pages = partial / PAGE_SIZE; + const unsigned last_page_offset = HPAGE_SIZE/PAGE_SIZE - whole_pages; + + for (i = HPAGE_SIZE/PAGE_SIZE - 1; i >= last_page_offset; ++i) + memclear_highpage_flush(&page[i], 0, PAGE_SIZE); + + if (!piece) + return; + + memclear_highpage_flush(&page[last_page_offset - 1], tailstart, piece); +} + +void truncate_huge_page(struct address_space *mapping, struct page *page) +{ + if (page->mapping != mapping) + return; + + clear_page_dirty(page); + ClearPageUptodate(page); + remove_from_page_cache(page); + huge_page_release(page); +} + +void truncate_hugepages(struct address_space *mapping, loff_t lstart) +{ + const pgoff_t start = (lstart + HPAGE_SIZE - 1) >> HPAGE_SHIFT; + const unsigned partial = lstart & (HPAGE_SIZE - 1); + struct pagevec pvec; + pgoff_t next; + int i; + + pagevec_init(&pvec, 0); + next = start; + + while (pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) { + for (i = 0; i < pagevec_count(&pvec); ++i) { + struct page *page = pvec.pages[i]; + pgoff_t page_index = page->index; + + if (page_index > next) + next = page_index; + + ++next; + + if (TestSetPageLocked(page)) + continue; + + if (PageWriteback(page)) { + unlock_page(page); + continue; + } + + truncate_huge_page(mapping, page); + unlock_page(page); + } + huge_pagevec_release(&pvec); + cond_resched(); + } + + if (partial) { + struct page *page = find_lock_page(mapping, start - 1); + if (page) { + wait_on_page_writeback(page); + truncate_partial_hugepage(page, partial); + unlock_page(page); + huge_page_release(page); + } + } + + next = start; + + while (1) { + if (!pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) { + if (next == start) + break; + next = start; + continue; + } + + for (i = 0; i < pagevec_count(&pvec); ++i) { + struct page *page = pvec.pages[i]; + + lock_page(page); + wait_on_page_writeback(page); + if (page->index > next) + next = page->index; + ++next; + truncate_huge_page(mapping, page); + unlock_page(page); + } + huge_pagevec_release(&pvec); + } + BUG_ON(!lstart && mapping->nrpages); +} + +static void hugetlbfs_delete_inode(struct inode *inode) +{ + list_del_init(&inode->i_hash); + list_del_init(&inode->i_list); + inode->i_state |= I_FREEING; + inodes_stat.nr_inodes--; + spin_unlock(&inode_lock); + + if (inode->i_data.nrpages) + truncate_hugepages(&inode->i_data, 0); + + security_ops->inode_delete(inode); + + clear_inode(inode); + destroy_inode(inode); +} + +static void hugetlbfs_forget_inode(struct inode *inode) +{ + struct super_block *super_block = inode->i_sb; + + if (list_empty(&inode->i_hash)) + goto out_truncate; + + if (!(inode->i_state & (I_DIRTY|I_LOCK))) { + list_del(&inode->i_list); + list_add(&inode->i_list, &inode_unused); + } + inodes_stat.nr_unused++; + if (!super_block | (super_block->s_flags & MS_ACTIVE)) { + spin_unlock(&inode_lock); + return; + } + + /* write_inode_now() ? */ + inodes_stat.nr_unused--; + list_del_init(&inode->i_hash); +out_truncate: + list_del_init(&inode->i_list); + inode->i_state |= I_FREEING; + inodes_stat.nr_inodes--; + spin_unlock(&inode_lock); + if (inode->i_data.nrpages) + truncate_hugepages(&inode->i_data, 0); + clear_inode(inode); + destroy_inode(inode); +} + +static void hugetlbfs_drop_inode(struct inode *inode) +{ + if (!inode->i_nlink) + hugetlbfs_delete_inode(inode); + else + hugetlbfs_forget_inode(inode); +} + +static void hugetlb_vmtruncate_list(struct list_head *list, unsigned long pgoff) +{ + unsigned long start, end, length, delta; + struct vm_area_struct *vma; + + list_for_each_entry(vma, list, shared) { + start = vma->vm_start; + end = vma->vm_end; + length = end - start; + + if (vma->vm_pgoff >= pgoff) { + zap_hugepage_range(vma, start, length); + continue; + } + + length >>= PAGE_SHIFT; + delta = pgoff = vma->vm_pgoff; + if (delta >= length) + continue; + + start += delta << PAGE_SHIFT; + length = (length - delta) << PAGE_SHIFT; + zap_hugepage_range(vma, start, length); + } +} + +static int hugetlb_vmtruncate(struct inode *inode, loff_t offset) +{ + unsigned long pgoff; + struct address_space *mapping = inode->i_mapping; + unsigned long limit; + + pgoff = (offset + HPAGE_SIZE - 1) >> HPAGE_SHIFT; + + if (inode->i_size < offset) + goto do_expand; + + inode->i_size = offset; + spin_lock(&mapping->i_shared_lock); + if (list_empty(&mapping->i_mmap) && list_empty(&mapping->i_mmap_shared)) + goto out_unlock; + if (!list_empty(&mapping->i_mmap)) + hugetlb_vmtruncate_list(&mapping->i_mmap, pgoff); + if (!list_empty(&mapping->i_mmap_shared)) + hugetlb_vmtruncate_list(&mapping->i_mmap_shared, pgoff); + +out_unlock: + spin_unlock(&mapping->i_shared_lock); + truncate_hugepages(mapping, offset); + return 0; + +do_expand: + limit = current->rlim[RLIMIT_FSIZE].rlim_cur; + if (limit != RLIM_INFINITY && offset > limit) + goto out_sig; + if (offset > inode->i_sb->s_maxbytes) + goto out; + inode->i_size = offset; + return 0; + +out_sig: + send_sig(SIGXFSZ, current, 0); +out: + return -EFBIG; +} + +static int hugetlbfs_setattr(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + int error; + unsigned int ia_valid = attr->ia_valid; + unsigned long dn_mask; + + BUG_ON(!inode); + + error = inode_change_ok(inode, attr); + if (error) + goto out; + + error = security_ops->inode_setattr(dentry, attr); + if (error) + goto out; + + if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) || + (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) + error = DQUOT_TRANSFER(inode, attr) ? -EDQUOT : 0; + if (error) + goto out; + + if (ia_valid & ATTR_SIZE) { + error = hugetlb_vmtruncate(inode, attr->ia_size); + if (error) + goto out; + attr->ia_valid &= ~ATTR_SIZE; + error = inode_setattr(inode, attr); + } + if (error) + goto out; + dn_mask = setattr_mask(ia_valid); + if (dn_mask) + dnotify_parent(dentry, dn_mask); +out: + return error; +} + +struct inode *hugetlbfs_get_inode(struct super_block *sb, int mode, int dev) +{ + struct inode * inode = new_inode(sb); + + if (inode) { + inode->i_mode = mode; + inode->i_uid = current->fsuid; + inode->i_gid = current->fsgid; + inode->i_blksize = PAGE_CACHE_SIZE; + inode->i_blocks = 0; + inode->i_rdev = NODEV; + inode->i_mapping->a_ops = &hugetlbfs_aops; + inode->i_mapping->backing_dev_info = &hugetlbfs_backing_dev_info; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + switch (mode & S_IFMT) { + default: + init_special_inode(inode, mode, dev); + break; + case S_IFREG: + inode->i_fop = &hugetlbfs_file_operations; + break; + case S_IFDIR: + inode->i_op = &hugetlbfs_dir_inode_operations; + inode->i_fop = &simple_dir_operations; + + /* directory inodes start off with i_nlink == 2 (for "." entry) */ + inode->i_nlink++; + break; + case S_IFLNK: + inode->i_op = &page_symlink_inode_operations; + break; + } + } + return inode; +} + +/* + * File creation. Allocate an inode, and we're done.. + */ +/* SMP-safe */ +static int hugetlbfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev) +{ + struct inode * inode = hugetlbfs_get_inode(dir->i_sb, mode, dev); + int error = -ENOSPC; + + if (inode) { + d_instantiate(dentry, inode); + dget(dentry); /* Extra count - pin the dentry in core */ + error = 0; + } + return error; +} + +static int hugetlbfs_mkdir(struct inode * dir, struct dentry * dentry, int mode) +{ + int retval = hugetlbfs_mknod(dir, dentry, mode | S_IFDIR, 0); + if (!retval) + dir->i_nlink++; + return retval; +} + +static int hugetlbfs_create(struct inode *dir, struct dentry *dentry, int mode) +{ + return hugetlbfs_mknod(dir, dentry, mode | S_IFREG, 0); +} + +static int hugetlbfs_symlink(struct inode * dir, struct dentry *dentry, const char * symname) +{ + struct inode *inode; + int error = -ENOSPC; + + inode = hugetlbfs_get_inode(dir->i_sb, S_IFLNK|S_IRWXUGO, 0); + if (inode) { + int l = strlen(symname)+1; + error = page_symlink(inode, symname, l); + if (!error) { + d_instantiate(dentry, inode); + dget(dentry); + } else + iput(inode); + } + return error; +} + +static struct address_space_operations hugetlbfs_aops = { + readpage: hugetlbfs_readpage, + writepage: fail_writepage, + prepare_write: hugetlbfs_prepare_write, + commit_write: hugetlbfs_commit_write +}; + +struct file_operations hugetlbfs_file_operations = { + read: generic_file_read, + write: generic_file_write, + mmap: hugetlbfs_file_mmap, + fsync: simple_sync_file, + sendfile: generic_file_sendfile, +}; + +static struct inode_operations hugetlbfs_dir_inode_operations = { + create: hugetlbfs_create, + lookup: simple_lookup, + link: simple_link, + unlink: simple_unlink, + symlink: hugetlbfs_symlink, + mkdir: hugetlbfs_mkdir, + rmdir: simple_rmdir, + mknod: hugetlbfs_mknod, + rename: simple_rename, + setattr: hugetlbfs_setattr, +}; + +static struct super_operations hugetlbfs_ops = { + statfs: simple_statfs, + drop_inode: hugetlbfs_drop_inode, +}; + +static int hugetlbfs_fill_super(struct super_block * sb, void * data, int silent) +{ + struct inode * inode; + struct dentry * root; + + sb->s_blocksize = PAGE_CACHE_SIZE; + sb->s_blocksize_bits = PAGE_CACHE_SHIFT; + sb->s_magic = HUGETLBFS_MAGIC; + sb->s_op = &hugetlbfs_ops; + inode = hugetlbfs_get_inode(sb, S_IFDIR | 0755, 0); + if (!inode) + return -ENOMEM; + + root = d_alloc_root(inode); + if (!root) { + iput(inode); + return -ENOMEM; + } + sb->s_root = root; + return 0; +} + +static struct super_block *hugetlbfs_get_sb(struct file_system_type *fs_type, + int flags, char *dev_name, void *data) +{ + return get_sb_nodev(fs_type, flags, data, hugetlbfs_fill_super); +} + +static struct file_system_type hugetlbfs_fs_type = { + name: "hugetlbfs", + get_sb: hugetlbfs_get_sb, + kill_sb: kill_litter_super, +}; + +static struct vfsmount *hugetlbfs_vfsmount; + +static atomic_t hugetlbfs_counter = ATOMIC_INIT(0); + +struct file *hugetlb_zero_setup(size_t size) +{ + int error, n; + struct file *file; + struct inode *inode; + struct dentry *dentry, *root; + struct qstr quick_string; + char buf[16]; + + if (!capable(CAP_IPC_LOCK)) + return ERR_PTR(-EPERM); + + n = atomic_read(&hugetlbfs_counter); + atomic_inc(&hugetlbfs_counter); + + root = hugetlbfs_vfsmount->mnt_root; + snprintf(buf, 16, "%d", n); + quick_string.name = buf; + quick_string.len = strlen(quick_string.name); + quick_string.hash = 0; + dentry = d_alloc(root, &quick_string); + if (!dentry) + return ERR_PTR(-ENOMEM); + + error = -ENFILE; + file = get_empty_filp(); + if (!file) + goto out_dentry; + + error = -ENOSPC; + inode = hugetlbfs_get_inode(root->d_sb, S_IFREG | S_IRWXUGO, 0); + if (!inode) + goto out_file; + + d_instantiate(dentry, inode); + inode->i_size = size; + inode->i_nlink = 0; + file->f_vfsmnt = mntget(hugetlbfs_vfsmount); + file->f_dentry = dentry; + file->f_op = &hugetlbfs_file_operations; + file->f_mode = FMODE_WRITE | FMODE_READ; + return file; + +out_file: + put_filp(file); +out_dentry: + dput(dentry); + return ERR_PTR(error); +} + +static int __init init_hugetlbfs_fs(void) +{ + int error; + struct vfsmount *vfsmount; + + error = register_filesystem(&hugetlbfs_fs_type); + if (error) + return error; + + vfsmount = kern_mount(&hugetlbfs_fs_type); + + if (!IS_ERR(vfsmount)) { + hugetlbfs_vfsmount = vfsmount; + return 0; + } + + error = PTR_ERR(vfsmount); + return error; +} + +static void __exit exit_hugetlbfs_fs(void) +{ + unregister_filesystem(&hugetlbfs_fs_type); +} + +module_init(init_hugetlbfs_fs) +module_exit(exit_hugetlbfs_fs) + +MODULE_LICENSE("GPL"); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/inode.c linux-2.5-bk/fs/inode.c --- linux-2.5.45/fs/inode.c Wed Oct 30 16:43:44 2002 +++ linux-2.5-bk/fs/inode.c Thu Oct 31 22:12:58 2002 @@ -142,7 +142,7 @@ return inode; } -static void destroy_inode(struct inode *inode) +void destroy_inode(struct inode *inode) { if (inode_has_buffers(inode)) BUG(); @@ -363,57 +363,69 @@ return res; } +static int can_unuse(struct inode *inode) +{ + if (inode->i_state) + return 0; + if (inode_has_buffers(inode)) + return 0; + if (atomic_read(&inode->i_count)) + return 0; + return 1; +} /* - * This is called with the inode lock held. It searches - * the in-use for freeable inodes, which are moved to a - * temporary list and then placed on the unused list by - * dispose_list. - * - * We don't expect to have to call this very often. + * Scan `goal' inodes on the unused list for freeable ones. They are moved to + * a temporary list and then are freed outside inode_lock by dispose_list(). * - * N.B. The spinlock is released during the call to - * dispose_list. + * Any inodes which are pinned purely because of attached pagecache have their + * pagecache removed. We expect the final iput() on that inode to add it to + * the front of the inode_unused list. So look for it there and if the + * inode is still freeable, proceed. The right inode is found 99.9% of the + * time in testing on a 4-way. */ -#define CAN_UNUSE(inode) \ - ((((inode)->i_state | (inode)->i_data.nrpages) == 0) && \ - !inode_has_buffers(inode)) -#define INODE(entry) (list_entry(entry, struct inode, i_list)) - -static inline void prune_icache(int goal) -{ - LIST_HEAD(list); - struct list_head *entry, *freeable = &list; - int count; - struct inode * inode; +static void prune_icache(int nr_to_scan) +{ + LIST_HEAD(freeable); + int nr_pruned = 0; + int nr_scanned; spin_lock(&inode_lock); + for (nr_scanned = 0; nr_scanned < nr_to_scan; nr_scanned++) { + struct inode *inode; - count = 0; - entry = inode_unused.prev; - for(; goal; goal--) { - struct list_head *tmp = entry; - - if (entry == &inode_unused) + if (list_empty(&inode_unused)) break; - entry = entry->prev; - inode = INODE(tmp); - if (inode->i_state & (I_FREEING|I_CLEAR|I_LOCK)) - continue; - if (!CAN_UNUSE(inode)) - continue; - if (atomic_read(&inode->i_count)) + + inode = list_entry(inode_unused.prev, struct inode, i_list); + + if (!can_unuse(inode)) { + list_move(&inode->i_list, &inode_unused); continue; - list_del(tmp); + } + if (inode->i_data.nrpages) { + __iget(inode); + spin_unlock(&inode_lock); + invalidate_inode_pages(&inode->i_data); + iput(inode); + spin_lock(&inode_lock); + + if (inode != list_entry(inode_unused.next, + struct inode, i_list)) + continue; /* wrong inode or list_empty */ + if (!can_unuse(inode)) + continue; + if (inode->i_data.nrpages) + continue; + } list_del_init(&inode->i_hash); - list_add(tmp, freeable); + list_move(&inode->i_list, &freeable); inode->i_state |= I_FREEING; - count++; + nr_pruned++; } - inodes_stat.nr_unused -= count; + inodes_stat.nr_unused -= nr_pruned; spin_unlock(&inode_lock); - - dispose_list(freeable); + dispose_list(&freeable); } /* diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/mbcache.c linux-2.5-bk/fs/mbcache.c --- linux-2.5.45/fs/mbcache.c Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/fs/mbcache.c Thu Oct 31 22:12:58 2002 @@ -0,0 +1,702 @@ +/* + * linux/fs/mbcache.c + * (C) 2001-2002 Andreas Gruenbacher, + */ + +/* + * Filesystem Meta Information Block Cache (mbcache) + * + * The mbcache caches blocks of block devices that need to be located + * by their device/block number, as well as by other criteria (such + * as the block's contents). + * + * There can only be one cache entry in a cache per device and block number. + * Additional indexes need not be unique in this sense. The number of + * additional indexes (=other criteria) can be hardwired (at compile time) + * or specified at cache create time. + * + * Each cache entry is of fixed size. An entry may be `valid' or `invalid' + * in the cache. A valid entry is in the main hash tables of the cache, + * and may also be in the lru list. An invalid entry is not in any hashes + * or lists. + * + * A valid cache entry is only in the lru list if no handles refer to it. + * Invalid cache entries will be freed when the last handle to the cache + * entry is released. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +#ifdef MB_CACHE_DEBUG +# define mb_debug(f...) do { \ + printk(KERN_DEBUG f); \ + printk("\n"); \ + } while (0) +#define mb_assert(c) do { if (!(c)) \ + printk(KERN_ERR "assertion " #c " failed\n"); \ + } while(0) +#else +# define mb_debug(f...) do { } while(0) +# define mb_assert(c) do { } while(0) +#endif +#define mb_error(f...) do { \ + printk(KERN_ERR f); \ + printk("\n"); \ + } while(0) + +MODULE_AUTHOR("Andreas Gruenbacher "); +MODULE_DESCRIPTION("Meta block cache (for extended attributes)"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(mb_cache_create); +EXPORT_SYMBOL(mb_cache_shrink); +EXPORT_SYMBOL(mb_cache_destroy); +EXPORT_SYMBOL(mb_cache_entry_alloc); +EXPORT_SYMBOL(mb_cache_entry_insert); +EXPORT_SYMBOL(mb_cache_entry_release); +EXPORT_SYMBOL(mb_cache_entry_takeout); +EXPORT_SYMBOL(mb_cache_entry_free); +EXPORT_SYMBOL(mb_cache_entry_dup); +EXPORT_SYMBOL(mb_cache_entry_get); +#if !defined(MB_CACHE_INDEXES_COUNT) || (MB_CACHE_INDEXES_COUNT > 0) +EXPORT_SYMBOL(mb_cache_entry_find_first); +EXPORT_SYMBOL(mb_cache_entry_find_next); +#endif + + +/* + * Global data: list of all mbcache's, lru list, and a spinlock for + * accessing cache data structures on SMP machines. (The lru list is + * global across all mbcaches.) + */ + +static LIST_HEAD(mb_cache_list); +static LIST_HEAD(mb_cache_lru_list); +static spinlock_t mb_cache_spinlock = SPIN_LOCK_UNLOCKED; +static struct shrinker *mb_shrinker; + +static inline int +mb_cache_indexes(struct mb_cache *cache) +{ +#ifdef MB_CACHE_INDEXES_COUNT + return MB_CACHE_INDEXES_COUNT; +#else + return cache->c_indexes_count; +#endif +} + +/* + * What the mbcache registers as to get shrunk dynamically. + */ + +static int mb_cache_shrink_fn(int nr_to_scan, unsigned int gfp_mask); + +static inline void +__mb_cache_entry_takeout_lru(struct mb_cache_entry *ce) +{ + if (!list_empty(&ce->e_lru_list)) + list_del_init(&ce->e_lru_list); +} + + +static inline void +__mb_cache_entry_into_lru(struct mb_cache_entry *ce) +{ + list_add(&ce->e_lru_list, &mb_cache_lru_list); +} + + +static inline int +__mb_cache_entry_in_lru(struct mb_cache_entry *ce) +{ + return (!list_empty(&ce->e_lru_list)); +} + + +/* + * Insert the cache entry into all hashes. + */ +static inline void +__mb_cache_entry_link(struct mb_cache_entry *ce) +{ + struct mb_cache *cache = ce->e_cache; + unsigned int bucket; + int n; + + bucket = hash_long((unsigned long)ce->e_bdev + + (ce->e_block & 0xffffff), cache->c_bucket_bits); + list_add(&ce->e_block_list, &cache->c_block_hash[bucket]); + for (n=0; ne_indexes[n].o_key, + cache->c_bucket_bits); + list_add(&ce->e_indexes[n].o_list, + &cache->c_indexes_hash[n][bucket]); + } +} + + +/* + * Remove the cache entry from all hashes. + */ +static inline void +__mb_cache_entry_unlink(struct mb_cache_entry *ce) +{ + int n; + + list_del_init(&ce->e_block_list); + for (n = 0; n < mb_cache_indexes(ce->e_cache); n++) + list_del(&ce->e_indexes[n].o_list); +} + + +static inline int +__mb_cache_entry_is_linked(struct mb_cache_entry *ce) +{ + return (!list_empty(&ce->e_block_list)); +} + + +static inline struct mb_cache_entry * +__mb_cache_entry_read(struct mb_cache_entry *ce) +{ + __mb_cache_entry_takeout_lru(ce); + atomic_inc(&ce->e_used); + return ce; +} + + +static inline void +__mb_cache_entry_forget(struct mb_cache_entry *ce) +{ + struct mb_cache *cache = ce->e_cache; + + mb_assert(atomic_read(&ce->e_used) == 0); + atomic_dec(&cache->c_entry_count); + if (cache->c_op.free) + cache->c_op.free(ce); + kmem_cache_free(cache->c_entry_cache, ce); +} + + +static inline void +__mb_cache_entry_release_unlock(struct mb_cache_entry *ce) +{ + if (atomic_dec_and_test(&ce->e_used)) { + if (!__mb_cache_entry_is_linked(ce)) + goto forget; + __mb_cache_entry_into_lru(ce); + } + spin_unlock(&mb_cache_spinlock); + return; +forget: + spin_unlock(&mb_cache_spinlock); + __mb_cache_entry_forget(ce); +} + + +/* + * mb_cache_shrink_fn() memory pressure callback + * + * This function is called by the kernel memory management when memory + * gets low. + * + * @nr_to_scan: Number of objects to scan + * @gfp_mask: (ignored) + * + * Returns the number of objects which are present in the cache. + */ +static int +mb_cache_shrink_fn(int nr_to_scan, unsigned int gfp_mask) +{ + LIST_HEAD(free_list); + struct list_head *l; + int count = 0; + + spin_lock(&mb_cache_spinlock); + list_for_each_prev(l, &mb_cache_list) { + struct mb_cache *cache = + list_entry(l, struct mb_cache, c_cache_list); + mb_debug("cache %s (%d)", cache->c_name, + atomic_read(&cache->c_entry_count)); + count += atomic_read(&cache->c_entry_count); + } + mb_debug("trying to free %d entries", nr_to_scan); + if (nr_to_scan == 0) { + spin_unlock(&mb_cache_spinlock); + goto out; + } + while (nr_to_scan && !list_empty(&mb_cache_lru_list)) { + struct mb_cache_entry *ce = + list_entry(mb_cache_lru_list.prev, + struct mb_cache_entry, e_lru_list); + list_move(&ce->e_lru_list, &free_list); + if (__mb_cache_entry_is_linked(ce)) + __mb_cache_entry_unlink(ce); + nr_to_scan--; + } + spin_unlock(&mb_cache_spinlock); + l = free_list.prev; + while (l != &free_list) { + struct mb_cache_entry *ce = list_entry(l, + struct mb_cache_entry, e_lru_list); + l = l->prev; + __mb_cache_entry_forget(ce); + count--; + } +out: + mb_debug("%d remaining entries ", count); + return count; +} + + +/* + * mb_cache_create() create a new cache + * + * All entries in one cache are equal size. Cache entries may be from + * multiple devices. If this is the first mbcache created, registers + * the cache with kernel memory management. Returns NULL if no more + * memory was available. + * + * @name: name of the cache (informal) + * @cache_op: contains the callback called when freeing a cache entry + * @entry_size: The size of a cache entry, including + * struct mb_cache_entry + * @indexes_count: number of additional indexes in the cache. Must equal + * MB_CACHE_INDEXES_COUNT if the number of indexes is + * hardwired. + * @bucket_bits: log2(number of hash buckets) + */ +struct mb_cache * +mb_cache_create(const char *name, struct mb_cache_op *cache_op, + size_t entry_size, int indexes_count, int bucket_bits) +{ + int m=0, n, bucket_count = 1 << bucket_bits; + struct mb_cache *cache = NULL; + + if(entry_size < sizeof(struct mb_cache_entry) + + indexes_count * sizeof(struct mb_cache_entry_index)) + return NULL; + + cache = kmalloc(sizeof(struct mb_cache) + + indexes_count * sizeof(struct list_head), GFP_KERNEL); + if (!cache) + goto fail; + cache->c_name = name; + if (cache_op) + cache->c_op.free = cache_op->free; + else + cache->c_op.free = NULL; + atomic_set(&cache->c_entry_count, 0); + cache->c_bucket_bits = bucket_bits; +#ifdef MB_CACHE_INDEXES_COUNT + mb_assert(indexes_count == MB_CACHE_INDEXES_COUNT); +#else + cache->c_indexes_count = indexes_count; +#endif + cache->c_block_hash = kmalloc(bucket_count * sizeof(struct list_head), + GFP_KERNEL); + if (!cache->c_block_hash) + goto fail; + for (n=0; nc_block_hash[n]); + for (m=0; mc_indexes_hash[m] = kmalloc(bucket_count * + sizeof(struct list_head), + GFP_KERNEL); + if (!cache->c_indexes_hash[m]) + goto fail; + for (n=0; nc_indexes_hash[m][n]); + } + cache->c_entry_cache = kmem_cache_create(name, entry_size, 0, + 0 /*SLAB_POISON | SLAB_RED_ZONE*/, NULL, NULL); + if (!cache->c_entry_cache) + goto fail; + + spin_lock(&mb_cache_spinlock); + if (list_empty(&mb_cache_list)) { + if (mb_shrinker) { + printk(KERN_ERR "%s: already have a shrinker!\n", + __FUNCTION__); + remove_shrinker(mb_shrinker); + } + mb_shrinker = set_shrinker(DEFAULT_SEEKS, mb_cache_shrink_fn); + } + list_add(&cache->c_cache_list, &mb_cache_list); + spin_unlock(&mb_cache_spinlock); + return cache; + +fail: + if (cache) { + while (--m >= 0) + kfree(cache->c_indexes_hash[m]); + if (cache->c_block_hash) + kfree(cache->c_block_hash); + kfree(cache); + } + return NULL; +} + + +/* + * mb_cache_shrink() + * + * Removes all cache entires of a device from the cache. All cache entries + * currently in use cannot be freed, and thus remain in the cache. All others + * are freed. + * + * @cache: which cache to shrink + * @bdev: which device's cache entries to shrink + */ +void +mb_cache_shrink(struct mb_cache *cache, struct block_device *bdev) +{ + LIST_HEAD(free_list); + struct list_head *l; + + spin_lock(&mb_cache_spinlock); + l = mb_cache_lru_list.prev; + while (l != &mb_cache_lru_list) { + struct mb_cache_entry *ce = + list_entry(l, struct mb_cache_entry, e_lru_list); + l = l->prev; + if (ce->e_bdev == bdev) { + list_move(&ce->e_lru_list, &free_list); + if (__mb_cache_entry_is_linked(ce)) + __mb_cache_entry_unlink(ce); + } + } + spin_unlock(&mb_cache_spinlock); + l = free_list.prev; + while (l != &free_list) { + struct mb_cache_entry *ce = + list_entry(l, struct mb_cache_entry, e_lru_list); + l = l->prev; + __mb_cache_entry_forget(ce); + } +} + + +/* + * mb_cache_destroy() + * + * Shrinks the cache to its minimum possible size (hopefully 0 entries), + * and then destroys it. If this was the last mbcache, un-registers the + * mbcache from kernel memory management. + */ +void +mb_cache_destroy(struct mb_cache *cache) +{ + LIST_HEAD(free_list); + struct list_head *l; + int n; + + spin_lock(&mb_cache_spinlock); + l = mb_cache_lru_list.prev; + while (l != &mb_cache_lru_list) { + struct mb_cache_entry *ce = + list_entry(l, struct mb_cache_entry, e_lru_list); + l = l->prev; + if (ce->e_cache == cache) { + list_move(&ce->e_lru_list, &free_list); + if (__mb_cache_entry_is_linked(ce)) + __mb_cache_entry_unlink(ce); + } + } + list_del(&cache->c_cache_list); + if (list_empty(&mb_cache_list) && mb_shrinker) { + remove_shrinker(mb_shrinker); + mb_shrinker = 0; + } + spin_unlock(&mb_cache_spinlock); + + l = free_list.prev; + while (l != &free_list) { + struct mb_cache_entry *ce = + list_entry(l, struct mb_cache_entry, e_lru_list); + l = l->prev; + __mb_cache_entry_forget(ce); + } + + if (atomic_read(&cache->c_entry_count) > 0) { + mb_error("cache %s: %d orphaned entries", + cache->c_name, + atomic_read(&cache->c_entry_count)); + } + + kmem_cache_destroy(cache->c_entry_cache); + + for (n=0; n < mb_cache_indexes(cache); n++) + kfree(cache->c_indexes_hash[n]); + kfree(cache->c_block_hash); + + kfree(cache); +} + + +/* + * mb_cache_entry_alloc() + * + * Allocates a new cache entry. The new entry will not be valid initially, + * and thus cannot be looked up yet. It should be filled with data, and + * then inserted into the cache using mb_cache_entry_insert(). Returns NULL + * if no more memory was available. + */ +struct mb_cache_entry * +mb_cache_entry_alloc(struct mb_cache *cache) +{ + struct mb_cache_entry *ce; + + atomic_inc(&cache->c_entry_count); + ce = kmem_cache_alloc(cache->c_entry_cache, GFP_KERNEL); + if (ce) { + INIT_LIST_HEAD(&ce->e_lru_list); + INIT_LIST_HEAD(&ce->e_block_list); + ce->e_cache = cache; + atomic_set(&ce->e_used, 1); + } + return ce; +} + + +/* + * mb_cache_entry_insert() + * + * Inserts an entry that was allocated using mb_cache_entry_alloc() into + * the cache. After this, the cache entry can be looked up, but is not yet + * in the lru list as the caller still holds a handle to it. Returns 0 on + * success, or -EBUSY if a cache entry for that device + inode exists + * already (this may happen after a failed lookup, but when another process + * has inserted the same cache entry in the meantime). + * + * @bdev: device the cache entry belongs to + * @block: block number + * @keys: array of additional keys. There must be indexes_count entries + * in the array (as specified when creating the cache). + */ +int +mb_cache_entry_insert(struct mb_cache_entry *ce, struct block_device *bdev, + sector_t block, unsigned int keys[]) +{ + struct mb_cache *cache = ce->e_cache; + unsigned int bucket; + struct list_head *l; + int error = -EBUSY, n; + + bucket = hash_long((unsigned long)bdev + (block & 0xffffffff), + cache->c_bucket_bits); + spin_lock(&mb_cache_spinlock); + list_for_each_prev(l, &cache->c_block_hash[bucket]) { + struct mb_cache_entry *ce = + list_entry(l, struct mb_cache_entry, e_block_list); + if (ce->e_bdev == bdev && ce->e_block == block) + goto out; + } + mb_assert(!__mb_cache_entry_is_linked(ce)); + ce->e_bdev = bdev; + ce->e_block = block; + for (n=0; ne_indexes[n].o_key = keys[n]; + __mb_cache_entry_link(ce); +out: + spin_unlock(&mb_cache_spinlock); + return error; +} + + +/* + * mb_cache_entry_release() + * + * Release a handle to a cache entry. When the last handle to a cache entry + * is released it is either freed (if it is invalid) or otherwise inserted + * in to the lru list. + */ +void +mb_cache_entry_release(struct mb_cache_entry *ce) +{ + spin_lock(&mb_cache_spinlock); + __mb_cache_entry_release_unlock(ce); +} + + +/* + * mb_cache_entry_takeout() + * + * Take a cache entry out of the cache, making it invalid. The entry can later + * be re-inserted using mb_cache_entry_insert(), or released using + * mb_cache_entry_release(). + */ +void +mb_cache_entry_takeout(struct mb_cache_entry *ce) +{ + spin_lock(&mb_cache_spinlock); + mb_assert(!__mb_cache_entry_in_lru(ce)); + if (__mb_cache_entry_is_linked(ce)) + __mb_cache_entry_unlink(ce); + spin_unlock(&mb_cache_spinlock); +} + + +/* + * mb_cache_entry_free() + * + * This is equivalent to the sequence mb_cache_entry_takeout() -- + * mb_cache_entry_release(). + */ +void +mb_cache_entry_free(struct mb_cache_entry *ce) +{ + spin_lock(&mb_cache_spinlock); + mb_assert(!__mb_cache_entry_in_lru(ce)); + if (__mb_cache_entry_is_linked(ce)) + __mb_cache_entry_unlink(ce); + __mb_cache_entry_release_unlock(ce); +} + + +/* + * mb_cache_entry_dup() + * + * Duplicate a handle to a cache entry (does not duplicate the cache entry + * itself). After the call, both the old and the new handle must be released. + */ +struct mb_cache_entry * +mb_cache_entry_dup(struct mb_cache_entry *ce) +{ + atomic_inc(&ce->e_used); + return ce; +} + + +/* + * mb_cache_entry_get() + * + * Get a cache entry by device / block number. (There can only be one entry + * in the cache per device and block.) Returns NULL if no such cache entry + * exists. + */ +struct mb_cache_entry * +mb_cache_entry_get(struct mb_cache *cache, struct block_device *bdev, + sector_t block) +{ + unsigned int bucket; + struct list_head *l; + struct mb_cache_entry *ce; + + bucket = hash_long((unsigned long)bdev + (block & 0xffffffff), + cache->c_bucket_bits); + spin_lock(&mb_cache_spinlock); + list_for_each(l, &cache->c_block_hash[bucket]) { + ce = list_entry(l, struct mb_cache_entry, e_block_list); + if (ce->e_bdev == bdev && ce->e_block == block) { + ce = __mb_cache_entry_read(ce); + goto cleanup; + } + } + ce = NULL; + +cleanup: + spin_unlock(&mb_cache_spinlock); + return ce; +} + +#if !defined(MB_CACHE_INDEXES_COUNT) || (MB_CACHE_INDEXES_COUNT > 0) + +static struct mb_cache_entry * +__mb_cache_entry_find(struct list_head *l, struct list_head *head, + int index, struct block_device *bdev, unsigned int key) +{ + while (l != head) { + struct mb_cache_entry *ce = + list_entry(l, struct mb_cache_entry, + e_indexes[index].o_list); + if (ce->e_bdev == bdev && + ce->e_indexes[index].o_key == key) { + ce = __mb_cache_entry_read(ce); + if (ce) + return ce; + } + l = l->next; + } + return NULL; +} + + +/* + * mb_cache_entry_find_first() + * + * Find the first cache entry on a given device with a certain key in + * an additional index. Additonal matches can be found with + * mb_cache_entry_find_next(). Returns NULL if no match was found. + * + * @cache: the cache to search + * @index: the number of the additonal index to search (0<=indexc_bucket_bits); + struct list_head *l; + struct mb_cache_entry *ce; + + mb_assert(index < mb_cache_indexes(cache)); + spin_lock(&mb_cache_spinlock); + l = cache->c_indexes_hash[index][bucket].next; + ce = __mb_cache_entry_find(l, &cache->c_indexes_hash[index][bucket], + index, bdev, key); + spin_unlock(&mb_cache_spinlock); + return ce; +} + + +/* + * mb_cache_entry_find_next() + * + * Find the next cache entry on a given device with a certain key in an + * additional index. Returns NULL if no match could be found. The previous + * entry is atomatically released, so that mb_cache_entry_find_next() can + * be called like this: + * + * entry = mb_cache_entry_find_first(); + * while (entry) { + * ... + * entry = mb_cache_entry_find_next(entry, ...); + * } + * + * @prev: The previous match + * @index: the number of the additonal index to search (0<=indexe_cache; + unsigned int bucket = hash_long(key, cache->c_bucket_bits); + struct list_head *l; + struct mb_cache_entry *ce; + + mb_assert(index < mb_cache_indexes(cache)); + spin_lock(&mb_cache_spinlock); + l = prev->e_indexes[index].o_list.next; + ce = __mb_cache_entry_find(l, &cache->c_indexes_hash[index][bucket], + index, bdev, key); + __mb_cache_entry_release_unlock(prev); + return ce; +} + +#endif /* !defined(MB_CACHE_INDEXES_COUNT) || (MB_CACHE_INDEXES_COUNT > 0) */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/namei.c linux-2.5-bk/fs/namei.c --- linux-2.5.45/fs/namei.c Wed Oct 30 16:42:26 2002 +++ linux-2.5-bk/fs/namei.c Thu Oct 31 22:12:58 2002 @@ -1279,8 +1279,9 @@ /* Negative dentry, just create the file */ if (!dentry->d_inode) { - error = vfs_create(dir->d_inode, dentry, - mode & ~current->fs->umask); + if (!IS_POSIXACL(dir->d_inode)) + mode &= ~current->fs->umask; + error = vfs_create(dir->d_inode, dentry, mode); up(&dir->d_inode->i_sem); dput(nd->dentry); nd->dentry = dentry; @@ -1442,7 +1443,8 @@ dentry = lookup_create(&nd, 0); error = PTR_ERR(dentry); - mode &= ~current->fs->umask; + if (!IS_POSIXACL(nd.dentry->d_inode)) + mode &= ~current->fs->umask; if (!IS_ERR(dentry)) { switch (mode & S_IFMT) { case 0: case S_IFREG: @@ -1508,8 +1510,9 @@ dentry = lookup_create(&nd, 1); error = PTR_ERR(dentry); if (!IS_ERR(dentry)) { - error = vfs_mkdir(nd.dentry->d_inode, dentry, - mode & ~current->fs->umask); + if (!IS_POSIXACL(nd.dentry->d_inode)) + mode &= ~current->fs->umask; + error = vfs_mkdir(nd.dentry->d_inode, dentry, mode); dput(dentry); } up(&nd.dentry->d_inode->i_sem); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/partitions/check.c linux-2.5-bk/fs/partitions/check.c --- linux-2.5.45/fs/partitions/check.c Wed Oct 30 16:43:41 2002 +++ linux-2.5-bk/fs/partitions/check.c Thu Oct 31 22:13:00 2002 @@ -277,231 +277,146 @@ #endif } -static ssize_t part_dev_read(struct device *dev, - char *page, size_t count, loff_t off) + +/* + * sysfs bindings for partitions + */ + +struct part_attribute { + struct attribute attr; + ssize_t (*show)(struct hd_struct *,char *,size_t,loff_t); +}; + +static ssize_t part_attr_show(struct kobject * kobj, struct attribute * attr, + char * page, size_t count, loff_t off) +{ + struct hd_struct * p = container_of(kobj,struct hd_struct,kobj); + struct part_attribute * part_attr = container_of(attr,struct part_attribute,attr); + ssize_t ret = 0; + if (part_attr->show) + part_attr->show(p,page,count,off); + return ret; +} + +static struct sysfs_ops part_sysfs_ops = { + .show part_attr_show, +}; + +static ssize_t part_dev_read(struct hd_struct * p, + char *page, size_t count, loff_t off) { - struct gendisk *disk = dev->parent->driver_data; - struct hd_struct *p = dev->driver_data; + struct gendisk *disk = container_of(p->kobj.parent,struct gendisk,kobj); int part = p - disk->part + 1; dev_t base = MKDEV(disk->major, disk->first_minor); return off ? 0 : sprintf(page, "%04x\n",base + part); } -static ssize_t part_start_read(struct device *dev, - char *page, size_t count, loff_t off) +static ssize_t part_start_read(struct hd_struct * p, + char *page, size_t count, loff_t off) { - struct hd_struct *p = dev->driver_data; return off ? 0 : sprintf(page, "%llu\n",(unsigned long long)p->start_sect); } -static ssize_t part_size_read(struct device *dev, - char *page, size_t count, loff_t off) +static ssize_t part_size_read(struct hd_struct * p, + char *page, size_t count, loff_t off) { - struct hd_struct *p = dev->driver_data; return off ? 0 : sprintf(page, "%llu\n",(unsigned long long)p->nr_sects); } -static ssize_t part_stat_read(struct device *dev, - char *page, size_t count, loff_t off) +static ssize_t part_stat_read(struct hd_struct * p, + char *page, size_t count, loff_t off) { - struct hd_struct *p = dev->driver_data; return off ? 0 : sprintf(page, "%8u %8llu %8u %8llu\n", p->reads, (u64)p->read_sectors, p->writes, (u64)p->write_sectors); } -static struct device_attribute part_attr_dev = { +static struct part_attribute part_attr_dev = { .attr = {.name = "dev", .mode = S_IRUGO }, .show = part_dev_read }; -static struct device_attribute part_attr_start = { +static struct part_attribute part_attr_start = { .attr = {.name = "start", .mode = S_IRUGO }, .show = part_start_read }; -static struct device_attribute part_attr_size = { +static struct part_attribute part_attr_size = { .attr = {.name = "size", .mode = S_IRUGO }, .show = part_size_read }; -static struct device_attribute part_attr_stat = { +static struct part_attribute part_attr_stat = { .attr = {.name = "stat", .mode = S_IRUGO }, .show = part_stat_read }; +static struct attribute * default_attrs[] = { + &part_attr_dev.attr, + &part_attr_start.attr, + &part_attr_size.attr, + &part_attr_stat.attr, + NULL, +}; + +extern struct subsystem block_subsys; + +static struct subsystem part_subsys = { + .parent = &block_subsys, + .default_attrs = default_attrs, + .sysfs_ops = &part_sysfs_ops, +}; + +static int __init part_subsys_init(void) +{ + return subsystem_register(&part_subsys); +} + +__initcall(part_subsys_init); + void delete_partition(struct gendisk *disk, int part) { struct hd_struct *p = disk->part + part - 1; - struct device *dev; if (!p->nr_sects) return; p->start_sect = 0; p->nr_sects = 0; p->reads = p->writes = p->read_sectors = p->write_sectors = 0; devfs_unregister(p->de); - dev = p->hd_driverfs_dev; - p->hd_driverfs_dev = NULL; - if (dev) { - device_remove_file(dev, &part_attr_stat); - device_remove_file(dev, &part_attr_size); - device_remove_file(dev, &part_attr_start); - device_remove_file(dev, &part_attr_dev); - device_unregister(dev); - } -} - -static void part_release(struct device *dev) -{ - kfree(dev); + kobject_unregister(&p->kobj); } void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len) { struct hd_struct *p = disk->part + part - 1; - struct device *parent = &disk->disk_dev; - struct device *dev; p->start_sect = start; p->nr_sects = len; devfs_register_partition(disk, part); - dev = kmalloc(sizeof(struct device), GFP_KERNEL); - if (!dev) - return; - memset(dev, 0, sizeof(struct device)); - dev->parent = parent; - sprintf(dev->bus_id, "p%d", part); - dev->release = part_release; - dev->driver_data = p; - device_register(dev); - device_create_file(dev, &part_attr_dev); - device_create_file(dev, &part_attr_start); - device_create_file(dev, &part_attr_size); - device_create_file(dev, &part_attr_stat); - p->hd_driverfs_dev = dev; + kobject_init(&p->kobj); + snprintf(p->kobj.name,KOBJ_NAME_LEN,"%s%d",disk->disk_name,part); + p->kobj.parent = &disk->kobj; + p->kobj.subsys = &part_subsys; + kobject_register(&p->kobj); } -static ssize_t disk_dev_read(struct device *dev, - char *page, size_t count, loff_t off) -{ - struct gendisk *disk = dev->driver_data; - dev_t base = MKDEV(disk->major, disk->first_minor); - return off ? 0 : sprintf(page, "%04x\n",base); -} -static ssize_t disk_range_read(struct device *dev, - char *page, size_t count, loff_t off) -{ - struct gendisk *disk = dev->driver_data; - return off ? 0 : sprintf(page, "%d\n",disk->minors); -} -static ssize_t disk_size_read(struct device *dev, - char *page, size_t count, loff_t off) -{ - struct gendisk *disk = dev->driver_data; - return off ? 0 : sprintf(page, "%llu\n",(unsigned long long)get_capacity(disk)); -} -static inline unsigned MSEC(unsigned x) -{ - return x * 1000 / HZ; -} -static ssize_t disk_stat_read(struct device *dev, - char *page, size_t count, loff_t off) -{ - struct gendisk *disk = dev->driver_data; - disk_round_stats(disk); - return off ? 0 : sprintf(page, - "%8u %8u %8llu %8u " - "%8u %8u %8llu %8u " - "%8u %8u %8u" - "\n", - disk->reads, disk->read_merges, (u64)disk->read_sectors, - MSEC(disk->read_ticks), - disk->writes, disk->write_merges, (u64)disk->write_sectors, - MSEC(disk->write_ticks), - disk->in_flight, MSEC(disk->io_ticks), - MSEC(disk->time_in_queue)); -} -static struct device_attribute disk_attr_dev = { - .attr = {.name = "dev", .mode = S_IRUGO }, - .show = disk_dev_read -}; -static struct device_attribute disk_attr_range = { - .attr = {.name = "range", .mode = S_IRUGO }, - .show = disk_range_read -}; -static struct device_attribute disk_attr_size = { - .attr = {.name = "size", .mode = S_IRUGO }, - .show = disk_size_read -}; -static struct device_attribute disk_attr_stat = { - .attr = {.name = "stat", .mode = S_IRUGO }, - .show = disk_stat_read -}; - -static void disk_driverfs_symlinks(struct gendisk *disk) -{ - struct device *target = disk->driverfs_dev; - struct device *dev = &disk->disk_dev; - struct device *p; - char *path; - char *s; - int length; - int depth; - - if (!target) - return; - - get_device(target); - - length = get_devpath_length(target); - length += strlen(".."); - - if (length > PATH_MAX) - return; - - if (!(path = kmalloc(length,GFP_KERNEL))) - return; - memset(path,0,length); - - /* our relative position */ - strcpy(path,".."); - - fill_devpath(target, path, length); - driverfs_create_symlink(&dev->dir, "device", path); - kfree(path); - - for (p = target, depth = 0; p; p = p->parent, depth++) - ; - length = get_devpath_length(dev); - length += 3 * depth - 1; - - if (length > PATH_MAX) - return; - - if (!(path = kmalloc(length,GFP_KERNEL))) - return; - memset(path,0,length); - for (s = path; depth--; s += 3) - strcpy(s, "../"); - - fill_devpath(dev, path, length); - driverfs_create_symlink(&target->dir, "block", path); - kfree(path); +static void disk_sysfs_symlinks(struct gendisk *disk) +{ + struct device *target = get_device(disk->driverfs_dev); + if (target) { + sysfs_create_link(&disk->kobj,&target->kobj,"device"); + sysfs_create_link(&target->kobj,&disk->kobj,"block"); + } } /* Not exported, helper to add_disk(). */ void register_disk(struct gendisk *disk) { - struct device *dev = &disk->disk_dev; struct parsed_partitions *state; struct block_device *bdev; char *s; int j; - strcpy(dev->bus_id, disk->disk_name); + strncpy(disk->kobj.name,disk->disk_name,KOBJ_NAME_LEN); /* ewww... some of these buggers have / in name... */ - s = strchr(dev->bus_id, '/'); + s = strchr(disk->kobj.name, '/'); if (s) *s = '!'; - device_add(dev); - device_create_file(dev, &disk_attr_dev); - device_create_file(dev, &disk_attr_range); - device_create_file(dev, &disk_attr_size); - device_create_file(dev, &disk_attr_stat); - disk_driverfs_symlinks(disk); + kobject_register(&disk->kobj); + disk_sysfs_symlinks(disk); if (disk->flags & GENHD_FL_CD) devfs_create_cdrom(disk); @@ -620,16 +535,12 @@ disk->time_in_queue = 0; disk->stamp = disk->stamp_idle = 0; devfs_remove_partitions(disk); - device_remove_file(&disk->disk_dev, &disk_attr_dev); - device_remove_file(&disk->disk_dev, &disk_attr_range); - device_remove_file(&disk->disk_dev, &disk_attr_size); - device_remove_file(&disk->disk_dev, &disk_attr_stat); - driverfs_remove_file(&disk->disk_dev.dir, "device"); + kobject_unregister(&disk->kobj); + sysfs_remove_link(&disk->kobj, "device"); if (disk->driverfs_dev) { - driverfs_remove_file(&disk->driverfs_dev->dir, "block"); + sysfs_remove_link(&disk->driverfs_dev->kobj, "block"); put_device(disk->driverfs_dev); } - device_del(&disk->disk_dev); } struct dev_name { @@ -680,3 +591,4 @@ return dname->name; } + diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/posix_acl.c linux-2.5-bk/fs/posix_acl.c --- linux-2.5.45/fs/posix_acl.c Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/fs/posix_acl.c Thu Oct 31 22:12:58 2002 @@ -0,0 +1,423 @@ +/* + * linux/fs/posix_acl.c + * + * Copyright (C) 2002 by Andreas Gruenbacher + * + * Fixes from William Schumacher incorporated on 15 March 2001. + * (Reported by Charles Bertsch, ). + */ + +/* + * This file contains generic functions for manipulating + * POSIX 1003.1e draft standard 17 ACLs. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +EXPORT_SYMBOL(posix_acl_alloc); +EXPORT_SYMBOL(posix_acl_clone); +EXPORT_SYMBOL(posix_acl_valid); +EXPORT_SYMBOL(posix_acl_equiv_mode); +EXPORT_SYMBOL(posix_acl_from_mode); +EXPORT_SYMBOL(posix_acl_create_masq); +EXPORT_SYMBOL(posix_acl_chmod_masq); +EXPORT_SYMBOL(posix_acl_masq_nfs_mode); +EXPORT_SYMBOL(posix_acl_permission); + +/* + * Allocate a new ACL with the specified number of entries. + */ +struct posix_acl * +posix_acl_alloc(int count, int flags) +{ + const size_t size = sizeof(struct posix_acl) + + count * sizeof(struct posix_acl_entry); + struct posix_acl *acl = kmalloc(size, flags); + if (acl) { + atomic_set(&acl->a_refcount, 1); + acl->a_count = count; + } + return acl; +} + +/* + * Clone an ACL. + */ +struct posix_acl * +posix_acl_clone(const struct posix_acl *acl, int flags) +{ + struct posix_acl *clone = NULL; + + if (acl) { + int size = sizeof(struct posix_acl) + acl->a_count * + sizeof(struct posix_acl_entry); + clone = kmalloc(size, flags); + if (clone) { + memcpy(clone, acl, size); + atomic_set(&clone->a_refcount, 1); + } + } + return clone; +} + +/* + * Check if an acl is valid. Returns 0 if it is, or -E... otherwise. + */ +int +posix_acl_valid(const struct posix_acl *acl) +{ + const struct posix_acl_entry *pa, *pe; + int state = ACL_USER_OBJ; + unsigned int id = 0; /* keep gcc happy */ + int needs_mask = 0; + + FOREACH_ACL_ENTRY(pa, acl, pe) { + if (pa->e_perm & ~(ACL_READ|ACL_WRITE|ACL_EXECUTE)) + return -EINVAL; + switch (pa->e_tag) { + case ACL_USER_OBJ: + if (state == ACL_USER_OBJ) { + id = 0; + state = ACL_USER; + break; + } + return -EINVAL; + + case ACL_USER: + if (state != ACL_USER) + return -EINVAL; + if (pa->e_id == ACL_UNDEFINED_ID || + pa->e_id < id) + return -EINVAL; + id = pa->e_id + 1; + needs_mask = 1; + break; + + case ACL_GROUP_OBJ: + if (state == ACL_USER) { + id = 0; + state = ACL_GROUP; + break; + } + return -EINVAL; + + case ACL_GROUP: + if (state != ACL_GROUP) + return -EINVAL; + if (pa->e_id == ACL_UNDEFINED_ID || + pa->e_id < id) + return -EINVAL; + id = pa->e_id + 1; + needs_mask = 1; + break; + + case ACL_MASK: + if (state != ACL_GROUP) + return -EINVAL; + state = ACL_OTHER; + break; + + case ACL_OTHER: + if (state == ACL_OTHER || + (state == ACL_GROUP && !needs_mask)) { + state = 0; + break; + } + return -EINVAL; + + default: + return -EINVAL; + } + } + if (state == 0) + return 0; + return -EINVAL; +} + +/* + * Returns 0 if the acl can be exactly represented in the traditional + * file mode permission bits, or else 1. Returns -E... on error. + */ +int +posix_acl_equiv_mode(const struct posix_acl *acl, mode_t *mode_p) +{ + const struct posix_acl_entry *pa, *pe; + mode_t mode = 0; + int not_equiv = 0; + + FOREACH_ACL_ENTRY(pa, acl, pe) { + switch (pa->e_tag) { + case ACL_USER_OBJ: + mode |= (pa->e_perm & S_IRWXO) << 6; + break; + case ACL_GROUP_OBJ: + mode |= (pa->e_perm & S_IRWXO) << 3; + break; + case ACL_OTHER: + mode |= pa->e_perm & S_IRWXO; + break; + case ACL_MASK: + mode = (mode & ~S_IRWXG) | + ((pa->e_perm & S_IRWXO) << 3); + not_equiv = 1; + break; + case ACL_USER: + case ACL_GROUP: + not_equiv = 1; + break; + default: + return -EINVAL; + } + } + if (mode_p) + *mode_p = (*mode_p & ~S_IRWXUGO) | mode; + return not_equiv; +} + +/* + * Create an ACL representing the file mode permission bits of an inode. + */ +struct posix_acl * +posix_acl_from_mode(mode_t mode, int flags) +{ + struct posix_acl *acl = posix_acl_alloc(3, flags); + if (!acl) + return ERR_PTR(-ENOMEM); + + acl->a_entries[0].e_tag = ACL_USER_OBJ; + acl->a_entries[0].e_id = ACL_UNDEFINED_ID; + acl->a_entries[0].e_perm = (mode & S_IRWXU) >> 6; + + acl->a_entries[1].e_tag = ACL_GROUP_OBJ; + acl->a_entries[1].e_id = ACL_UNDEFINED_ID; + acl->a_entries[1].e_perm = (mode & S_IRWXG) >> 3; + + acl->a_entries[2].e_tag = ACL_OTHER; + acl->a_entries[2].e_id = ACL_UNDEFINED_ID; + acl->a_entries[2].e_perm = (mode & S_IRWXO); + return acl; +} + +/* + * Return 0 if current is granted want access to the inode + * by the acl. Returns -E... otherwise. + */ +int +posix_acl_permission(struct inode *inode, const struct posix_acl *acl, int want) +{ + const struct posix_acl_entry *pa, *pe, *mask_obj; + int found = 0; + + FOREACH_ACL_ENTRY(pa, acl, pe) { + switch(pa->e_tag) { + case ACL_USER_OBJ: + /* (May have been checked already) */ + if (inode->i_uid == current->fsuid) + goto check_perm; + break; + case ACL_USER: + if (pa->e_id == current->fsuid) + goto mask; + break; + case ACL_GROUP_OBJ: + if (in_group_p(inode->i_gid)) { + found = 1; + if ((pa->e_perm & want) == want) + goto mask; + } + break; + case ACL_GROUP: + if (in_group_p(pa->e_id)) { + found = 1; + if ((pa->e_perm & want) == want) + goto mask; + } + break; + case ACL_MASK: + break; + case ACL_OTHER: + if (found) + return -EACCES; + else + goto check_perm; + default: + return -EIO; + } + } + return -EIO; + +mask: + for (mask_obj = pa+1; mask_obj != pe; mask_obj++) { + if (mask_obj->e_tag == ACL_MASK) { + if ((pa->e_perm & mask_obj->e_perm & want) == want) + return 0; + return -EACCES; + } + } + +check_perm: + if ((pa->e_perm & want) == want) + return 0; + return -EACCES; +} + +/* + * Modify acl when creating a new inode. The caller must ensure the acl is + * only referenced once. + * + * mode_p initially must contain the mode parameter to the open() / creat() + * system calls. All permissions that are not granted by the acl are removed. + * The permissions in the acl are changed to reflect the mode_p parameter. + */ +int +posix_acl_create_masq(struct posix_acl *acl, mode_t *mode_p) +{ + struct posix_acl_entry *pa, *pe; + struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL; + mode_t mode = *mode_p; + int not_equiv = 0; + + /* assert(atomic_read(acl->a_refcount) == 1); */ + + FOREACH_ACL_ENTRY(pa, acl, pe) { + switch(pa->e_tag) { + case ACL_USER_OBJ: + pa->e_perm &= (mode >> 6) | ~S_IRWXO; + mode &= (pa->e_perm << 6) | ~S_IRWXU; + break; + + case ACL_USER: + case ACL_GROUP: + not_equiv = 1; + break; + + case ACL_GROUP_OBJ: + group_obj = pa; + break; + + case ACL_OTHER: + pa->e_perm &= mode | ~S_IRWXO; + mode &= pa->e_perm | ~S_IRWXO; + break; + + case ACL_MASK: + mask_obj = pa; + not_equiv = 1; + break; + + default: + return -EIO; + } + } + + if (mask_obj) { + mask_obj->e_perm &= (mode >> 3) | ~S_IRWXO; + mode &= (mask_obj->e_perm << 3) | ~S_IRWXG; + } else { + if (!group_obj) + return -EIO; + group_obj->e_perm &= (mode >> 3) | ~S_IRWXO; + mode &= (group_obj->e_perm << 3) | ~S_IRWXG; + } + + *mode_p = (*mode_p & ~S_IRWXUGO) | mode; + return not_equiv; +} + +/* + * Modify the ACL for the chmod syscall. + */ +int +posix_acl_chmod_masq(struct posix_acl *acl, mode_t mode) +{ + struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL; + struct posix_acl_entry *pa, *pe; + + /* assert(atomic_read(acl->a_refcount) == 1); */ + + FOREACH_ACL_ENTRY(pa, acl, pe) { + switch(pa->e_tag) { + case ACL_USER_OBJ: + pa->e_perm = (mode & S_IRWXU) >> 6; + break; + + case ACL_USER: + case ACL_GROUP: + break; + + case ACL_GROUP_OBJ: + group_obj = pa; + break; + + case ACL_MASK: + mask_obj = pa; + break; + + case ACL_OTHER: + pa->e_perm = (mode & S_IRWXO); + break; + + default: + return -EIO; + } + } + + if (mask_obj) { + mask_obj->e_perm = (mode & S_IRWXG) >> 3; + } else { + if (!group_obj) + return -EIO; + group_obj->e_perm = (mode & S_IRWXG) >> 3; + } + + return 0; +} + +/* + * Adjust the mode parameter so that NFSv2 grants nobody permissions + * that may not be granted by the ACL. This is necessary because NFSv2 + * may compute access permissions on the client side, and may serve cached + * data whenever it assumes access would be granted. Since ACLs may also + * be used to deny access to specific users, the minimal permissions + * for secure operation over NFSv2 are very restrictive. Permissions + * granted to users via Access Control Lists will not be effective over + * NFSv2. + * + * Privilege escalation can only happen for read operations, as writes are + * always carried out on the NFS server, where the proper access checks are + * implemented. + */ +int +posix_acl_masq_nfs_mode(struct posix_acl *acl, mode_t *mode_p) +{ + struct posix_acl_entry *pa, *pe; int min_perm = S_IRWXO; + + FOREACH_ACL_ENTRY(pa, acl, pe) { + switch(pa->e_tag) { + case ACL_USER_OBJ: + break; + + case ACL_USER: + case ACL_GROUP_OBJ: + case ACL_GROUP: + case ACL_MASK: + case ACL_OTHER: + min_perm &= pa->e_perm; + break; + + default: + return -EIO; + } + } + *mode_p = (*mode_p & ~(S_IRWXG|S_IRWXO)) | (min_perm << 3) | min_perm; + + return 0; +} diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/proc/array.c linux-2.5-bk/fs/proc/array.c --- linux-2.5.45/fs/proc/array.c Wed Oct 30 16:43:01 2002 +++ linux-2.5-bk/fs/proc/array.c Thu Oct 31 22:13:00 2002 @@ -64,6 +64,7 @@ #include #include #include +#include #include #include #include diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/proc/base.c linux-2.5-bk/fs/proc/base.c --- linux-2.5.45/fs/proc/base.c Wed Oct 30 16:43:03 2002 +++ linux-2.5-bk/fs/proc/base.c Thu Oct 31 22:13:00 2002 @@ -28,6 +28,7 @@ #include #include #include +#include /* * For hysterical raisins we keep the same inumbers as in the old procfs. @@ -54,6 +55,7 @@ PROC_PID_MAPS, PROC_PID_CPU, PROC_PID_MOUNTS, + PROC_PID_WCHAN, PROC_PID_FD_DIR = 0x8000, /* 0x8000-0xffff */ }; @@ -81,6 +83,9 @@ E(PROC_PID_ROOT, "root", S_IFLNK|S_IRWXUGO), E(PROC_PID_EXE, "exe", S_IFLNK|S_IRWXUGO), E(PROC_PID_MOUNTS, "mounts", S_IFREG|S_IRUGO), +#ifdef CONFIG_KALLSYMS + E(PROC_PID_WCHAN, "wchan", S_IFREG|S_IRUGO), +#endif {0,0,NULL,0} }; #undef E @@ -245,6 +250,28 @@ return res; } +#ifdef CONFIG_KALLSYMS +/* + * Provides a wchan file via kallsyms in a proper one-value-per-file format. + * Returns the resolved symbol. If that fails, simply return the address. + */ +static int proc_pid_wchan(struct task_struct *task, char *buffer) +{ + const char *sym_name, *ignore; + unsigned long wchan, dummy; + + wchan = get_wchan(task); + + if (!kallsyms_address_to_symbol(wchan, &ignore, &dummy, &dummy, + &ignore, &dummy, &dummy, &sym_name, + &dummy, &dummy)) { + return sprintf(buffer, "%lu", wchan); + } + + return sprintf(buffer, "%s", sym_name); +} +#endif + /************************************************************************/ /* Here the fs part begins */ /************************************************************************/ @@ -1016,6 +1043,12 @@ case PROC_PID_MOUNTS: inode->i_fop = &proc_mounts_operations; break; +#ifdef CONFIG_KALLSYMS + case PROC_PID_WCHAN: + inode->i_fop = &proc_info_file_operations; + ei->op.proc_read = proc_pid_wchan; + break; +#endif default: printk("procfs: impossible type (%d)",p->type); iput(inode); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/proc/proc_misc.c linux-2.5-bk/fs/proc/proc_misc.c --- linux-2.5.45/fs/proc/proc_misc.c Wed Oct 30 16:41:56 2002 +++ linux-2.5-bk/fs/proc/proc_misc.c Thu Oct 31 22:13:00 2002 @@ -360,14 +360,14 @@ int j; if(!cpu_online(i)) continue; - user += kstat.per_cpu_user[i]; - nice += kstat.per_cpu_nice[i]; - system += kstat.per_cpu_system[i]; - idle += kstat.per_cpu_idle[i]; - iowait += kstat.per_cpu_iowait[i]; + user += kstat_cpu(i).cpustat.user; + nice += kstat_cpu(i).cpustat.nice; + system += kstat_cpu(i).cpustat.system; + idle += kstat_cpu(i).cpustat.idle; + iowait += kstat_cpu(i).cpustat.iowait; #if !defined(CONFIG_ARCH_S390) for (j = 0 ; j < NR_IRQS ; j++) - sum += kstat.irqs[i][j]; + sum += kstat_cpu(i).irqs[j]; #endif } @@ -381,11 +381,11 @@ if (!cpu_online(i)) continue; len += sprintf(page + len, "cpu%d %u %u %u %u %u\n", i, - jiffies_to_clock_t(kstat.per_cpu_user[i]), - jiffies_to_clock_t(kstat.per_cpu_nice[i]), - jiffies_to_clock_t(kstat.per_cpu_system[i]), - jiffies_to_clock_t(kstat.per_cpu_idle[i]), - jiffies_to_clock_t(kstat.per_cpu_iowait[i])); + jiffies_to_clock_t(kstat_cpu(i).cpustat.user), + jiffies_to_clock_t(kstat_cpu(i).cpustat.nice), + jiffies_to_clock_t(kstat_cpu(i).cpustat.system), + jiffies_to_clock_t(kstat_cpu(i).cpustat.idle), + jiffies_to_clock_t(kstat_cpu(i).cpustat.idle)); } len += sprintf(page + len, "intr %u", sum); @@ -398,18 +398,18 @@ for (major = 0; major < DK_MAX_MAJOR; major++) { for (disk = 0; disk < DK_MAX_DISK; disk++) { - int active = kstat.dk_drive[major][disk] + - kstat.dk_drive_rblk[major][disk] + - kstat.dk_drive_wblk[major][disk]; + int active = dkstat.drive[major][disk] + + dkstat.drive_rblk[major][disk] + + dkstat.drive_wblk[major][disk]; if (active) len += sprintf(page + len, "(%u,%u):(%u,%u,%u,%u,%u) ", major, disk, - kstat.dk_drive[major][disk], - kstat.dk_drive_rio[major][disk], - kstat.dk_drive_rblk[major][disk], - kstat.dk_drive_wio[major][disk], - kstat.dk_drive_wblk[major][disk] + dkstat.drive[major][disk], + dkstat.drive_rio[major][disk], + dkstat.drive_rblk[major][disk], + dkstat.drive_wio[major][disk], + dkstat.drive_wblk[major][disk] ); } } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/fs/xattr_acl.c linux-2.5-bk/fs/xattr_acl.c --- linux-2.5.45/fs/xattr_acl.c Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/fs/xattr_acl.c Thu Oct 31 22:12:58 2002 @@ -0,0 +1,99 @@ +/* + * linux/fs/xattr_acl.c + * + * Almost all from linux/fs/ext2/acl.c: + * Copyright (C) 2001 by Andreas Gruenbacher, + */ + +#include +#include +#include +#include +#include + + +/* + * Convert from extended attribute to in-memory representation. + */ +struct posix_acl * +posix_acl_from_xattr(const void *value, size_t size) +{ + posix_acl_xattr_header *header = (posix_acl_xattr_header *)value; + posix_acl_xattr_entry *entry = (posix_acl_xattr_entry *)(header+1), *end; + int count; + struct posix_acl *acl; + struct posix_acl_entry *acl_e; + + if (!value) + return NULL; + if (size < sizeof(posix_acl_xattr_header)) + return ERR_PTR(-EINVAL); + if (header->a_version != cpu_to_le32(POSIX_ACL_XATTR_VERSION)) + return ERR_PTR(-EINVAL); + + count = posix_acl_xattr_count(size); + if (count < 0) + return ERR_PTR(-EINVAL); + if (count == 0) + return NULL; + + acl = posix_acl_alloc(count, GFP_KERNEL); + if (!acl) + return ERR_PTR(-ENOMEM); + acl_e = acl->a_entries; + + for (end = entry + count; entry != end; acl_e++, entry++) { + acl_e->e_tag = le16_to_cpu(entry->e_tag); + acl_e->e_perm = le16_to_cpu(entry->e_perm); + + switch(acl_e->e_tag) { + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + acl_e->e_id = ACL_UNDEFINED_ID; + break; + + case ACL_USER: + case ACL_GROUP: + acl_e->e_id = le32_to_cpu(entry->e_id); + break; + + default: + goto fail; + } + } + return acl; + +fail: + posix_acl_release(acl); + return ERR_PTR(-EINVAL); +} +EXPORT_SYMBOL (posix_acl_from_xattr); + +/* + * Convert from in-memory to extended attribute representation. + */ +int +posix_acl_to_xattr(const struct posix_acl *acl, void *buffer, size_t size) +{ + posix_acl_xattr_header *ext_acl = (posix_acl_xattr_header *)buffer; + posix_acl_xattr_entry *ext_entry = ext_acl->a_entries; + int real_size, n; + + real_size = posix_acl_xattr_size(acl->a_count); + if (!buffer) + return real_size; + if (real_size > size) + return -ERANGE; + + ext_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION); + + for (n=0; n < acl->a_count; n++, ext_entry++) { + ext_entry->e_tag = cpu_to_le16(acl->a_entries[n].e_tag); + ext_entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm); + ext_entry->e_id = cpu_to_le32(acl->a_entries[n].e_id); + } + return real_size; +} +EXPORT_SYMBOL (posix_acl_to_xattr); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/asm-alpha/poll.h linux-2.5-bk/include/asm-alpha/poll.h --- linux-2.5.45/include/asm-alpha/poll.h Wed Oct 30 16:42:26 2002 +++ linux-2.5-bk/include/asm-alpha/poll.h Thu Oct 31 22:13:01 2002 @@ -1,17 +1,18 @@ #ifndef __ALPHA_POLL_H #define __ALPHA_POLL_H -#define POLLIN 1 -#define POLLPRI 2 -#define POLLOUT 4 -#define POLLERR 8 -#define POLLHUP 16 -#define POLLNVAL 32 -#define POLLRDNORM 64 -#define POLLRDBAND 128 -#define POLLWRNORM 256 -#define POLLWRBAND 512 -#define POLLMSG 1024 +#define POLLIN (1 << 0) +#define POLLPRI (1 << 1) +#define POLLOUT (1 << 2) +#define POLLERR (1 << 3) +#define POLLHUP (1 << 4) +#define POLLNVAL (1 << 5) +#define POLLRDNORM (1 << 6) +#define POLLRDBAND (1 << 7) +#define POLLWRNORM (1 << 8) +#define POLLWRBAND (1 << 9) +#define POLLMSG (1 << 10) +#define POLLREMOVE (1 << 11) struct pollfd { int fd; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/asm-i386/mman.h linux-2.5-bk/include/asm-i386/mman.h --- linux-2.5.45/include/asm-i386/mman.h Wed Oct 30 16:41:56 2002 +++ linux-2.5-bk/include/asm-i386/mman.h Thu Oct 31 22:13:02 2002 @@ -18,6 +18,8 @@ #define MAP_EXECUTABLE 0x1000 /* mark it as an executable */ #define MAP_LOCKED 0x2000 /* pages are locked */ #define MAP_NORESERVE 0x4000 /* don't check for reservations */ +#define MAP_POPULATE 0x8000 /* populate (prefault) pagetables */ +#define MAP_NONBLOCK 0x10000 /* do not block on IO */ #define MS_ASYNC 1 /* sync memory asynchronously */ #define MS_INVALIDATE 2 /* invalidate the caches */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/asm-i386/processor.h linux-2.5-bk/include/asm-i386/processor.h --- linux-2.5.45/include/asm-i386/processor.h Wed Oct 30 16:41:39 2002 +++ linux-2.5-bk/include/asm-i386/processor.h Thu Oct 31 22:13:02 2002 @@ -270,7 +270,7 @@ /* This decides where the kernel will search for a free chunk of vm * space during mmap's. */ -#define TASK_UNMAPPED_BASE (TASK_SIZE / 3) +#define TASK_UNMAPPED_BASE (PAGE_ALIGN(TASK_SIZE / 3)) /* * Size of io_bitmap in longwords: 32 is ports 0-0x3ff. diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/asm-i386/unistd.h linux-2.5-bk/include/asm-i386/unistd.h --- linux-2.5.45/include/asm-i386/unistd.h Wed Oct 30 16:43:05 2002 +++ linux-2.5-bk/include/asm-i386/unistd.h Thu Oct 31 22:13:02 2002 @@ -261,7 +261,8 @@ #define __NR_sys_epoll_create 254 #define __NR_sys_epoll_ctl 255 #define __NR_sys_epoll_wait 256 - +#define __NR_remap_file_pages 257 + /* user-visible error numbers are in the range -1 - -124: see */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/asm-sparc/mman.h linux-2.5-bk/include/asm-sparc/mman.h --- linux-2.5.45/include/asm-sparc/mman.h Wed Oct 30 16:43:43 2002 +++ linux-2.5-bk/include/asm-sparc/mman.h Thu Oct 31 22:13:05 2002 @@ -32,6 +32,9 @@ #define MCL_CURRENT 0x2000 /* lock all currently mapped pages */ #define MCL_FUTURE 0x4000 /* lock all additions to address space */ +#define MAP_POPULATE 0x8000 /* populate (prefault) pagetables */ +#define MAP_NONBLOCK 0x10000 /* do not block on IO */ + /* XXX Need to add flags to SunOS's mctl, mlockall, and madvise system * XXX calls. */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/asm-sparc64/mman.h linux-2.5-bk/include/asm-sparc64/mman.h --- linux-2.5.45/include/asm-sparc64/mman.h Wed Oct 30 16:42:55 2002 +++ linux-2.5-bk/include/asm-sparc64/mman.h Thu Oct 31 22:13:05 2002 @@ -32,6 +32,9 @@ #define MCL_CURRENT 0x2000 /* lock all currently mapped pages */ #define MCL_FUTURE 0x4000 /* lock all additions to address space */ +#define MAP_POPULATE 0x8000 /* populate (prefault) pagetables */ +#define MAP_NONBLOCK 0x10000 /* do not block on IO */ + /* XXX Need to add flags to SunOS's mctl, mlockall, and madvise system * XXX calls. */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/asm-x86_64/mman.h linux-2.5-bk/include/asm-x86_64/mman.h --- linux-2.5.45/include/asm-x86_64/mman.h Wed Oct 30 16:41:56 2002 +++ linux-2.5-bk/include/asm-x86_64/mman.h Thu Oct 31 22:13:05 2002 @@ -19,6 +19,8 @@ #define MAP_EXECUTABLE 0x1000 /* mark it as an executable */ #define MAP_LOCKED 0x2000 /* pages are locked */ #define MAP_NORESERVE 0x4000 /* don't check for reservations */ +#define MAP_POPULATE 0x8000 /* populate (prefault) pagetables */ +#define MAP_NONBLOCK 0x10000 /* do not block on IO */ #define MS_ASYNC 1 /* sync memory asynchronously */ #define MS_INVALIDATE 2 /* invalidate the caches */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/linux/blkdev.h linux-2.5-bk/include/linux/blkdev.h --- linux-2.5.45/include/linux/blkdev.h Wed Oct 30 16:42:20 2002 +++ linux-2.5-bk/include/linux/blkdev.h Thu Oct 31 22:13:05 2002 @@ -11,6 +11,22 @@ #include +/* + * Disk stats ... + */ + +#define DK_MAX_MAJOR 16 +#define DK_MAX_DISK 16 + +struct disk_stat { + unsigned int drive[DK_MAX_MAJOR][DK_MAX_DISK]; + unsigned int drive_rio[DK_MAX_MAJOR][DK_MAX_DISK]; + unsigned int drive_wio[DK_MAX_MAJOR][DK_MAX_DISK]; + unsigned int drive_rblk[DK_MAX_MAJOR][DK_MAX_DISK]; + unsigned int drive_wblk[DK_MAX_MAJOR][DK_MAX_DISK]; +}; +extern struct disk_stat dkstat; + struct request_queue; typedef struct request_queue request_queue_t; struct elevator_s; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/linux/dcookies.h linux-2.5-bk/include/linux/dcookies.h --- linux-2.5.45/include/linux/dcookies.h Wed Oct 30 16:42:29 2002 +++ linux-2.5-bk/include/linux/dcookies.h Thu Oct 31 22:13:05 2002 @@ -44,7 +44,7 @@ * Returns 0 on success, with *cookie filled in */ int get_dcookie(struct dentry * dentry, struct vfsmount * vfsmnt, - u32 * cookie); + unsigned long * cookie); #else @@ -59,7 +59,7 @@ } static inline int get_dcookie(struct dentry * dentry, - struct vfsmount * vfsmnt, u32 * cookie) + struct vfsmount * vfsmnt, unsigned long * cookie) { return -ENOSYS; } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/linux/device.h linux-2.5-bk/include/linux/device.h --- linux-2.5.45/include/linux/device.h Wed Oct 30 16:43:40 2002 +++ linux-2.5-bk/include/linux/device.h Thu Oct 31 22:13:05 2002 @@ -28,7 +28,7 @@ #include #include #include -#include +#include #define DEVICE_NAME_SIZE 80 #define DEVICE_ID_SIZE 32 @@ -65,14 +65,13 @@ atomic_t refcount; u32 present; + struct subsystem subsys; + struct subsystem drvsubsys; + struct subsystem devsubsys; struct list_head node; struct list_head devices; struct list_head drivers; - struct driver_dir_entry dir; - struct driver_dir_entry device_dir; - struct driver_dir_entry driver_dir; - int (*match)(struct device * dev, struct device_driver * drv); struct device * (*add) (struct device * parent, char * bus_id); int (*hotplug) (struct device *dev, char **envp, @@ -119,12 +118,11 @@ atomic_t refcount; u32 present; + struct kobject kobj; struct list_head bus_list; struct list_head class_list; struct list_head devices; - struct driver_dir_entry dir; - int (*probe) (struct device * dev); int (*remove) (struct device * dev); void (*shutdown) (struct device * dev); @@ -177,14 +175,13 @@ u32 devnum; + struct subsystem subsys; + struct subsystem devsubsys; + struct subsystem drvsubsys; struct list_head node; struct list_head drivers; struct list_head intf_list; - struct driver_dir_entry dir; - struct driver_dir_entry driver_dir; - struct driver_dir_entry device_dir; - int (*add_device)(struct device *); void (*remove_device)(struct device *); int (*hotplug)(struct device *dev, char **envp, @@ -232,9 +229,9 @@ char * name; struct device_class * devclass; + struct kobject kobj; struct list_head node; struct list_head devices; - struct driver_dir_entry dir; u32 devnum; @@ -275,6 +272,7 @@ struct list_head intf_list; struct device * parent; + struct kobject kobj; char name[DEVICE_NAME_SIZE]; /* descriptive ascii string */ char bus_id[BUS_ID_SIZE]; /* position on parent bus */ @@ -285,8 +283,6 @@ * persists for the right amount of time */ struct bus_type * bus; /* type of bus device is on */ - struct driver_dir_entry dir; - struct device_driver *driver; /* which driver has allocated this device */ void *driver_data; /* data private to the driver */ @@ -437,6 +433,11 @@ extern int device_suspend(u32 state, u32 level); extern void device_resume(u32 level); extern void device_shutdown(void); + + +/* drivrs/base/firmware.c */ +extern int firmware_register(struct subsystem *); +extern void firmware_uregister(struct subsystem *); /* debugging and troubleshooting/diagnostic helpers. */ #ifdef DEBUG diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/linux/driverfs_fs.h linux-2.5-bk/include/linux/driverfs_fs.h --- linux-2.5.45/include/linux/driverfs_fs.h Wed Oct 30 16:42:57 2002 +++ linux-2.5-bk/include/linux/driverfs_fs.h Wed Dec 31 16:00:00 1969 @@ -1,68 +0,0 @@ -/* - * driverfs_fs.h - definitions for the device driver filesystem - * - * Copyright (c) 2001 Patrick Mochel - * - * 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 is a simple, ram-based filesystem, which allows kernel - * callbacks for read/write of files. - * - * Please see Documentation/filesystems/driverfs.txt for more information. - */ - -#ifndef _DRIVER_FS_H_ -#define _DRIVER_FS_H_ - -struct driver_dir_entry; -struct attribute; - -struct driverfs_ops { - int (*open)(struct driver_dir_entry *); - int (*close)(struct driver_dir_entry *); - ssize_t (*show)(struct driver_dir_entry *, struct attribute *,char *, size_t, loff_t); - ssize_t (*store)(struct driver_dir_entry *,struct attribute *,const char *, size_t, loff_t); -}; - -struct driver_dir_entry { - char * name; - struct dentry * dentry; - mode_t mode; - struct driverfs_ops * ops; -}; - -struct attribute { - char * name; - mode_t mode; -}; - -extern int -driverfs_create_dir(struct driver_dir_entry *, struct driver_dir_entry *); - -extern void -driverfs_remove_dir(struct driver_dir_entry * entry); - -extern int -driverfs_create_file(struct attribute * attr, - struct driver_dir_entry * parent); - -extern int -driverfs_create_symlink(struct driver_dir_entry * parent, - char * name, char * target); - -extern void -driverfs_remove_file(struct driver_dir_entry *, const char * name); - -#endif /* _DDFS_H_ */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/linux/ext2_fs.h linux-2.5-bk/include/linux/ext2_fs.h --- linux-2.5.45/include/linux/ext2_fs.h Wed Oct 30 16:41:56 2002 +++ linux-2.5-bk/include/linux/ext2_fs.h Thu Oct 31 22:13:05 2002 @@ -58,8 +58,6 @@ */ #define EXT2_BAD_INO 1 /* Bad blocks inode */ #define EXT2_ROOT_INO 2 /* Root inode */ -#define EXT2_ACL_IDX_INO 3 /* ACL inode */ -#define EXT2_ACL_DATA_INO 4 /* ACL inode */ #define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */ #define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */ @@ -99,7 +97,6 @@ #else # define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size) #endif -#define EXT2_ACLE_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_acl_entry)) #define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (__u32)) #ifdef __KERNEL__ # define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_blocksize_bits) @@ -134,28 +131,6 @@ #endif /* - * ACL structures - */ -struct ext2_acl_header /* Header of Access Control Lists */ -{ - __u32 aclh_size; - __u32 aclh_file_count; - __u32 aclh_acle_count; - __u32 aclh_first_acle; -}; - -struct ext2_acl_entry /* Access Control List Entry */ -{ - __u32 acle_size; - __u16 acle_perms; /* Access permissions */ - __u16 acle_type; /* Type of entry */ - __u16 acle_tag; /* User or group identity */ - __u16 acle_pad1; - __u32 acle_next; /* Pointer on next entry for the */ - /* same inode or on next free entry */ -}; - -/* * Structure of a blocks group descriptor */ struct ext2_group_desc @@ -332,6 +307,8 @@ #define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */ #define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */ #define EXT2_MOUNT_NO_UID32 0x0200 /* Disable 32-bit UIDs */ +#define EXT2_MOUNT_XATTR_USER 0x4000 /* Extended user attributes */ +#define EXT2_MOUNT_POSIX_ACL 0x8000 /* POSIX Access Control Lists */ #define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt #define set_opt(o, opt) o |= EXT2_MOUNT_##opt @@ -410,7 +387,20 @@ __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/ __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */ __u16 s_padding1; - __u32 s_reserved[204]; /* Padding to the end of the block */ + /* + * Journaling support valid if EXT3_FEATURE_COMPAT_HAS_JOURNAL set. + */ + __u8 s_journal_uuid[16]; /* uuid of journal superblock */ + __u32 s_journal_inum; /* inode number of journal file */ + __u32 s_journal_dev; /* device number of journal file */ + __u32 s_last_orphan; /* start of list of inodes to delete */ + __u32 s_hash_seed[4]; /* HTREE hash seed */ + __u8 s_def_hash_version; /* Default hash version to use */ + __u8 s_reserved_char_pad; + __u16 s_reserved_word_pad; + __u32 s_default_mount_opts; + __u32 s_first_meta_bg; /* First metablock block group */ + __u32 s_reserved[190]; /* Padding to the end of the block */ }; /* @@ -473,10 +463,12 @@ #define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 #define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 #define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 +#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010 #define EXT2_FEATURE_INCOMPAT_ANY 0xffffffff -#define EXT2_FEATURE_COMPAT_SUPP 0 -#define EXT2_FEATURE_INCOMPAT_SUPP EXT2_FEATURE_INCOMPAT_FILETYPE +#define EXT2_FEATURE_COMPAT_SUPP EXT2_FEATURE_COMPAT_EXT_ATTR +#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \ + EXT2_FEATURE_INCOMPAT_META_BG) #define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ EXT2_FEATURE_RO_COMPAT_BTREE_DIR) @@ -488,6 +480,20 @@ */ #define EXT2_DEF_RESUID 0 #define EXT2_DEF_RESGID 0 + +/* + * Default mount options + */ +#define EXT2_DEFM_DEBUG 0x0001 +#define EXT2_DEFM_BSDGROUPS 0x0002 +#define EXT2_DEFM_XATTR_USER 0x0004 +#define EXT2_DEFM_ACL 0x0008 +#define EXT2_DEFM_UID16 0x0010 + /* Not used by ext2, but reserved for use by ext3 */ +#define EXT3_DEFM_JMODE 0x0060 +#define EXT3_DEFM_JMODE_DATA 0x0020 +#define EXT3_DEFM_JMODE_ORDERED 0x0040 +#define EXT3_DEFM_JMODE_WBACK 0x0060 /* * Structure of a directory entry diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/linux/ext2_fs_sb.h linux-2.5-bk/include/linux/ext2_fs_sb.h --- linux-2.5.45/include/linux/ext2_fs_sb.h Wed Oct 30 16:43:40 2002 +++ linux-2.5-bk/include/linux/ext2_fs_sb.h Thu Oct 31 22:13:05 2002 @@ -43,6 +43,8 @@ int s_inode_size; int s_first_ino; u32 s_next_generation; + unsigned long s_dir_count; + u8 *debts; }; #endif /* _LINUX_EXT2_FS_SB */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/linux/ext3_fs.h linux-2.5-bk/include/linux/ext3_fs.h --- linux-2.5.45/include/linux/ext3_fs.h Wed Oct 30 16:43:00 2002 +++ linux-2.5-bk/include/linux/ext3_fs.h Thu Oct 31 22:13:05 2002 @@ -64,8 +64,6 @@ */ #define EXT3_BAD_INO 1 /* Bad blocks inode */ #define EXT3_ROOT_INO 2 /* Root inode */ -#define EXT3_ACL_IDX_INO 3 /* ACL inode */ -#define EXT3_ACL_DATA_INO 4 /* ACL inode */ #define EXT3_BOOT_LOADER_INO 5 /* Boot loader inode */ #define EXT3_UNDEL_DIR_INO 6 /* Undelete directory inode */ #define EXT3_RESIZE_INO 7 /* Reserved group descriptors inode */ @@ -95,7 +93,6 @@ #else # define EXT3_BLOCK_SIZE(s) (EXT3_MIN_BLOCK_SIZE << (s)->s_log_block_size) #endif -#define EXT3_ACLE_PER_BLOCK(s) (EXT3_BLOCK_SIZE(s) / sizeof (struct ext3_acl_entry)) #define EXT3_ADDR_PER_BLOCK(s) (EXT3_BLOCK_SIZE(s) / sizeof (__u32)) #ifdef __KERNEL__ # define EXT3_BLOCK_SIZE_BITS(s) ((s)->s_blocksize_bits) @@ -130,28 +127,6 @@ #endif /* - * ACL structures - */ -struct ext3_acl_header /* Header of Access Control Lists */ -{ - __u32 aclh_size; - __u32 aclh_file_count; - __u32 aclh_acle_count; - __u32 aclh_first_acle; -}; - -struct ext3_acl_entry /* Access Control List Entry */ -{ - __u32 acle_size; - __u16 acle_perms; /* Access permissions */ - __u16 acle_type; /* Type of entry */ - __u16 acle_tag; /* User or group identity */ - __u16 acle_pad1; - __u32 acle_next; /* Pointer on next entry for the */ - /* same inode or on next free entry */ -}; - -/* * Structure of a blocks group descriptor */ struct ext3_group_desc @@ -347,6 +322,8 @@ #define EXT3_MOUNT_WRITEBACK_DATA 0x0C00 /* No data ordering */ #define EXT3_MOUNT_UPDATE_JOURNAL 0x1000 /* Update the journal format */ #define EXT3_MOUNT_NO_UID32 0x2000 /* Disable 32-bit UIDs */ +#define EXT3_MOUNT_XATTR_USER 0x4000 /* Extended user attributes */ +#define EXT3_MOUNT_POSIX_ACL 0x8000 /* POSIX Access Control Lists */ /* Compatibility, for having both ext2_fs.h and ext3_fs.h included at once */ #ifndef _LINUX_EXT2_FS_H @@ -449,7 +426,9 @@ __u8 s_def_hash_version; /* Default hash version to use */ __u8 s_reserved_char_pad; __u16 s_reserved_word_pad; - __u32 s_reserved[192]; /* Padding to the end of the block */ + __u32 s_default_mount_opts; + __u32 s_first_meta_bg; /* First metablock block group */ + __u32 s_reserved[190]; /* Padding to the end of the block */ }; #ifdef __KERNEL__ @@ -528,8 +507,11 @@ #define EXT3_FEATURE_INCOMPAT_FILETYPE 0x0002 #define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 /* Needs recovery */ #define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 /* Journal device */ +#define EXT3_FEATURE_INCOMPAT_META_BG 0x0010 -#define EXT3_FEATURE_COMPAT_SUPP 0 +#define EXT3_FEATURE_COMPAT_SUPP EXT2_FEATURE_COMPAT_EXT_ATTR +#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \ + EXT2_FEATURE_INCOMPAT_META_BG) #define EXT3_FEATURE_INCOMPAT_SUPP (EXT3_FEATURE_INCOMPAT_FILETYPE| \ EXT3_FEATURE_INCOMPAT_RECOVER) #define EXT3_FEATURE_RO_COMPAT_SUPP (EXT3_FEATURE_RO_COMPAT_SPARSE_SUPER| \ @@ -543,6 +525,19 @@ #define EXT3_DEF_RESGID 0 /* + * Default mount options + */ +#define EXT3_DEFM_DEBUG 0x0001 +#define EXT3_DEFM_BSDGROUPS 0x0002 +#define EXT3_DEFM_XATTR_USER 0x0004 +#define EXT3_DEFM_ACL 0x0008 +#define EXT3_DEFM_UID16 0x0010 +#define EXT3_DEFM_JMODE 0x0060 +#define EXT3_DEFM_JMODE_DATA 0x0020 +#define EXT3_DEFM_JMODE_ORDERED 0x0040 +#define EXT3_DEFM_JMODE_WBACK 0x0060 + +/* * Structure of a directory entry */ #define EXT3_NAME_LEN 255 @@ -713,6 +708,7 @@ /* inode.c */ +extern int ext3_forget(handle_t *, int, struct inode *, struct buffer_head *, int); extern struct buffer_head * ext3_getblk (handle_t *, struct inode *, long, int, int *); extern struct buffer_head * ext3_bread (handle_t *, struct inode *, int, int, int *); @@ -781,8 +777,10 @@ /* namei.c */ extern struct inode_operations ext3_dir_inode_operations; +extern struct inode_operations ext3_special_inode_operations; /* symlink.c */ +extern struct inode_operations ext3_symlink_inode_operations; extern struct inode_operations ext3_fast_symlink_inode_operations; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/linux/ext3_fs_i.h linux-2.5-bk/include/linux/ext3_fs_i.h --- linux-2.5.45/include/linux/ext3_fs_i.h Wed Oct 30 16:41:35 2002 +++ linux-2.5-bk/include/linux/ext3_fs_i.h Thu Oct 31 22:13:05 2002 @@ -41,6 +41,10 @@ __u32 i_prealloc_count; #endif __u32 i_dir_start_lookup; +#ifdef CONFIG_EXT3_FS_POSIX_ACL + struct posix_acl *i_acl; + struct posix_acl *i_default_acl; +#endif struct list_head i_orphan; /* unlinked but open inodes */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/linux/ext3_jbd.h linux-2.5-bk/include/linux/ext3_jbd.h --- linux-2.5.45/include/linux/ext3_jbd.h Wed Oct 30 16:41:54 2002 +++ linux-2.5-bk/include/linux/ext3_jbd.h Thu Oct 31 22:13:05 2002 @@ -30,13 +30,19 @@ #define EXT3_SINGLEDATA_TRANS_BLOCKS 8 +/* Extended attributes may touch two data buffers, two bitmap buffers, + * and two group and summaries. */ + +#define EXT3_XATTR_TRANS_BLOCKS 8 + /* Define the minimum size for a transaction which modifies data. This * needs to take into account the fact that we may end up modifying two * quota files too (one for the group, one for the user quota). The * superblock only gets updated once, of course, so don't bother * counting that again for the quota updates. */ -#define EXT3_DATA_TRANS_BLOCKS (3 * EXT3_SINGLEDATA_TRANS_BLOCKS - 2) +#define EXT3_DATA_TRANS_BLOCKS (3 * EXT3_SINGLEDATA_TRANS_BLOCKS + \ + EXT3_XATTR_TRANS_BLOCKS - 2) extern int ext3_writepage_trans_blocks(struct inode *inode); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/linux/fs.h linux-2.5-bk/include/linux/fs.h --- linux-2.5.45/include/linux/fs.h Wed Oct 30 16:42:21 2002 +++ linux-2.5-bk/include/linux/fs.h Thu Oct 31 22:13:06 2002 @@ -110,6 +110,7 @@ #define MS_MOVE 8192 #define MS_REC 16384 #define MS_VERBOSE 32768 +#define MS_POSIXACL (1<<16) /* VFS does not apply the umask */ #define MS_ACTIVE (1<<30) #define MS_NOUSER (1<<31) @@ -164,6 +165,7 @@ #define IS_IMMUTABLE(inode) ((inode)->i_flags & S_IMMUTABLE) #define IS_NOATIME(inode) (__IS_FLG(inode, MS_NOATIME) || ((inode)->i_flags & S_NOATIME)) #define IS_NODIRATIME(inode) __IS_FLG(inode, MS_NODIRATIME) +#define IS_POSIXACL(inode) __IS_FLG(inode, MS_POSIXACL) #define IS_DEADDIR(inode) ((inode)->i_flags & S_DEAD) @@ -1147,6 +1149,7 @@ extern int filemap_fdatawait(struct address_space *); extern void sync_supers(void); extern sector_t bmap(struct inode *, sector_t); +extern int setattr_mask(unsigned int); extern int notify_change(struct dentry *, struct iattr *); extern int permission(struct inode *, int); extern int vfs_permission(struct inode *, int); @@ -1225,6 +1228,7 @@ extern void __iget(struct inode * inode); extern void clear_inode(struct inode *); +extern void destroy_inode(struct inode *); extern struct inode *new_inode(struct super_block *); extern void remove_suid(struct dentry *); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/linux/genhd.h linux-2.5-bk/include/linux/genhd.h --- linux-2.5.45/include/linux/genhd.h Wed Oct 30 16:41:57 2002 +++ linux-2.5-bk/include/linux/genhd.h Thu Oct 31 22:13:06 2002 @@ -62,7 +62,7 @@ sector_t start_sect; sector_t nr_sects; devfs_handle_t de; /* primary (master) devfs entry */ - struct device *hd_driverfs_dev; /* support driverfs hiearchy */ + struct kobject kobj; unsigned reads, read_sectors, writes, write_sectors; int policy; }; @@ -93,7 +93,7 @@ devfs_handle_t de; /* more of the same */ devfs_handle_t disk_de; /* piled higher and deeper */ struct device *driverfs_dev; - struct device disk_dev; + struct kobject kobj; struct timer_rand_state *random; int policy; @@ -129,8 +129,6 @@ { disk->capacity = size; } - -extern struct device_class disk_devclass; #endif /* __KERNEL__ */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/linux/hugetlb.h linux-2.5-bk/include/linux/hugetlb.h --- linux-2.5.45/include/linux/hugetlb.h Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/include/linux/hugetlb.h Thu Oct 31 22:13:06 2002 @@ -0,0 +1,53 @@ +#ifndef _LINUX_HUGETLB_H +#define _LINUX_HUGETLB_H + +#ifdef CONFIG_HUGETLB_PAGE +static inline int is_vm_hugetlb_page(struct vm_area_struct *vma) +{ + return vma->vm_flags & VM_HUGETLB; +} + +int copy_hugetlb_page_range(struct mm_struct *, struct mm_struct *, struct vm_area_struct *); +int follow_hugetlb_page(struct mm_struct *, struct vm_area_struct *, struct page **, struct vm_area_struct **, unsigned long *, int *, int); +void zap_hugepage_range(struct vm_area_struct *, unsigned long, unsigned long); +void unmap_hugepage_range(struct vm_area_struct *, unsigned long, unsigned long); +int hugetlb_prefault(struct address_space *, struct vm_area_struct *); +void huge_page_release(struct page *); +#else /* !CONFIG_HUGETLB_PAGE */ +static inline int is_vm_hugetlb_page(struct vm_area_struct *vma) +{ + return 0; +} + +#define follow_hugetlb_page(m,v,p,vs,a,b,i) ({ BUG(); 0; }) +#define copy_hugetlb_page_range(src, dst, vma) ({ BUG(); 0; }) +#define hugetlb_prefault(mapping, vma) ({ BUG(); 0; }) +#define zap_hugepage_range(vma, start, len) BUG() +#define unmap_hugepage_range(vma, start, end) BUG() +#define huge_page_release(page) BUG() + +#endif /* !CONFIG_HUGETLB_PAGE */ + +#ifdef CONFIG_HUGETLBFS +extern struct file_operations hugetlbfs_file_operations; +extern struct vm_operations_struct hugetlb_vm_ops; +struct file *hugetlb_zero_setup(size_t); + +static inline int is_file_hugepages(struct file *file) +{ + return file->f_op == &hugetlbfs_file_operations; +} + +static inline void set_file_hugepages(struct file *file) +{ + file->f_op = &hugetlbfs_file_operations; +} +#else /* !CONFIG_HUGETLBFS */ + +#define is_file_hugepages(file) 0 +#define set_file_hugepages(file) BUG() +#define hugetlb_zero_setup(size) ERR_PTR(-ENOSYS) + +#endif /* !CONFIG_HUGETLBFS */ + +#endif /* _LINUX_HUGETLB_H */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/linux/ipc.h linux-2.5-bk/include/linux/ipc.h --- linux-2.5.45/include/linux/ipc.h Wed Oct 30 16:41:31 2002 +++ linux-2.5-bk/include/linux/ipc.h Thu Oct 31 22:13:06 2002 @@ -56,6 +56,8 @@ /* used by in-kernel data structures */ struct kern_ipc_perm { + spinlock_t lock; + int deleted; key_t key; uid_t uid; gid_t gid; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/linux/kernel_stat.h linux-2.5-bk/include/linux/kernel_stat.h --- linux-2.5.45/include/linux/kernel_stat.h Wed Oct 30 16:42:29 2002 +++ linux-2.5-bk/include/linux/kernel_stat.h Thu Oct 31 22:13:06 2002 @@ -5,6 +5,7 @@ #include #include #include +#include /* * 'kernel_stat.h' contains the definitions needed for doing @@ -12,26 +13,25 @@ * used by rstatd/perfmeter */ -#define DK_MAX_MAJOR 16 -#define DK_MAX_DISK 16 +struct cpu_usage_stat { + unsigned int user; + unsigned int nice; + unsigned int system; + unsigned int idle; + unsigned int iowait; +}; struct kernel_stat { - unsigned int per_cpu_user[NR_CPUS], - per_cpu_nice[NR_CPUS], - per_cpu_system[NR_CPUS], - per_cpu_idle[NR_CPUS], - per_cpu_iowait[NR_CPUS]; - unsigned int dk_drive[DK_MAX_MAJOR][DK_MAX_DISK]; - unsigned int dk_drive_rio[DK_MAX_MAJOR][DK_MAX_DISK]; - unsigned int dk_drive_wio[DK_MAX_MAJOR][DK_MAX_DISK]; - unsigned int dk_drive_rblk[DK_MAX_MAJOR][DK_MAX_DISK]; - unsigned int dk_drive_wblk[DK_MAX_MAJOR][DK_MAX_DISK]; + struct cpu_usage_stat cpustat; #if !defined(CONFIG_ARCH_S390) - unsigned int irqs[NR_CPUS][NR_IRQS]; + unsigned int irqs[NR_IRQS]; #endif }; -extern struct kernel_stat kstat; +DECLARE_PER_CPU(struct kernel_stat, kstat); + +#define kstat_cpu(cpu) per_cpu(kstat, cpu) +#define kstat_this_cpu kstat_cpu(smp_processor_id()) extern unsigned long nr_context_switches(void); @@ -50,8 +50,9 @@ { int i, sum=0; - for (i = 0 ; i < NR_CPUS ; i++) - sum += kstat.irqs[i][irq]; + for (i = 0 ; i < NR_CPUS ; i++) + if (cpu_possible(i)) + sum += kstat_cpu(i).irqs[irq]; return sum; } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/linux/kobject.h linux-2.5-bk/include/linux/kobject.h --- linux-2.5.45/include/linux/kobject.h Wed Oct 30 16:41:37 2002 +++ linux-2.5-bk/include/linux/kobject.h Thu Oct 31 22:13:06 2002 @@ -12,8 +12,10 @@ #include #include +#define KOBJ_NAME_LEN 16 + struct kobject { - char name[16]; + char name[KOBJ_NAME_LEN]; atomic_t refcount; struct list_head entry; struct kobject * parent; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/linux/mbcache.h linux-2.5-bk/include/linux/mbcache.h --- linux-2.5.45/include/linux/mbcache.h Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/include/linux/mbcache.h Thu Oct 31 22:13:06 2002 @@ -0,0 +1,72 @@ +/* + File: linux/mbcache.h + + (C) 2001 by Andreas Gruenbacher, +*/ + +/* Hardwire the number of additional indexes */ +#define MB_CACHE_INDEXES_COUNT 1 + +struct mb_cache_entry; + +struct mb_cache_op { + void (*free)(struct mb_cache_entry *); +}; + +struct mb_cache { + struct list_head c_cache_list; + const char *c_name; + struct mb_cache_op c_op; + atomic_t c_entry_count; + int c_bucket_bits; +#ifndef MB_CACHE_INDEXES_COUNT + int c_indexes_count; +#endif + kmem_cache_t *c_entry_cache; + struct list_head *c_block_hash; + struct list_head *c_indexes_hash[0]; +}; + +struct mb_cache_entry_index { + struct list_head o_list; + unsigned int o_key; +}; + +struct mb_cache_entry { + struct list_head e_lru_list; + struct mb_cache *e_cache; + atomic_t e_used; + struct block_device *e_bdev; + sector_t e_block; + struct list_head e_block_list; + struct mb_cache_entry_index e_indexes[0]; +}; + +/* Functions on caches */ + +struct mb_cache * mb_cache_create(const char *, struct mb_cache_op *, size_t, + int, int); +void mb_cache_shrink(struct mb_cache *, struct block_device *); +void mb_cache_destroy(struct mb_cache *); + +/* Functions on cache entries */ + +struct mb_cache_entry *mb_cache_entry_alloc(struct mb_cache *); +int mb_cache_entry_insert(struct mb_cache_entry *, struct block_device *, + sector_t, unsigned int[]); +void mb_cache_entry_rehash(struct mb_cache_entry *, unsigned int[]); +void mb_cache_entry_release(struct mb_cache_entry *); +void mb_cache_entry_takeout(struct mb_cache_entry *); +void mb_cache_entry_free(struct mb_cache_entry *); +struct mb_cache_entry *mb_cache_entry_dup(struct mb_cache_entry *); +struct mb_cache_entry *mb_cache_entry_get(struct mb_cache *, + struct block_device *, + sector_t); +#if !defined(MB_CACHE_INDEXES_COUNT) || (MB_CACHE_INDEXES_COUNT > 0) +struct mb_cache_entry *mb_cache_entry_find_first(struct mb_cache *cache, int, + struct block_device *, + unsigned int); +struct mb_cache_entry *mb_cache_entry_find_next(struct mb_cache_entry *, int, + struct block_device *, + unsigned int); +#endif diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/linux/mm.h linux-2.5-bk/include/linux/mm.h --- linux-2.5.45/include/linux/mm.h Wed Oct 30 16:41:39 2002 +++ linux-2.5-bk/include/linux/mm.h Thu Oct 31 22:13:06 2002 @@ -130,6 +130,7 @@ void (*open)(struct vm_area_struct * area); void (*close)(struct vm_area_struct * area); struct page * (*nopage)(struct vm_area_struct * area, unsigned long address, int unused); + int (*populate)(struct vm_area_struct * area, unsigned long address, unsigned long len, unsigned long prot, unsigned long pgoff, int nonblock); }; /* forward declaration; pte_chain is meant to be internal to rmap.c */ @@ -365,9 +366,12 @@ extern pmd_t *FASTCALL(__pmd_alloc(struct mm_struct *mm, pgd_t *pgd, unsigned long address)); extern pte_t *FASTCALL(pte_alloc_kernel(struct mm_struct *mm, pmd_t *pmd, unsigned long address)); extern pte_t *FASTCALL(pte_alloc_map(struct mm_struct *mm, pmd_t *pmd, unsigned long address)); +extern int install_page(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long addr, struct page *page, unsigned long prot); extern int handle_mm_fault(struct mm_struct *mm,struct vm_area_struct *vma, unsigned long address, int write_access); extern int make_pages_present(unsigned long addr, unsigned long end); extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write); +extern int sys_remap_file_pages(unsigned long start, unsigned long size, unsigned long prot, unsigned long pgoff, unsigned long nonblock); + extern struct page * follow_page(struct mm_struct *mm, unsigned long address, int write); int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, unsigned long start, @@ -376,20 +380,6 @@ int __set_page_dirty_buffers(struct page *page); int __set_page_dirty_nobuffers(struct page *page); -#ifdef CONFIG_HUGETLB_PAGE -#define is_vm_hugetlb_page(vma) (vma->vm_flags & VM_HUGETLB) -extern int copy_hugetlb_page_range(struct mm_struct *, struct mm_struct *, struct vm_area_struct *); -extern int follow_hugetlb_page(struct mm_struct *, struct vm_area_struct *, struct page **, struct vm_area_struct **, unsigned long *, int *, int); -extern int free_hugepages(struct vm_area_struct *); - -#else -#define is_vm_hugetlb_page(vma) (0) -#define follow_hugetlb_page(mm, vma, pages, vmas, start, len, i) (0) -#define copy_hugetlb_page_range(dst, src, vma) (0) -#define free_hugepages(mpnt) do { } while(0) -#endif - - /* * Prototype to add a shrinker callback for ageable caches. * @@ -533,6 +523,7 @@ struct vm_area_struct **pprev); extern int split_vma(struct mm_struct * mm, struct vm_area_struct * vma, unsigned long addr, int new_below); +extern void unmap_vma(struct mm_struct *mm, struct vm_area_struct *area); /* Look up the first VMA which intersects the interval start_addr..end_addr-1, NULL if none. Assume start_addr < end_addr. */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/linux/pagevec.h linux-2.5-bk/include/linux/pagevec.h --- linux-2.5.45/include/linux/pagevec.h Wed Oct 30 16:43:44 2002 +++ linux-2.5-bk/include/linux/pagevec.h Thu Oct 31 22:13:06 2002 @@ -20,7 +20,7 @@ void __pagevec_release_nonlru(struct pagevec *pvec); void __pagevec_free(struct pagevec *pvec); void __pagevec_lru_add(struct pagevec *pvec); -void lru_add_drain(void); +void __pagevec_lru_add_active(struct pagevec *pvec); void pagevec_deactivate_inactive(struct pagevec *pvec); void pagevec_strip(struct pagevec *pvec); unsigned int pagevec_lookup(struct pagevec *pvec, struct address_space *mapping, diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/linux/posix_acl.h linux-2.5-bk/include/linux/posix_acl.h --- linux-2.5.45/include/linux/posix_acl.h Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/include/linux/posix_acl.h Thu Oct 31 22:13:06 2002 @@ -0,0 +1,87 @@ +/* + File: linux/posix_acl.h + + (C) 2002 Andreas Gruenbacher, +*/ + + +#ifndef __LINUX_POSIX_ACL_H +#define __LINUX_POSIX_ACL_H + +#include + +#define ACL_UNDEFINED_ID (-1) + +/* a_type field in acl_user_posix_entry_t */ +#define ACL_TYPE_ACCESS (0x8000) +#define ACL_TYPE_DEFAULT (0x4000) + +/* e_tag entry in struct posix_acl_entry */ +#define ACL_USER_OBJ (0x01) +#define ACL_USER (0x02) +#define ACL_GROUP_OBJ (0x04) +#define ACL_GROUP (0x08) +#define ACL_MASK (0x10) +#define ACL_OTHER (0x20) + +/* permissions in the e_perm field */ +#define ACL_READ (0x04) +#define ACL_WRITE (0x02) +#define ACL_EXECUTE (0x01) +//#define ACL_ADD (0x08) +//#define ACL_DELETE (0x10) + +struct posix_acl_entry { + short e_tag; + unsigned short e_perm; + unsigned int e_id; +}; + +struct posix_acl { + atomic_t a_refcount; + unsigned int a_count; + struct posix_acl_entry a_entries[0]; +}; + +#define FOREACH_ACL_ENTRY(pa, acl, pe) \ + for(pa=(acl)->a_entries, pe=pa+(acl)->a_count; paa_refcount); + return acl; +} + +/* + * Free an ACL handle. + */ +static inline void +posix_acl_release(struct posix_acl *acl) +{ + if (acl && atomic_dec_and_test(&acl->a_refcount)) + kfree(acl); +} + + +/* posix_acl.c */ + +extern struct posix_acl *posix_acl_alloc(int, int); +extern struct posix_acl *posix_acl_clone(const struct posix_acl *, int); +extern int posix_acl_valid(const struct posix_acl *); +extern int posix_acl_permission(struct inode *, const struct posix_acl *, int); +extern struct posix_acl *posix_acl_from_mode(mode_t, int); +extern int posix_acl_equiv_mode(const struct posix_acl *, mode_t *); +extern int posix_acl_create_masq(struct posix_acl *, mode_t *); +extern int posix_acl_chmod_masq(struct posix_acl *, mode_t); +extern int posix_acl_masq_nfs_mode(struct posix_acl *, mode_t *); + +extern struct posix_acl *get_posix_acl(struct inode *, int); +extern int set_posix_acl(struct inode *, int, struct posix_acl *); + +#endif /* __LINUX_POSIX_ACL_H */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/linux/posix_acl_xattr.h linux-2.5-bk/include/linux/posix_acl_xattr.h --- linux-2.5.45/include/linux/posix_acl_xattr.h Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/include/linux/posix_acl_xattr.h Thu Oct 31 22:13:06 2002 @@ -0,0 +1,55 @@ +/* + File: linux/posix_acl_xattr.h + + Extended attribute system call representation of Access Control Lists. + + Copyright (C) 2000 by Andreas Gruenbacher + Copyright (C) 2002 SGI - Silicon Graphics, Inc + */ +#ifndef _POSIX_ACL_XATTR_H +#define _POSIX_ACL_XATTR_H + +#include + +/* Extended attribute names */ +#define POSIX_ACL_XATTR_ACCESS "system.posix_acl_access" +#define POSIX_ACL_XATTR_DEFAULT "system.posix_acl_default" + +/* Supported ACL a_version fields */ +#define POSIX_ACL_XATTR_VERSION 0x0002 + + +/* An undefined entry e_id value */ +#define ACL_UNDEFINED_ID (-1) + +typedef struct { + __u16 e_tag; + __u16 e_perm; + __u32 e_id; +} posix_acl_xattr_entry; + +typedef struct { + __u32 a_version; + posix_acl_xattr_entry a_entries[0]; +} posix_acl_xattr_header; + + +static inline size_t +posix_acl_xattr_size(int count) +{ + return (sizeof(posix_acl_xattr_header) + + (count * sizeof(posix_acl_xattr_entry))); +} + +static inline int +posix_acl_xattr_count(size_t size) +{ + if (size < sizeof(posix_acl_xattr_header)) + return -1; + size -= sizeof(posix_acl_xattr_header); + if (size % sizeof(posix_acl_xattr_entry)) + return -1; + return size / sizeof(posix_acl_xattr_entry); +} + +#endif /* _POSIX_ACL_XATTR_H */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/linux/sched.h linux-2.5-bk/include/linux/sched.h --- linux-2.5.45/include/linux/sched.h Wed Oct 30 16:41:55 2002 +++ linux-2.5-bk/include/linux/sched.h Thu Oct 31 22:13:06 2002 @@ -173,6 +173,7 @@ struct vm_area_struct * mmap; /* list of VMAs */ struct rb_root mm_rb; struct vm_area_struct * mmap_cache; /* last find_vma result */ + unsigned long free_area_cache; /* first hole */ pgd_t * pgd; atomic_t mm_users; /* How many users with user space? */ atomic_t mm_count; /* How many references to "struct mm_struct" (users count as 1) */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/linux/shm.h linux-2.5-bk/include/linux/shm.h --- linux-2.5.45/include/linux/shm.h Wed Oct 30 16:43:34 2002 +++ linux-2.5-bk/include/linux/shm.h Thu Oct 31 22:13:06 2002 @@ -88,6 +88,7 @@ /* shm_mode upper byte flags */ #define SHM_DEST 01000 /* segment will be destroyed on last detach */ #define SHM_LOCKED 02000 /* segment will not be swapped */ +#define SHM_HUGETLB 04000 /* segment will use huge TLB pages */ asmlinkage long sys_shmget (key_t key, size_t size, int flag); asmlinkage long sys_shmat (int shmid, char *shmaddr, int shmflg, unsigned long *addr); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/linux/swap.h linux-2.5-bk/include/linux/swap.h --- linux-2.5.45/include/linux/swap.h Wed Oct 30 16:41:37 2002 +++ linux-2.5-bk/include/linux/swap.h Thu Oct 31 22:13:06 2002 @@ -156,8 +156,9 @@ /* linux/mm/swap.c */ extern void FASTCALL(lru_cache_add(struct page *)); - +extern void FASTCALL(lru_cache_add_active(struct page *)); extern void FASTCALL(activate_page(struct page *)); +void lru_add_drain(void); extern void swap_setup(void); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/linux/sysfs.h linux-2.5-bk/include/linux/sysfs.h --- linux-2.5.45/include/linux/sysfs.h Wed Oct 30 16:42:26 2002 +++ linux-2.5-bk/include/linux/sysfs.h Thu Oct 31 22:13:06 2002 @@ -9,18 +9,16 @@ #ifndef _SYSFS_H_ #define _SYSFS_H_ -struct driver_dir_entry; -struct attribute; struct kobject; -struct sysfs_ops { - ssize_t (*show)(struct kobject *, struct attribute *,char *, size_t, loff_t); - ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t, loff_t); -}; - struct attribute { char * name; mode_t mode; +}; + +struct sysfs_ops { + ssize_t (*show)(struct kobject *, struct attribute *,char *, size_t, loff_t); + ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t, loff_t); }; extern int diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/linux/videodev.h linux-2.5-bk/include/linux/videodev.h --- linux-2.5.45/include/linux/videodev.h Wed Oct 30 16:44:13 2002 +++ linux-2.5-bk/include/linux/videodev.h Thu Oct 31 22:13:06 2002 @@ -4,10 +4,10 @@ #include #include -#if 0 +#if 1 /* * v4l2 is still work-in-progress, integration planed for 2.5.x - * v4l2 project homepage: http://www.thedirks.org/v4l2/ + * documentation: http://bytesex.org/v4l/ * patches available from: http://bytesex.org/patches/ */ # define HAVE_V4L2 1 @@ -32,10 +32,7 @@ int minor; /* new interface -- we will use file_operations directly - * like soundcore does. - * kernel_ioctl() will be called by video_generic_ioctl. - * video_generic_ioctl() does the userspace copying of the - * ioctl arguments */ + * like soundcore does. */ struct file_operations *fops; void *priv; /* Used to be 'private' but that upsets C++ */ @@ -397,7 +394,7 @@ #define VID_HARDWARE_PWC 31 /* Philips webcams */ #define VID_HARDWARE_MEYE 32 /* Sony Vaio MotionEye cameras */ #define VID_HARDWARE_CPIA2 33 -#define VID_HARDWARE_VICAM 34 +#define VID_HARDWARE_VICAM 34 #endif /* __LINUX_VIDEODEV_H */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/linux/videodev2.h linux-2.5-bk/include/linux/videodev2.h --- linux-2.5.45/include/linux/videodev2.h Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/include/linux/videodev2.h Thu Oct 31 22:13:06 2002 @@ -0,0 +1,859 @@ +#ifndef __LINUX_VIDEODEV2_H +#define __LINUX_VIDEODEV2_H +/* + * Video for Linux Two + * + * Header file for v4l or V4L2 drivers and applications, for + * Linux kernels 2.2.x or 2.4.x. + * + * See http://bytesex.org/v4l/ for API specs and other + * v4l2 documentation. + * + * Author: Bill Dirks + * Justin Schoeman + * et al. + */ +#include /* need struct timeval */ + +/* + * M I S C E L L A N E O U S + */ + +/* Four-character-code (FOURCC) */ +#define v4l2_fourcc(a,b,c,d)\ + (((__u32)(a)<<0)|((__u32)(b)<<8)|((__u32)(c)<<16)|((__u32)(d)<<24)) + +/* + * E N U M S + */ +enum v4l2_field { + V4L2_FIELD_ANY = 0, /* driver can choose from none, + top, bottom, interlaced + depending on whatever it thinks + is approximate ... */ + V4L2_FIELD_NONE = 1, /* this device has no fields ... */ + V4L2_FIELD_TOP = 2, /* top field only */ + V4L2_FIELD_BOTTOM = 3, /* bottom field only */ + V4L2_FIELD_INTERLACED = 4, /* both fields interlaced */ + V4L2_FIELD_SEQ_TB = 5, /* both fields sequential into one + buffer, top-bottom order */ + V4L2_FIELD_SEQ_BT = 6, /* same as above + bottom-top order */ + V4L2_FIELD_ALTERNATE = 7, /* both fields alternating into + separate buffers */ +}; +#define V4L2_FIELD_HAS_TOP(field) \ + ((field) == V4L2_FIELD_TOP ||\ + (field) == V4L2_FIELD_INTERLACED ||\ + (field) == V4L2_FIELD_SEQ_TB ||\ + (field) == V4L2_FIELD_SEQ_BT) +#define V4L2_FIELD_HAS_BOTTOM(field) \ + ((field) == V4L2_FIELD_BOTTOM ||\ + (field) == V4L2_FIELD_INTERLACED ||\ + (field) == V4L2_FIELD_SEQ_TB ||\ + (field) == V4L2_FIELD_SEQ_BT) +#define V4L2_FIELD_HAS_BOTH(field) \ + ((field) == V4L2_FIELD_INTERLACED ||\ + (field) == V4L2_FIELD_SEQ_TB ||\ + (field) == V4L2_FIELD_SEQ_BT) + +enum v4l2_buf_type { + V4L2_BUF_TYPE_VIDEO_CAPTURE = 1, + V4L2_BUF_TYPE_VIDEO_OUTPUT = 2, + V4L2_BUF_TYPE_VIDEO_OVERLAY = 3, + V4L2_BUF_TYPE_VBI_CAPTURE = 4, + V4L2_BUF_TYPE_VBI_OUTPUT = 5, + V4L2_BUF_TYPE_PRIVATE = 0x80, +}; + +enum v4l2_ctrl_type { + V4L2_CTRL_TYPE_INTEGER = 1, + V4L2_CTRL_TYPE_BOOLEAN = 2, + V4L2_CTRL_TYPE_MENU = 3, + V4L2_CTRL_TYPE_BUTTON = 4, +}; + +enum v4l2_tuner_type { + V4L2_TUNER_RADIO = 1, + V4L2_TUNER_ANALOG_TV = 2, +}; + +enum v4l2_memory { + V4L2_MEMORY_MMAP = 1, + V4L2_MEMORY_USERPTR = 2, + V4L2_MEMORY_OVERLAY = 3, +}; + +/* see also http://vektor.theorem.ca/graphics/ycbcr/ */ +enum v4l2_colorspace { + /* ITU-R 601 -- broadcast NTSC/PAL */ + V4L2_COLORSPACE_SMPTE170M = 1, + + /* 1125-Line (US) HDTV */ + V4L2_COLORSPACE_SMPTE240M = 2, + + /* HD and modern captures. */ + V4L2_COLORSPACE_REC709 = 3, + + /* broken BT878 extents (601, luma range 16-253 instead of 16-235) */ + V4L2_COLORSPACE_BT878 = 4, + + /* These should be useful. Assume 601 extents. */ + V4L2_COLORSPACE_470_SYSTEM_M = 5, + V4L2_COLORSPACE_470_SYSTEM_BG = 6, + + /* I know there will be cameras that send this. So, this is + * unspecified chromaticities and full 0-255 on each of the + * Y'CbCr components + */ + V4L2_COLORSPACE_JPEG = 7, + + /* For RGB colourspaces, this is probably a good start. */ + V4L2_COLORSPACE_SRGB = 8, +}; + +struct v4l2_rect { + __s32 left; + __s32 top; + __s32 width; + __s32 height; +}; + +struct v4l2_fract { + __u32 numerator; + __u32 denominator; +}; + +/* + * D R I V E R C A P A B I L I T I E S + */ +struct v4l2_capability +{ + __u8 driver[16]; /* i.e. "bttv" */ + __u8 card[32]; /* i.e. "Hauppauge WinTV" */ + __u8 bus_info[32]; /* "PCI:" + pci_dev->slot_name */ + __u32 version; /* should use KERNEL_VERSION() */ + __u32 capabilities; /* Device capabilities */ + __u32 reserved[4]; +}; + +/* Values for 'capabilities' field */ +#define V4L2_CAP_VIDEO_CAPTURE 0x00000001 /* Is a video capture device */ +#define V4L2_CAP_VIDEO_OUTPUT 0x00000002 /* Is a video output device */ +#define V4L2_CAP_VIDEO_OVERLAY 0x00000004 /* Can do video overlay */ +#define V4L2_CAP_VBI_CAPTURE 0x00000010 /* Is a VBI capture device */ +#define V4L2_CAP_VBI_OUTPUT 0x00000020 /* Is a VBI output device */ +#define V4L2_CAP_RDS_CAPTURE 0x00000100 /* RDS data capture */ + +#define V4L2_CAP_TUNER 0x00010000 /* Has a tuner */ +#define V4L2_CAP_AUDIO 0x00020000 /* has audio support */ + +#define V4L2_CAP_READWRITE 0x01000000 /* read/write systemcalls */ +#define V4L2_CAP_ASYNCIO 0x02000000 /* async I/O */ +#define V4L2_CAP_STREAMING 0x04000000 /* streaming I/O ioctls */ + +/* + * V I D E O I M A G E F O R M A T + */ + +struct v4l2_pix_format +{ + __u32 width; + __u32 height; + __u32 pixelformat; + enum v4l2_field field; + __u32 bytesperline; /* for padding, zero if unused */ + __u32 sizeimage; + enum v4l2_colorspace colorspace; + __u32 priv; /* private data, depends on pixelformat */ +}; + +/* Pixel format FOURCC depth Description */ +#define V4L2_PIX_FMT_RGB332 v4l2_fourcc('R','G','B','1') /* 8 RGB-3-3-2 */ +#define V4L2_PIX_FMT_RGB555 v4l2_fourcc('R','G','B','O') /* 16 RGB-5-5-5 */ +#define V4L2_PIX_FMT_RGB565 v4l2_fourcc('R','G','B','P') /* 16 RGB-5-6-5 */ +#define V4L2_PIX_FMT_RGB555X v4l2_fourcc('R','G','B','Q') /* 16 RGB-5-5-5 BE */ +#define V4L2_PIX_FMT_RGB565X v4l2_fourcc('R','G','B','R') /* 16 RGB-5-6-5 BE */ +#define V4L2_PIX_FMT_BGR24 v4l2_fourcc('B','G','R','3') /* 24 BGR-8-8-8 */ +#define V4L2_PIX_FMT_RGB24 v4l2_fourcc('R','G','B','3') /* 24 RGB-8-8-8 */ +#define V4L2_PIX_FMT_BGR32 v4l2_fourcc('B','G','R','4') /* 32 BGR-8-8-8-8 */ +#define V4L2_PIX_FMT_RGB32 v4l2_fourcc('R','G','B','4') /* 32 RGB-8-8-8-8 */ +#define V4L2_PIX_FMT_GREY v4l2_fourcc('G','R','E','Y') /* 8 Greyscale */ +#define V4L2_PIX_FMT_YVU410 v4l2_fourcc('Y','V','U','9') /* 9 YVU 4:1:0 */ +#define V4L2_PIX_FMT_YVU420 v4l2_fourcc('Y','V','1','2') /* 12 YVU 4:2:0 */ +#define V4L2_PIX_FMT_YUYV v4l2_fourcc('Y','U','Y','V') /* 16 YUV 4:2:2 */ +#define V4L2_PIX_FMT_UYVY v4l2_fourcc('U','Y','V','Y') /* 16 YUV 4:2:2 */ +#define V4L2_PIX_FMT_YUV422P v4l2_fourcc('4','2','2','P') /* 16 YVU422 planar */ +#define V4L2_PIX_FMT_YUV411P v4l2_fourcc('4','1','1','P') /* 16 YVU411 planar */ +#define V4L2_PIX_FMT_Y41P v4l2_fourcc('Y','4','1','P') /* 12 YUV 4:1:1 */ + +/* two planes -- one Y, one Cr + Cb interleaved */ +#define V4L2_PIX_FMT_NV12 v4l2_fourcc('N','V','1','2') /* 12 Y/CbCr 4:2:0 */ +#define V4L2_PIX_FMT_NV21 v4l2_fourcc('N','V','2','1') /* 12 Y/CrCb 4:2:0 */ + +/* The following formats are not defined in the V4L2 specification */ +#define V4L2_PIX_FMT_YUV410 v4l2_fourcc('Y','U','V','9') /* 9 YUV 4:1:0 */ +#define V4L2_PIX_FMT_YUV420 v4l2_fourcc('Y','U','1','2') /* 12 YUV 4:2:0 */ +#define V4L2_PIX_FMT_YYUV v4l2_fourcc('Y','Y','U','V') /* 16 YUV 4:2:2 */ +#define V4L2_PIX_FMT_HI240 v4l2_fourcc('H','I','2','4') /* 8 8-bit color */ + +/* compressed formats */ +#define V4L2_PIX_FMT_MJPEG v4l2_fourcc('M','J','P','G') /* Motion-JPEG */ +#define V4L2_PIX_FMT_JPEG v4l2_fourcc('J','P','E','G') /* JFIF JPEG */ +#define V4L2_PIX_FMT_DV v4l2_fourcc('d','v','s','d') /* 1394 */ +#define V4L2_PIX_FMT_MPEG v4l2_fourcc('M','P','E','G') /* MPEG */ + +/* Vendor-specific formats */ +#define V4L2_PIX_FMT_WNVA v4l2_fourcc('W','N','V','A') /* Winnov hw compres */ + +/* + * F O R M A T E N U M E R A T I O N + */ +struct v4l2_fmtdesc +{ + __u32 index; /* Format number */ + enum v4l2_buf_type type; /* buffer type */ + __u32 flags; + __u8 description[32]; /* Description string */ + __u32 pixelformat; /* Format fourcc */ + __u32 reserved[4]; +}; + +#define V4L2_FMT_FLAG_COMPRESSED 0x0001 + + +/* + * T I M E C O D E + */ +struct v4l2_timecode +{ + __u32 type; + __u32 flags; + __u8 frames; + __u8 seconds; + __u8 minutes; + __u8 hours; + __u8 userbits[4]; +}; + +/* Type */ +#define V4L2_TC_TYPE_24FPS 1 +#define V4L2_TC_TYPE_25FPS 2 +#define V4L2_TC_TYPE_30FPS 3 +#define V4L2_TC_TYPE_50FPS 4 +#define V4L2_TC_TYPE_60FPS 5 + +/* Flags */ +#define V4L2_TC_FLAG_DROPFRAME 0x0001 /* "drop-frame" mode */ +#define V4L2_TC_FLAG_COLORFRAME 0x0002 +#define V4L2_TC_USERBITS_field 0x000C +#define V4L2_TC_USERBITS_USERDEFINED 0x0000 +#define V4L2_TC_USERBITS_8BITCHARS 0x0008 +/* The above is based on SMPTE timecodes */ + + +/* + * C O M P R E S S I O N P A R A M E T E R S + */ +#if 0 +/* ### generic compression settings don't work, there is too much + * ### codec-specific stuff. Maybe reuse that for MPEG codec settings + * ### later ... */ +struct v4l2_compression +{ + __u32 quality; + __u32 keyframerate; + __u32 pframerate; + __u32 reserved[5]; +}; +#endif + +struct v4l2_jpegcompression +{ + int quality; + + int APPn; /* Number of APP segment to be written, + * must be 0..15 */ + int APP_len; /* Length of data in JPEG APPn segment */ + char APP_data[60]; /* Data in the JPEG APPn segment. */ + + int COM_len; /* Length of data in JPEG COM segment */ + char COM_data[60]; /* Data in JPEG COM segment */ + + __u32 jpeg_markers; /* Which markers should go into the JPEG + * output. Unless you exactly know what + * you do, leave them untouched. + * Inluding less markers will make the + * resulting code smaller, but there will + * be fewer aplications which can read it. + * The presence of the APP and COM marker + * is influenced by APP_len and COM_len + * ONLY, not by this property! */ + +#define V4L2_JPEG_MARKER_DHT (1<<3) /* Define Huffman Tables */ +#define V4L2_JPEG_MARKER_DQT (1<<4) /* Define Quantization Tables */ +#define V4L2_JPEG_MARKER_DRI (1<<5) /* Define Restart Interval */ +#define V4L2_JPEG_MARKER_COM (1<<6) /* Comment segment */ +#define V4L2_JPEG_MARKER_APP (1<<7) /* App segment, driver will + * allways use APP0 */ +}; + + +/* + * M E M O R Y - M A P P I N G B U F F E R S + */ +struct v4l2_requestbuffers +{ + __u32 count; + enum v4l2_buf_type type; + enum v4l2_memory memory; + __u32 reserved[2]; +}; + +struct v4l2_buffer +{ + __u32 index; + enum v4l2_buf_type type; + __u32 bytesused; + __u32 flags; + enum v4l2_field field; + struct timeval timestamp; + struct v4l2_timecode timecode; + __u32 sequence; + + /* memory location */ + enum v4l2_memory memory; + union { + __u32 offset; + unsigned long userptr; + } m; + __u32 length; + + __u32 reserved[2]; +}; + +/* Flags for 'flags' field */ +#define V4L2_BUF_FLAG_MAPPED 0x0001 /* Buffer is mapped (flag) */ +#define V4L2_BUF_FLAG_QUEUED 0x0002 /* Buffer is queued for processing */ +#define V4L2_BUF_FLAG_DONE 0x0004 /* Buffer is ready */ +#define V4L2_BUF_FLAG_KEYFRAME 0x0008 /* Image is a keyframe (I-frame) */ +#define V4L2_BUF_FLAG_PFRAME 0x0010 /* Image is a P-frame */ +#define V4L2_BUF_FLAG_BFRAME 0x0020 /* Image is a B-frame */ +#define V4L2_BUF_FLAG_TIMECODE 0x0100 /* timecode field is valid */ + +/* + * O V E R L A Y P R E V I E W + */ +struct v4l2_framebuffer +{ + __u32 capability; + __u32 flags; +/* FIXME: in theory we should pass something like PCI device + memory + * region + offset instead of some physical address */ + void* base; + struct v4l2_pix_format fmt; +}; +/* Flags for the 'capability' field. Read only */ +#define V4L2_FBUF_CAP_EXTERNOVERLAY 0x0001 +#define V4L2_FBUF_CAP_CHROMAKEY 0x0002 +#define V4L2_FBUF_CAP_LIST_CLIPPING 0x0004 +#define V4L2_FBUF_CAP_BITMAP_CLIPPING 0x0008 +/* Flags for the 'flags' field. */ +#define V4L2_FBUF_FLAG_PRIMARY 0x0001 +#define V4L2_FBUF_FLAG_OVERLAY 0x0002 +#define V4L2_FBUF_FLAG_CHROMAKEY 0x0004 + +struct v4l2_clip +{ + struct v4l2_rect c; + struct v4l2_clip *next; +}; + +struct v4l2_window +{ + struct v4l2_rect w; + enum v4l2_field field; + __u32 chromakey; + struct v4l2_clip *clips; + __u32 clipcount; + void *bitmap; +}; + + +/* + * C A P T U R E P A R A M E T E R S + */ +struct v4l2_captureparm +{ + __u32 capability; /* Supported modes */ + __u32 capturemode; /* Current mode */ + struct v4l2_fract timeperframe; /* Time per frame in .1us units */ + __u32 extendedmode; /* Driver-specific extensions */ + __u32 readbuffers; /* # of buffers for read */ + __u32 reserved[4]; +}; +/* Flags for 'capability' and 'capturemode' fields */ +#define V4L2_MODE_HIGHQUALITY 0x0001 /* High quality imaging mode */ +#define V4L2_CAP_TIMEPERFRAME 0x1000 /* timeperframe field is supported */ + +struct v4l2_outputparm +{ + __u32 capability; /* Supported modes */ + __u32 outputmode; /* Current mode */ + struct v4l2_fract timeperframe; /* Time per frame in seconds */ + __u32 extendedmode; /* Driver-specific extensions */ + __u32 writebuffers; /* # of buffers for write */ + __u32 reserved[4]; +}; + +/* + * I N P U T I M A G E C R O P P I N G + */ + +struct v4l2_cropcap { + enum v4l2_buf_type type; + struct v4l2_rect bounds; + struct v4l2_rect defrect; + struct v4l2_fract pixelaspect; +}; + +struct v4l2_crop { + enum v4l2_buf_type type; + struct v4l2_rect c; +}; + +/* + * A N A L O G V I D E O S T A N D A R D + */ + +typedef __u64 v4l2_std_id; + +/* one bit for each */ +#define V4L2_STD_PAL_B ((v4l2_std_id)0x00000001) +#define V4L2_STD_PAL_B1 ((v4l2_std_id)0x00000002) +#define V4L2_STD_PAL_G ((v4l2_std_id)0x00000004) +#define V4L2_STD_PAL_H ((v4l2_std_id)0x00000008) +#define V4L2_STD_PAL_I ((v4l2_std_id)0x00000010) +#define V4L2_STD_PAL_D ((v4l2_std_id)0x00000020) +#define V4L2_STD_PAL_D1 ((v4l2_std_id)0x00000040) +#define V4L2_STD_PAL_K ((v4l2_std_id)0x00000080) + +#define V4L2_STD_PAL_M ((v4l2_std_id)0x00000100) +#define V4L2_STD_PAL_N ((v4l2_std_id)0x00000200) +#define V4L2_STD_PAL_Nc ((v4l2_std_id)0x00000400) +#define V4L2_STD_PAL_60 ((v4l2_std_id)0x00000800) + +#define V4L2_STD_NTSC_M ((v4l2_std_id)0x00001000) +#define V4L2_STD_NTSC_M_JP ((v4l2_std_id)0x00002000) + +#define V4L2_STD_SECAM_B ((v4l2_std_id)0x00010000) +#define V4L2_STD_SECAM_D ((v4l2_std_id)0x00020000) +#define V4L2_STD_SECAM_G ((v4l2_std_id)0x00040000) +#define V4L2_STD_SECAM_H ((v4l2_std_id)0x00080000) +#define V4L2_STD_SECAM_K ((v4l2_std_id)0x00100000) +#define V4L2_STD_SECAM_K1 ((v4l2_std_id)0x00200000) +#define V4L2_STD_SECAM_L ((v4l2_std_id)0x00400000) + +/* ATSC/HDTV */ +#define V4L2_STD_ATSC_8_VSB ((v4l2_std_id)0x01000000) +#define V4L2_STD_ATSC_16_VSB ((v4l2_std_id)0x02000000) + +/* some common needed stuff */ +#define V4L2_STD_PAL_BG (V4L2_STD_PAL_B |\ + V4L2_STD_PAL_B1 |\ + V4L2_STD_PAL_G) +#define V4L2_STD_PAL_DK (V4L2_STD_PAL_D |\ + V4L2_STD_PAL_D1 |\ + V4L2_STD_PAL_K) +#define V4L2_STD_PAL (V4L2_STD_PAL_BG |\ + V4L2_STD_PAL_DK |\ + V4L2_STD_PAL_H |\ + V4L2_STD_PAL_I) +#define V4L2_STD_NTSC (V4L2_STD_NTSC_M |\ + V4L2_STD_NTSC_M_JP) +#define V4L2_STD_SECAM (V4L2_STD_SECAM_B |\ + V4L2_STD_SECAM_D |\ + V4L2_STD_SECAM_G |\ + V4L2_STD_SECAM_H |\ + V4L2_STD_SECAM_K |\ + V4L2_STD_SECAM_K1 |\ + V4L2_STD_SECAM_L) + +#define V4L2_STD_525_60 (V4L2_STD_PAL_M |\ + V4L2_STD_PAL_60 |\ + V4L2_STD_NTSC) +#define V4L2_STD_625_50 (V4L2_STD_PAL |\ + V4L2_STD_PAL_N |\ + V4L2_STD_PAL_Nc |\ + V4L2_STD_SECAM) + +#define V4L2_STD_UNKNOWN 0 +#define V4L2_STD_ALL (V4L2_STD_525_60 |\ + V4L2_STD_625_50) + +struct v4l2_standard +{ + __u32 index; + v4l2_std_id id; + __u8 name[24]; + struct v4l2_fract frameperiod; /* Frames, not fields */ + __u32 framelines; + __u32 reserved[4]; +}; + + +/* + * V I D E O I N P U T S + */ +struct v4l2_input +{ + __u32 index; /* Which input */ + __u8 name[32]; /* Label */ + __u32 type; /* Type of input */ + __u32 audioset; /* Associated audios (bitfield) */ + __u32 tuner; /* Associated tuner */ + v4l2_std_id std; + __u32 status; + __u32 reserved[4]; +}; +/* Values for the 'type' field */ +#define V4L2_INPUT_TYPE_TUNER 1 +#define V4L2_INPUT_TYPE_CAMERA 2 + +/* field 'status' - general */ +#define V4L2_IN_ST_NO_POWER 0x00000001 /* Attached device is off */ +#define V4L2_IN_ST_NO_SIGNAL 0x00000002 +#define V4L2_IN_ST_NO_COLOR 0x00000004 + +/* field 'status' - analog */ +#define V4L2_IN_ST_NO_H_LOCK 0x00000100 /* No horizontal sync lock */ +#define V4L2_IN_ST_COLOR_KILL 0x00000200 /* Color killer is active */ + +/* field 'status' - digital */ +#define V4L2_IN_ST_NO_SYNC 0x00010000 /* No synchronization lock */ +#define V4L2_IN_ST_NO_EQU 0x00020000 /* No equalizer lock */ +#define V4L2_IN_ST_NO_CARRIER 0x00040000 /* Carrier recovery failed */ + +/* field 'status' - VCR and set-top box */ +#define V4L2_IN_ST_MACROVISION 0x01000000 /* Macrovision detected */ +#define V4L2_IN_ST_NO_ACCESS 0x02000000 /* Conditional access denied */ +#define V4L2_IN_ST_VTR 0x04000000 /* VTR time constant */ + +/* + * V I D E O O U T P U T S + */ +struct v4l2_output +{ + __u32 index; /* Which output */ + __u8 name[32]; /* Label */ + __u32 type; /* Type of output */ + __u32 audioset; /* Associated audios (bitfield) */ + __u32 modulator; /* Associated modulator */ + v4l2_std_id std; + __u32 reserved[4]; +}; +/* Values for the 'type' field */ +#define V4L2_OUTPUT_TYPE_MODULATOR 1 +#define V4L2_OUTPUT_TYPE_ANALOG 2 +#define V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY 3 + +/* + * C O N T R O L S + */ +struct v4l2_control +{ + __u32 id; + __s32 value; +}; + +/* Used in the VIDIOC_QUERYCTRL ioctl for querying controls */ +struct v4l2_queryctrl +{ + __u32 id; + enum v4l2_ctrl_type type; + __u8 name[32]; /* Whatever */ + __s32 minimum; /* Note signedness */ + __s32 maximum; + __s32 step; + __s32 default_value; + __u32 flags; + __u32 reserved[2]; +}; + +/* Used in the VIDIOC_QUERYMENU ioctl for querying menu items */ +struct v4l2_querymenu +{ + __u32 id; + __u32 index; + __u8 name[32]; /* Whatever */ + __u32 reserved; +}; + +/* Control flags */ +#define V4L2_CTRL_FLAG_DISABLED 0x0001 +#define V4L2_CTRL_FLAG_GRABBED 0x0002 + +/* Control IDs defined by V4L2 */ +#define V4L2_CID_BASE 0x00980900 +/* IDs reserved for driver specific controls */ +#define V4L2_CID_PRIVATE_BASE 0x08000000 + +#define V4L2_CID_BRIGHTNESS (V4L2_CID_BASE+0) +#define V4L2_CID_CONTRAST (V4L2_CID_BASE+1) +#define V4L2_CID_SATURATION (V4L2_CID_BASE+2) +#define V4L2_CID_HUE (V4L2_CID_BASE+3) +#define V4L2_CID_AUDIO_VOLUME (V4L2_CID_BASE+5) +#define V4L2_CID_AUDIO_BALANCE (V4L2_CID_BASE+6) +#define V4L2_CID_AUDIO_BASS (V4L2_CID_BASE+7) +#define V4L2_CID_AUDIO_TREBLE (V4L2_CID_BASE+8) +#define V4L2_CID_AUDIO_MUTE (V4L2_CID_BASE+9) +#define V4L2_CID_AUDIO_LOUDNESS (V4L2_CID_BASE+10) +#define V4L2_CID_BLACK_LEVEL (V4L2_CID_BASE+11) +#define V4L2_CID_AUTO_WHITE_BALANCE (V4L2_CID_BASE+12) +#define V4L2_CID_DO_WHITE_BALANCE (V4L2_CID_BASE+13) +#define V4L2_CID_RED_BALANCE (V4L2_CID_BASE+14) +#define V4L2_CID_BLUE_BALANCE (V4L2_CID_BASE+15) +#define V4L2_CID_GAMMA (V4L2_CID_BASE+16) +#define V4L2_CID_WHITENESS (V4L2_CID_GAMMA) /* ? Not sure */ +#define V4L2_CID_EXPOSURE (V4L2_CID_BASE+17) +#define V4L2_CID_AUTOGAIN (V4L2_CID_BASE+18) +#define V4L2_CID_GAIN (V4L2_CID_BASE+19) +#define V4L2_CID_HFLIP (V4L2_CID_BASE+20) +#define V4L2_CID_VFLIP (V4L2_CID_BASE+21) +#define V4L2_CID_HCENTER (V4L2_CID_BASE+22) +#define V4L2_CID_VCENTER (V4L2_CID_BASE+23) +#define V4L2_CID_LASTP1 (V4L2_CID_BASE+24) /* last CID + 1 */ + +/* + * T U N I N G + */ +struct v4l2_tuner +{ + __u32 index; + __u8 name[32]; + enum v4l2_tuner_type type; + __u32 capability; + __u32 rangelow; + __u32 rangehigh; + __u32 rxsubchans; + __u32 audmode; + __s32 signal; + __s32 afc; + __u32 reserved[4]; +}; + +struct v4l2_modulator +{ + __u32 index; + __u8 name[32]; + __u32 capability; + __u32 rangelow; + __u32 rangehigh; + __u32 txsubchans; + __u32 reserved[4]; +}; + +/* Flags for the 'capability' field */ +#define V4L2_TUNER_CAP_LOW 0x0001 +#define V4L2_TUNER_CAP_NORM 0x0002 +#define V4L2_TUNER_CAP_STEREO 0x0010 +#define V4L2_TUNER_CAP_LANG2 0x0020 +#define V4L2_TUNER_CAP_SAP 0x0020 +#define V4L2_TUNER_CAP_LANG1 0x0040 + +/* Flags for the 'rxsubchans' field */ +#define V4L2_TUNER_SUB_MONO 0x0001 +#define V4L2_TUNER_SUB_STEREO 0x0002 +#define V4L2_TUNER_SUB_LANG2 0x0004 +#define V4L2_TUNER_SUB_SAP 0x0004 +#define V4L2_TUNER_SUB_LANG1 0x0008 + +/* Values for the 'audmode' field */ +#define V4L2_TUNER_MODE_MONO 0x0000 +#define V4L2_TUNER_MODE_STEREO 0x0001 +#define V4L2_TUNER_MODE_LANG2 0x0002 +#define V4L2_TUNER_MODE_SAP 0x0002 +#define V4L2_TUNER_MODE_LANG1 0x0003 + +struct v4l2_frequency +{ + __u32 tuner; + enum v4l2_tuner_type type; + __u32 frequency; + __u32 reserved[8]; +}; + +/* + * A U D I O + */ +struct v4l2_audio +{ + __u32 index; + __u8 name[32]; + __u32 capability; + __u32 mode; + __u32 reserved[2]; +}; +/* Flags for the 'capability' field */ +#define V4L2_AUDCAP_STEREO 0x00001 +#define V4L2_AUDCAP_AVL 0x00002 + +/* Flags for the 'mode' field */ +#define V4L2_AUDMODE_AVL 0x00001 + +struct v4l2_audioout +{ + __u32 index; + __u8 name[32]; + __u32 capability; + __u32 mode; + __u32 reserved[2]; +}; + +/* + * D A T A S E R V I C E S ( V B I ) + * + * Data services API by Michael Schimek + */ + +struct v4l2_vbi_format +{ + __u32 sampling_rate; /* in 1 Hz */ + __u32 offset; + __u32 samples_per_line; + __u32 sample_format; /* V4L2_PIX_FMT_* */ + __s32 start[2]; + __u32 count[2]; + __u32 flags; /* V4L2_VBI_* */ + __u32 reserved[2]; /* must be zero */ +}; + +/* VBI flags */ +#define V4L2_VBI_UNSYNC (1<< 0) +#define V4L2_VBI_INTERLACED (1<< 1) + + +/* + * A G G R E G A T E S T R U C T U R E S + */ + +/* Stream data format + */ +struct v4l2_format +{ + enum v4l2_buf_type type; + union + { + struct v4l2_pix_format pix; // V4L2_BUF_TYPE_VIDEO_CAPTURE + struct v4l2_window win; // V4L2_BUF_TYPE_VIDEO_OVERLAY + struct v4l2_vbi_format vbi; // V4L2_BUF_TYPE_VBI_CAPTURE + __u8 raw_data[200]; // user-defined + } fmt; +}; + + +/* Stream type-dependent parameters + */ +struct v4l2_streamparm +{ + enum v4l2_buf_type type; + union + { + struct v4l2_captureparm capture; + struct v4l2_outputparm output; + __u8 raw_data[200]; /* user-defined */ + } parm; +}; + + + +/* + * I O C T L C O D E S F O R V I D E O D E V I C E S + * + */ +#define VIDIOC_QUERYCAP _IOR ('V', 0, struct v4l2_capability) +#define VIDIOC_RESERVED _IO ('V', 1) +#define VIDIOC_ENUM_FMT _IOWR ('V', 2, struct v4l2_fmtdesc) +#define VIDIOC_G_FMT _IOWR ('V', 4, struct v4l2_format) +#define VIDIOC_S_FMT _IOWR ('V', 5, struct v4l2_format) +#if 0 +#define VIDIOC_G_COMP _IOR ('V', 6, struct v4l2_compression) +#define VIDIOC_S_COMP _IOW ('V', 7, struct v4l2_compression) +#endif +#define VIDIOC_REQBUFS _IOWR ('V', 8, struct v4l2_requestbuffers) +#define VIDIOC_QUERYBUF _IOWR ('V', 9, struct v4l2_buffer) +#define VIDIOC_G_FBUF _IOR ('V', 10, struct v4l2_framebuffer) +#define VIDIOC_S_FBUF _IOW ('V', 11, struct v4l2_framebuffer) +#define VIDIOC_OVERLAY _IOWR ('V', 14, int) +#define VIDIOC_QBUF _IOWR ('V', 15, struct v4l2_buffer) +#define VIDIOC_DQBUF _IOWR ('V', 17, struct v4l2_buffer) +#define VIDIOC_STREAMON _IOW ('V', 18, int) +#define VIDIOC_STREAMOFF _IOW ('V', 19, int) +#define VIDIOC_G_PARM _IOWR ('V', 21, struct v4l2_streamparm) +#define VIDIOC_S_PARM _IOW ('V', 22, struct v4l2_streamparm) +#define VIDIOC_G_STD _IOR ('V', 23, v4l2_std_id) +#define VIDIOC_S_STD _IOW ('V', 24, v4l2_std_id) +#define VIDIOC_ENUMSTD _IOWR ('V', 25, struct v4l2_standard) +#define VIDIOC_ENUMINPUT _IOWR ('V', 26, struct v4l2_input) +#define VIDIOC_G_CTRL _IOWR ('V', 27, struct v4l2_control) +#define VIDIOC_S_CTRL _IOW ('V', 28, struct v4l2_control) +#define VIDIOC_G_TUNER _IOWR ('V', 29, struct v4l2_tuner) +#define VIDIOC_S_TUNER _IOW ('V', 30, struct v4l2_tuner) +#define VIDIOC_G_AUDIO _IOWR ('V', 33, struct v4l2_audio) +#define VIDIOC_S_AUDIO _IOW ('V', 34, struct v4l2_audio) +#define VIDIOC_QUERYCTRL _IOWR ('V', 36, struct v4l2_queryctrl) +#define VIDIOC_QUERYMENU _IOWR ('V', 37, struct v4l2_querymenu) +#define VIDIOC_G_INPUT _IOR ('V', 38, int) +#define VIDIOC_S_INPUT _IOWR ('V', 39, int) +#define VIDIOC_G_OUTPUT _IOR ('V', 46, int) +#define VIDIOC_S_OUTPUT _IOWR ('V', 47, int) +#define VIDIOC_ENUMOUTPUT _IOWR ('V', 48, struct v4l2_output) +#define VIDIOC_G_AUDOUT _IOWR ('V', 49, struct v4l2_audioout) +#define VIDIOC_S_AUDOUT _IOW ('V', 50, struct v4l2_audioout) +#define VIDIOC_G_MODULATOR _IOWR ('V', 54, struct v4l2_modulator) +#define VIDIOC_S_MODULATOR _IOW ('V', 55, struct v4l2_modulator) +#define VIDIOC_G_FREQUENCY _IOWR ('V', 56, struct v4l2_frequency) +#define VIDIOC_S_FREQUENCY _IOW ('V', 57, struct v4l2_frequency) +#define VIDIOC_CROPCAP _IOR ('V', 58, struct v4l2_cropcap) +#define VIDIOC_G_CROP _IOWR ('V', 59, struct v4l2_crop) +#define VIDIOC_S_CROP _IOW ('V', 60, struct v4l2_crop) +#define VIDIOC_G_JPEGCOMP _IOR ('V', 61, struct v4l2_jpegcompression) +#define VIDIOC_S_JPEGCOMP _IOW ('V', 62, struct v4l2_jpegcompression) +#define VIDIOC_QUERYSTD _IOR ('V', 63, v4l2_std_id) +#define VIDIOC_TRY_FMT _IOWR ('V', 63, struct v4l2_format) + +#define BASE_VIDIOC_PRIVATE 192 /* 192-255 are private */ + + +#ifdef __KERNEL__ +/* + * + * V 4 L 2 D R I V E R H E L P E R A P I + * + * Some commonly needed functions for drivers (v4l2-common.o module) + */ +#include + +/* Video standard functions */ +extern unsigned int v4l2_video_std_fps(struct v4l2_standard *vs); +extern int v4l2_video_std_construct(struct v4l2_standard *vs, + int id, char *name); + +/* Compatibility layer interface */ +typedef int (*v4l2_kioctl)(struct inode *inode, struct file *file, + unsigned int cmd, void *arg); +int v4l_compat_translate_ioctl(struct inode *inode, struct file *file, + int cmd, void *arg, v4l2_kioctl driver_ioctl); + +/* names for fancy debug output */ +extern char *v4l2_field_names[]; +extern char *v4l2_type_names[]; +extern char *v4l2_ioctl_names[]; + +#endif /* __KERNEL__ */ +#endif /* __LINUX_VIDEODEV2_H */ + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/include/linux/xattr_acl.h linux-2.5-bk/include/linux/xattr_acl.h --- linux-2.5.45/include/linux/xattr_acl.h Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/include/linux/xattr_acl.h Thu Oct 31 22:13:06 2002 @@ -0,0 +1,50 @@ +/* + File: linux/xattr_acl.h + + (extended attribute representation of access control lists) + + (C) 2000 Andreas Gruenbacher, +*/ + +#ifndef _LINUX_XATTR_ACL_H +#define _LINUX_XATTR_ACL_H + +#include + +#define XATTR_NAME_ACL_ACCESS "system.posix_acl_access" +#define XATTR_NAME_ACL_DEFAULT "system.posix_acl_default" + +#define XATTR_ACL_VERSION 0x0002 + +typedef struct { + __u16 e_tag; + __u16 e_perm; + __u32 e_id; +} xattr_acl_entry; + +typedef struct { + __u32 a_version; + xattr_acl_entry a_entries[0]; +} xattr_acl_header; + +static inline size_t xattr_acl_size(int count) +{ + return sizeof(xattr_acl_header) + count * sizeof(xattr_acl_entry); +} + +static inline int xattr_acl_count(size_t size) +{ + if (size < sizeof(xattr_acl_header)) + return -1; + size -= sizeof(xattr_acl_header); + if (size % sizeof(xattr_acl_entry)) + return -1; + return size / sizeof(xattr_acl_entry); +} + +struct posix_acl * posix_acl_from_xattr(const void *value, size_t size); +int posix_acl_to_xattr(const struct posix_acl *acl, void *buffer, size_t size); + + + +#endif /* _LINUX_XATTR_ACL_H */ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/init/do_mounts.c linux-2.5-bk/init/do_mounts.c --- linux-2.5.45/init/do_mounts.c Wed Oct 30 16:42:27 2002 +++ linux-2.5-bk/init/do_mounts.c Thu Oct 31 22:13:07 2002 @@ -101,7 +101,7 @@ /* read device number from .../dev */ - sprintf(path, "/sys/bus/block/devices/%s/dev", name); + sprintf(path, "/sys/block/%s/dev", name); fd = open(path, 0, 0); if (fd < 0) goto fail; @@ -119,7 +119,7 @@ return res; /* otherwise read range from .../range */ - sprintf(path, "/sys/bus/block/devices/%s/range", name); + sprintf(path, "/sys/block/%s/range", name); fd = open(path, 0, 0); if (fd < 0) goto fail; @@ -166,7 +166,7 @@ int part; sys_mkdir("/sys", 0700); - if (sys_mount("driverfs", "/sys", "driverfs", 0, NULL) < 0) + if (sys_mount("sysfs", "/sys", "sysfs", 0, NULL) < 0) goto out; if (strncmp(name, "/dev/", 5) != 0) { diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/ipc/msg.c linux-2.5-bk/ipc/msg.c --- linux-2.5.45/ipc/msg.c Wed Oct 30 16:41:33 2002 +++ linux-2.5-bk/ipc/msg.c Thu Oct 31 22:13:07 2002 @@ -65,7 +65,7 @@ static struct ipc_ids msg_ids; #define msg_lock(id) ((struct msg_queue*)ipc_lock(&msg_ids,id)) -#define msg_unlock(id) ipc_unlock(&msg_ids,id) +#define msg_unlock(msq) ipc_unlock(&(msq)->q_perm) #define msg_rmid(id) ((struct msg_queue*)ipc_rmid(&msg_ids,id)) #define msg_checkid(msq, msgid) \ ipc_checkid(&msg_ids,&msq->q_perm,msgid) @@ -93,7 +93,7 @@ int retval; struct msg_queue *msq; - msq = (struct msg_queue *) kmalloc (sizeof (*msq), GFP_KERNEL); + msq = ipc_rcu_alloc(sizeof(*msq)); if (!msq) return -ENOMEM; @@ -103,14 +103,14 @@ msq->q_perm.security = NULL; retval = security_ops->msg_queue_alloc_security(msq); if (retval) { - kfree(msq); + ipc_rcu_free(msq, sizeof(*msq)); return retval; } id = ipc_addid(&msg_ids, &msq->q_perm, msg_ctlmni); if(id == -1) { security_ops->msg_queue_free_security(msq); - kfree(msq); + ipc_rcu_free(msq, sizeof(*msq)); return -ENOSPC; } @@ -122,7 +122,7 @@ INIT_LIST_HEAD(&msq->q_messages); INIT_LIST_HEAD(&msq->q_receivers); INIT_LIST_HEAD(&msq->q_senders); - msg_unlock(id); + msg_unlock(msq); return msg_buildid(id,msq->q_perm.seq); } @@ -271,7 +271,7 @@ expunge_all(msq,-EIDRM); ss_wakeup(&msq->q_senders,1); - msg_unlock(id); + msg_unlock(msq); tmp = msq->q_messages.next; while(tmp != &msq->q_messages) { @@ -282,7 +282,7 @@ } atomic_sub(msq->q_cbytes, &msg_bytes); security_ops->msg_queue_free_security(msq); - kfree(msq); + ipc_rcu_free(msq, sizeof(struct msg_queue)); } asmlinkage long sys_msgget (key_t key, int msgflg) @@ -308,7 +308,7 @@ ret = -EACCES; else ret = msg_buildid(id, msq->q_perm.seq); - msg_unlock(id); + msg_unlock(msq); } up(&msg_ids.sem); return ret; @@ -488,7 +488,7 @@ tbuf.msg_qbytes = msq->q_qbytes; tbuf.msg_lspid = msq->q_lspid; tbuf.msg_lrpid = msq->q_lrpid; - msg_unlock(msqid); + msg_unlock(msq); if (copy_msqid_to_user(buf, &tbuf, version)) return -EFAULT; return success_return; @@ -541,7 +541,7 @@ * due to a larger queue size. */ ss_wakeup(&msq->q_senders,0); - msg_unlock(msqid); + msg_unlock(msq); break; } case IPC_RMID: @@ -553,10 +553,10 @@ up(&msg_ids.sem); return err; out_unlock_up: - msg_unlock(msqid); + msg_unlock(msq); goto out_up; out_unlock: - msg_unlock(msqid); + msg_unlock(msq); return err; } @@ -651,7 +651,7 @@ goto out_unlock_free; } ss_add(msq, &s); - msg_unlock(msqid); + msg_unlock(msq); schedule(); current->state= TASK_RUNNING; @@ -684,7 +684,7 @@ msg = NULL; out_unlock_free: - msg_unlock(msqid); + msg_unlock(msq); out_free: if(msg!=NULL) free_msg(msg); @@ -766,7 +766,7 @@ atomic_sub(msg->m_ts,&msg_bytes); atomic_dec(&msg_hdrs); ss_wakeup(&msq->q_senders,0); - msg_unlock(msqid); + msg_unlock(msq); out_success: msgsz = (msgsz > msg->m_ts) ? msg->m_ts : msgsz; if (put_user (msg->m_type, &msgp->mtype) || @@ -777,7 +777,6 @@ return msgsz; } else { - struct msg_queue *t; /* no message waiting. Prepare for pipelined * receive. */ @@ -795,7 +794,7 @@ msr_d.r_maxsize = msgsz; msr_d.r_msg = ERR_PTR(-EAGAIN); current->state = TASK_INTERRUPTIBLE; - msg_unlock(msqid); + msg_unlock(msq); schedule(); current->state = TASK_RUNNING; @@ -804,21 +803,19 @@ if(!IS_ERR(msg)) goto out_success; - t = msg_lock(msqid); - if(t==NULL) - msqid=-1; + msq = msg_lock(msqid); msg = (struct msg_msg*)msr_d.r_msg; if(!IS_ERR(msg)) { /* our message arived while we waited for * the spinlock. Process it. */ - if(msqid!=-1) - msg_unlock(msqid); + if(msq) + msg_unlock(msq); goto out_success; } err = PTR_ERR(msg); if(err == -EAGAIN) { - if(msqid==-1) + if(!msq) BUG(); list_del(&msr_d.r_list); if (signal_pending(current)) @@ -828,8 +825,8 @@ } } out_unlock: - if(msqid!=-1) - msg_unlock(msqid); + if(msq) + msg_unlock(msq); return err; } @@ -862,7 +859,7 @@ msq->q_stime, msq->q_rtime, msq->q_ctime); - msg_unlock(i); + msg_unlock(msq); pos += len; if(pos < offset) { diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/ipc/sem.c linux-2.5-bk/ipc/sem.c --- linux-2.5.45/ipc/sem.c Wed Oct 30 16:42:26 2002 +++ linux-2.5-bk/ipc/sem.c Thu Oct 31 22:13:07 2002 @@ -69,7 +69,7 @@ #define sem_lock(id) ((struct sem_array*)ipc_lock(&sem_ids,id)) -#define sem_unlock(id) ipc_unlock(&sem_ids,id) +#define sem_unlock(sma) ipc_unlock(&(sma)->sem_perm) #define sem_rmid(id) ((struct sem_array*)ipc_rmid(&sem_ids,id)) #define sem_checkid(sma, semid) \ ipc_checkid(&sem_ids,&sma->sem_perm,semid) @@ -126,7 +126,7 @@ return -ENOSPC; size = sizeof (*sma) + nsems * sizeof (struct sem); - sma = (struct sem_array *) ipc_alloc(size); + sma = ipc_rcu_alloc(size); if (!sma) { return -ENOMEM; } @@ -138,14 +138,14 @@ sma->sem_perm.security = NULL; retval = security_ops->sem_alloc_security(sma); if (retval) { - ipc_free(sma, size); + ipc_rcu_free(sma, size); return retval; } id = ipc_addid(&sem_ids, &sma->sem_perm, sc_semmni); if(id == -1) { security_ops->sem_free_security(sma); - ipc_free(sma, size); + ipc_rcu_free(sma, size); return -ENOSPC; } used_sems += nsems; @@ -156,7 +156,7 @@ /* sma->undo = NULL; */ sma->sem_nsems = nsems; sma->sem_ctime = CURRENT_TIME; - sem_unlock(id); + sem_unlock(sma); return sem_buildid(id, sma->sem_perm.seq); } @@ -189,7 +189,7 @@ err = -EACCES; else err = sem_buildid(id, sma->sem_perm.seq); - sem_unlock(id); + sem_unlock(sma); } up(&sem_ids.sem); @@ -205,12 +205,12 @@ if(smanew==NULL) return -EIDRM; if(smanew != sma || sem_checkid(sma,semid) || sma->sem_nsems != nsems) { - sem_unlock(semid); + sem_unlock(smanew); return -EIDRM; } if (ipcperms(&sma->sem_perm, flg)) { - sem_unlock(semid); + sem_unlock(smanew); return -EACCES; } return 0; @@ -423,12 +423,12 @@ q->prev = NULL; wake_up_process(q->sleeper); /* doesn't sleep */ } - sem_unlock(id); + sem_unlock(sma); used_sems -= sma->sem_nsems; size = sizeof (*sma) + sma->sem_nsems * sizeof (struct sem); security_ops->sem_free_security(sma); - ipc_free(sma, size); + ipc_rcu_free(sma, size); } static unsigned long copy_semid_to_user(void *buf, struct semid64_ds *in, int version) @@ -456,6 +456,7 @@ static int semctl_nolock(int semid, int semnum, int cmd, int version, union semun arg) { int err = -EINVAL; + struct sem_array *sma; switch(cmd) { case IPC_INFO: @@ -489,7 +490,6 @@ } case SEM_STAT: { - struct sem_array *sma; struct semid64_ds tbuf; int id; @@ -511,7 +511,7 @@ tbuf.sem_otime = sma->sem_otime; tbuf.sem_ctime = sma->sem_ctime; tbuf.sem_nsems = sma->sem_nsems; - sem_unlock(semid); + sem_unlock(sma); if (copy_semid_to_user (arg.buf, &tbuf, version)) return -EFAULT; return id; @@ -521,7 +521,7 @@ } return err; out_unlock: - sem_unlock(semid); + sem_unlock(sma); return err; } @@ -555,7 +555,7 @@ int i; if(nsems > SEMMSL_FAST) { - sem_unlock(semid); + sem_unlock(sma); sem_io = ipc_alloc(sizeof(ushort)*nsems); if(sem_io == NULL) return -ENOMEM; @@ -566,7 +566,7 @@ for (i = 0; i < sma->sem_nsems; i++) sem_io[i] = sma->sem_base[i].semval; - sem_unlock(semid); + sem_unlock(sma); err = 0; if(copy_to_user(array, sem_io, nsems*sizeof(ushort))) err = -EFAULT; @@ -577,7 +577,7 @@ int i; struct sem_undo *un; - sem_unlock(semid); + sem_unlock(sma); if(nsems > SEMMSL_FAST) { sem_io = ipc_alloc(sizeof(ushort)*nsems); @@ -619,7 +619,7 @@ tbuf.sem_otime = sma->sem_otime; tbuf.sem_ctime = sma->sem_ctime; tbuf.sem_nsems = sma->sem_nsems; - sem_unlock(semid); + sem_unlock(sma); if (copy_semid_to_user (arg.buf, &tbuf, version)) return -EFAULT; return 0; @@ -665,7 +665,7 @@ } } out_unlock: - sem_unlock(semid); + sem_unlock(sma); out_free: if(sem_io != fast_sem_io) ipc_free(sem_io, sizeof(ushort)*nsems); @@ -750,18 +750,18 @@ ipcp->mode = (ipcp->mode & ~S_IRWXUGO) | (setbuf.mode & S_IRWXUGO); sma->sem_ctime = CURRENT_TIME; - sem_unlock(semid); + sem_unlock(sma); err = 0; break; default: - sem_unlock(semid); + sem_unlock(sma); err = -EINVAL; break; } return err; out_unlock: - sem_unlock(semid); + sem_unlock(sma); return err; } @@ -914,7 +914,7 @@ saved_add_count = 0; if (current->sysvsem.undo_list != NULL) saved_add_count = current->sysvsem.undo_list->add_count; - sem_unlock(semid); + sem_unlock(sma); unlock_semundo(); error = get_undo_list(&undo_list); @@ -1052,18 +1052,17 @@ current->sysvsem.sleep_list = &queue; for (;;) { - struct sem_array* tmp; queue.status = -EINTR; queue.sleeper = current; current->state = TASK_INTERRUPTIBLE; - sem_unlock(semid); + sem_unlock(sma); unlock_semundo(); schedule(); lock_semundo(); - tmp = sem_lock(semid); - if(tmp==NULL) { + sma = sem_lock(semid); + if(sma==NULL) { if(queue.prev != NULL) BUG(); current->sysvsem.sleep_list = NULL; @@ -1098,7 +1097,7 @@ if (alter) update_queue (sma); out_unlock_semundo_free: - sem_unlock(semid); + sem_unlock(sma); out_semundo_free: unlock_semundo(); out_free: @@ -1185,7 +1184,7 @@ remove_from_queue(q->sma,q); } if(sma!=NULL) - sem_unlock(semid); + sem_unlock(sma); } undo_list = current->sysvsem.undo_list; @@ -1233,7 +1232,7 @@ /* maybe some queued-up processes were waiting for this */ update_queue(sma); next_entry: - sem_unlock(semid); + sem_unlock(sma); } __exit_semundo(current); @@ -1265,7 +1264,7 @@ sma->sem_perm.cgid, sma->sem_otime, sma->sem_ctime); - sem_unlock(i); + sem_unlock(sma); pos += len; if(pos < offset) { diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/ipc/shm.c linux-2.5-bk/ipc/shm.c --- linux-2.5.45/ipc/shm.c Wed Oct 30 16:42:56 2002 +++ linux-2.5-bk/ipc/shm.c Thu Oct 31 22:13:07 2002 @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -37,9 +38,7 @@ static struct ipc_ids shm_ids; #define shm_lock(id) ((struct shmid_kernel*)ipc_lock(&shm_ids,id)) -#define shm_unlock(id) ipc_unlock(&shm_ids,id) -#define shm_lockall() ipc_lockall(&shm_ids) -#define shm_unlockall() ipc_unlockall(&shm_ids) +#define shm_unlock(shp) ipc_unlock(&(shp)->shm_perm) #define shm_get(id) ((struct shmid_kernel*)ipc_get(&shm_ids,id)) #define shm_buildid(id, seq) \ ipc_buildid(&shm_ids, id, seq) @@ -92,7 +91,7 @@ shp->shm_atim = CURRENT_TIME; shp->shm_lprid = current->pid; shp->shm_nattch++; - shm_unlock(id); + shm_unlock(shp); } /* This is called by fork, once for every shm attach. */ @@ -113,11 +112,12 @@ { shm_tot -= (shp->shm_segsz + PAGE_SIZE - 1) >> PAGE_SHIFT; shm_rmid (shp->id); - shm_unlock(shp->id); - shmem_lock(shp->shm_file, 0); + shm_unlock(shp); + if (!is_file_hugepages(shp->shm_file)) + shmem_lock(shp->shm_file, 0); fput (shp->shm_file); security_ops->shm_free_security(shp); - kfree (shp); + ipc_rcu_free(shp, sizeof(struct shmid_kernel)); } /* @@ -143,7 +143,7 @@ shp->shm_flags & SHM_DEST) shm_destroy (shp); else - shm_unlock(id); + shm_unlock(shp); up (&shm_ids.sem); } @@ -180,7 +180,7 @@ if (shm_tot + numpages >= shm_ctlall) return -ENOSPC; - shp = (struct shmid_kernel *) kmalloc (sizeof (*shp), GFP_USER); + shp = ipc_rcu_alloc(sizeof(*shp)); if (!shp) return -ENOMEM; @@ -190,12 +190,16 @@ shp->shm_perm.security = NULL; error = security_ops->shm_alloc_security(shp); if (error) { - kfree(shp); + ipc_rcu_free(shp, sizeof(*shp)); return error; } - sprintf (name, "SYSV%08x", key); - file = shmem_file_setup(name, size, VM_ACCOUNT); + if (shmflg & SHM_HUGETLB) + file = hugetlb_zero_setup(size); + else { + sprintf (name, "SYSV%08x", key); + file = shmem_file_setup(name, size, VM_ACCOUNT); + } error = PTR_ERR(file); if (IS_ERR(file)) goto no_file; @@ -214,16 +218,19 @@ shp->id = shm_buildid(id,shp->shm_perm.seq); shp->shm_file = file; file->f_dentry->d_inode->i_ino = shp->id; - file->f_op = &shm_file_operations; + if (shmflg & SHM_HUGETLB) + set_file_hugepages(file); + else + file->f_op = &shm_file_operations; shm_tot += numpages; - shm_unlock (id); + shm_unlock(shp); return shp->id; no_id: fput(file); no_file: security_ops->shm_free_security(shp); - kfree(shp); + ipc_rcu_free(shp, sizeof(*shp)); return error; } @@ -252,9 +259,10 @@ err = -EACCES; else err = shm_buildid(id, shp->shm_perm.seq); - shm_unlock(id); + shm_unlock(shp); } up(&shm_ids.sem); + return err; } @@ -379,8 +387,10 @@ struct shmid_kernel *shp; int err, version; - if (cmd < 0 || shmid < 0) - return -EINVAL; + if (cmd < 0 || shmid < 0) { + err = -EINVAL; + goto out; + } version = ipc_parse_version(&cmd); @@ -401,7 +411,7 @@ err= shm_ids.max_id; if(err<0) err = 0; - return err; + goto out; } case SHM_INFO: { @@ -409,19 +419,20 @@ memset(&shm_info,0,sizeof(shm_info)); down(&shm_ids.sem); - shm_lockall(); shm_info.used_ids = shm_ids.in_use; shm_get_stat (&shm_info.shm_rss, &shm_info.shm_swp); shm_info.shm_tot = shm_tot; shm_info.swap_attempts = 0; shm_info.swap_successes = 0; err = shm_ids.max_id; - shm_unlockall(); up(&shm_ids.sem); - if(copy_to_user (buf, &shm_info, sizeof(shm_info))) - return -EFAULT; + if(copy_to_user (buf, &shm_info, sizeof(shm_info))) { + err = -EFAULT; + goto out; + } - return err < 0 ? 0 : err; + err = err < 0 ? 0 : err; + goto out; } case SHM_STAT: case IPC_STAT: @@ -430,9 +441,10 @@ int result; memset(&tbuf, 0, sizeof(tbuf)); shp = shm_lock(shmid); - if(shp==NULL) - return -EINVAL; - if(cmd==SHM_STAT) { + if(shp==NULL) { + err = -EINVAL; + goto out; + } else if(cmd==SHM_STAT) { err = -EINVAL; if (shmid > shm_ids.max_id) goto out_unlock; @@ -454,10 +466,12 @@ tbuf.shm_cpid = shp->shm_cprid; tbuf.shm_lpid = shp->shm_lprid; tbuf.shm_nattch = shp->shm_nattch; - shm_unlock(shmid); + shm_unlock(shp); if(copy_shmid_to_user (buf, &tbuf, version)) - return -EFAULT; - return result; + err = -EFAULT; + else + err = result; + goto out; } case SHM_LOCK: case SHM_UNLOCK: @@ -465,24 +479,30 @@ /* Allow superuser to lock segment in memory */ /* Should the pages be faulted in here or leave it to user? */ /* need to determine interaction with current->swappable */ - if (!capable(CAP_IPC_LOCK)) - return -EPERM; + if (!capable(CAP_IPC_LOCK)) { + err = -EPERM; + goto out; + } shp = shm_lock(shmid); - if(shp==NULL) - return -EINVAL; + if(shp==NULL) { + err = -EINVAL; + goto out; + } err = shm_checkid(shp,shmid); if(err) goto out_unlock; if(cmd==SHM_LOCK) { - shmem_lock(shp->shm_file, 1); + if (!is_file_hugepages(shp->shm_file)) + shmem_lock(shp->shm_file, 1); shp->shm_flags |= SHM_LOCKED; } else { - shmem_lock(shp->shm_file, 0); + if (!is_file_hugepages(shp->shm_file)) + shmem_lock(shp->shm_file, 0); shp->shm_flags &= ~SHM_LOCKED; } - shm_unlock(shmid); - return err; + shm_unlock(shp); + goto out; } case IPC_RMID: { @@ -514,17 +534,19 @@ shp->shm_flags |= SHM_DEST; /* Do not find it any more */ shp->shm_perm.key = IPC_PRIVATE; - shm_unlock(shmid); + shm_unlock(shp); } else shm_destroy (shp); up(&shm_ids.sem); - return err; + goto out; } case IPC_SET: { - if(copy_shmid_from_user (&setbuf, buf, version)) - return -EFAULT; + if(copy_shmid_from_user (&setbuf, buf, version)) { + err = -EFAULT; + goto out; + } down(&shm_ids.sem); shp = shm_lock(shmid); err=-EINVAL; @@ -549,17 +571,19 @@ } default: - return -EINVAL; + err = -EINVAL; + goto out; } err = 0; out_unlock_up: - shm_unlock(shmid); + shm_unlock(shp); out_up: up(&shm_ids.sem); - return err; + goto out; out_unlock: - shm_unlock(shmid); + shm_unlock(shp); +out: return err; } @@ -579,10 +603,10 @@ int acc_mode; void *user_addr; - if (shmid < 0) - return -EINVAL; - - if ((addr = (ulong)shmaddr)) { + if (shmid < 0) { + err = -EINVAL; + goto out; + } else if ((addr = (ulong)shmaddr)) { if (addr & (SHMLBA-1)) { if (shmflg & SHM_RND) addr &= ~(SHMLBA-1); /* round down */ @@ -612,21 +636,24 @@ * additional creator id... */ shp = shm_lock(shmid); - if(shp == NULL) - return -EINVAL; + if(shp == NULL) { + err = -EINVAL; + goto out; + } err = shm_checkid(shp,shmid); if (err) { - shm_unlock(shmid); - return err; + shm_unlock(shp); + goto out; } if (ipcperms(&shp->shm_perm, acc_mode)) { - shm_unlock(shmid); - return -EACCES; + shm_unlock(shp); + err = -EACCES; + goto out; } file = shp->shm_file; size = file->f_dentry->d_inode->i_size; shp->shm_nattch++; - shm_unlock(shmid); + shm_unlock(shp); down_write(¤t->mm->mmap_sem); if (addr && !(shmflg & SHM_REMAP)) { @@ -655,15 +682,15 @@ shp->shm_flags & SHM_DEST) shm_destroy (shp); else - shm_unlock(shmid); + shm_unlock(shp); up (&shm_ids.sem); *raddr = (unsigned long) user_addr; err = 0; if (IS_ERR(user_addr)) err = PTR_ERR(user_addr); +out: return err; - } /* @@ -673,18 +700,24 @@ asmlinkage long sys_shmdt (char *shmaddr) { struct mm_struct *mm = current->mm; - struct vm_area_struct *shmd, *shmdnext; + struct vm_area_struct *vma; + unsigned long address = (unsigned long)shmaddr; int retval = -EINVAL; down_write(&mm->mmap_sem); - for (shmd = mm->mmap; shmd; shmd = shmdnext) { - shmdnext = shmd->vm_next; - if (shmd->vm_ops == &shm_vm_ops - && shmd->vm_start - (shmd->vm_pgoff << PAGE_SHIFT) == (ulong) shmaddr) { - do_munmap(mm, shmd->vm_start, shmd->vm_end - shmd->vm_start); - retval = 0; - } - } + vma = find_vma(mm, address); + if (!vma) + goto out; + if (vma->vm_start != address) + goto out; + + /* ->vm_pgoff is always 0, see do_mmap() in sys_shmat() */ + retval = 0; + if (vma->vm_ops == &shm_vm_ops || (vma->vm_flags & VM_HUGETLB)) + do_munmap(mm, vma->vm_start, vma->vm_end - vma->vm_start); + else + retval = -EINVAL; +out: up_write(&mm->mmap_sem); return retval; } @@ -727,7 +760,7 @@ shp->shm_atim, shp->shm_dtim, shp->shm_ctim); - shm_unlock(i); + shm_unlock(shp); pos += len; if(pos < offset) { diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/ipc/util.c linux-2.5-bk/ipc/util.c --- linux-2.5.45/ipc/util.c Wed Oct 30 16:42:28 2002 +++ linux-2.5-bk/ipc/util.c Thu Oct 31 22:13:07 2002 @@ -8,6 +8,8 @@ * Chris Evans, * Nov 1999 - ipc helper functions, unified SMP locking * Manfred Spraul + * Oct 2002 - One lock per IPC id. RCU ipc_free for lock-free grow_ary(). + * Mingming Cao */ #include @@ -20,6 +22,8 @@ #include #include #include +#include +#include #if defined(CONFIG_SYSVIPC) @@ -69,13 +73,12 @@ ids->seq_max = seq_limit; } - ids->entries = ipc_alloc(sizeof(struct ipc_id)*size); + ids->entries = ipc_rcu_alloc(sizeof(struct ipc_id)*size); if(ids->entries == NULL) { printk(KERN_ERR "ipc_init_ids() failed, ipc service disabled.\n"); ids->size = 0; } - ids->ary = SPIN_LOCK_UNLOCKED; for(i=0;isize;i++) ids->entries[i].p = NULL; } @@ -84,7 +87,8 @@ * ipc_findkey - find a key in an ipc identifier set * @ids: Identifier set * @key: The key to find - * + * + * Requires ipc_ids.sem locked. * Returns the identifier if found or -1 if not. */ @@ -92,8 +96,9 @@ { int id; struct kern_ipc_perm* p; + int max_id = ids->max_id; - for (id = 0; id <= ids->max_id; id++) { + for (id = 0; id <= max_id; id++) { p = ids->entries[id].p; if(p==NULL) continue; @@ -103,6 +108,9 @@ return -1; } +/* + * Requires ipc_ids.sem locked + */ static int grow_ary(struct ipc_ids* ids, int newsize) { struct ipc_id* new; @@ -114,21 +122,21 @@ if(newsize <= ids->size) return newsize; - new = ipc_alloc(sizeof(struct ipc_id)*newsize); + new = ipc_rcu_alloc(sizeof(struct ipc_id)*newsize); if(new == NULL) return ids->size; memcpy(new, ids->entries, sizeof(struct ipc_id)*ids->size); for(i=ids->size;iary); - old = ids->entries; - ids->entries = new; i = ids->size; + + ids->entries = new; + wmb(); ids->size = newsize; - spin_unlock(&ids->ary); - ipc_free(old, sizeof(struct ipc_id)*i); + + ipc_rcu_free(old, sizeof(struct ipc_id)*i); return ids->size; } @@ -166,7 +174,10 @@ if(ids->seq > ids->seq_max) ids->seq = 0; - spin_lock(&ids->ary); + new->lock = SPIN_LOCK_UNLOCKED; + new->deleted = 0; + rcu_read_lock(); + spin_lock(&new->lock); ids->entries[id].p = new; return id; } @@ -180,6 +191,8 @@ * fed an invalid identifier. The entry is removed and internal * variables recomputed. The object associated with the identifier * is returned. + * ipc_ids.sem and the spinlock for this ID is hold before this function + * is called, and remain locked on the exit. */ struct kern_ipc_perm* ipc_rmid(struct ipc_ids* ids, int id) @@ -188,6 +201,7 @@ int lid = id % SEQ_MULTIPLIER; if(lid >= ids->size) BUG(); + p = ids->entries[lid].p; ids->entries[lid].p = NULL; if(p==NULL) @@ -202,6 +216,7 @@ } while (ids->entries[lid].p == NULL); ids->max_id = lid; } + p->deleted = 1; return p; } @@ -224,14 +239,14 @@ } /** - * ipc_free - free ipc space + * ipc_free - free ipc space * @ptr: pointer returned by ipc_alloc * @size: size of block * * Free a block created with ipc_alloc. The caller must know the size * used in the allocation call. */ - + void ipc_free(void* ptr, int size) { if(size > PAGE_SIZE) @@ -240,6 +255,85 @@ kfree(ptr); } +struct ipc_rcu_kmalloc +{ + struct rcu_head rcu; + /* "void *" makes sure alignment of following data is sane. */ + void *data[0]; +}; + +struct ipc_rcu_vmalloc +{ + struct rcu_head rcu; + struct work_struct work; + /* "void *" makes sure alignment of following data is sane. */ + void *data[0]; +}; + +static inline int rcu_use_vmalloc(int size) +{ + /* Too big for a single page? */ + if (sizeof(struct ipc_rcu_kmalloc) + size > PAGE_SIZE) + return 1; + return 0; +} + +/** + * ipc_rcu_alloc - allocate ipc and rcu space + * @size: size desired + * + * Allocate memory for the rcu header structure + the object. + * Returns the pointer to the object. + * NULL is returned if the allocation fails. + */ + +void* ipc_rcu_alloc(int size) +{ + void* out; + /* + * We prepend the allocation with the rcu struct, and + * workqueue if necessary (for vmalloc). + */ + if (rcu_use_vmalloc(size)) { + out = vmalloc(sizeof(struct ipc_rcu_vmalloc) + size); + if (out) out += sizeof(struct ipc_rcu_vmalloc); + } else { + out = kmalloc(sizeof(struct ipc_rcu_kmalloc)+size, GFP_KERNEL); + if (out) out += sizeof(struct ipc_rcu_kmalloc); + } + + return out; +} + +/** + * ipc_schedule_free - free ipc + rcu space + * + * Since RCU callback function is called in bh, + * we need to defer the vfree to schedule_work + */ +static void ipc_schedule_free(void* arg) +{ + struct ipc_rcu_vmalloc *free = arg; + + INIT_WORK(&free->work, vfree, free); + schedule_work(&free->work); +} + +void ipc_rcu_free(void* ptr, int size) +{ + if (rcu_use_vmalloc(size)) { + struct ipc_rcu_vmalloc *free; + free = ptr - sizeof(*free); + call_rcu(&free->rcu, ipc_schedule_free, free); + } else { + struct ipc_rcu_kmalloc *free; + free = ptr - sizeof(*free); + /* kfree takes a "const void *" so gcc warns. So we cast. */ + call_rcu(&free->rcu, (void (*)(void *))kfree, free); + } + +} + /** * ipcperms - check IPC permissions * @ipcp: IPC permission set @@ -311,6 +405,77 @@ out->cgid = NEW_TO_OLD_GID(in->cgid); out->mode = in->mode; out->seq = in->seq; +} + +/* + * ipc_get() requires ipc_ids.sem down, otherwise we need a rmb() here + * to sync with grow_ary(); + * + * So far only shm_get_stat() uses ipc_get() via shm_get(). So ipc_get() + * is called with shm_ids.sem locked. Thus a rmb() is not needed here, + * as grow_ary() also requires shm_ids.sem down(for shm). + * + * But if ipc_get() is used in the future without ipc_ids.sem down, + * we need to add a rmb() before accessing the entries array + */ +struct kern_ipc_perm* ipc_get(struct ipc_ids* ids, int id) +{ + struct kern_ipc_perm* out; + int lid = id % SEQ_MULTIPLIER; + if(lid >= ids->size) + return NULL; + rmb(); + out = ids->entries[lid].p; + return out; +} + +struct kern_ipc_perm* ipc_lock(struct ipc_ids* ids, int id) +{ + struct kern_ipc_perm* out; + int lid = id % SEQ_MULTIPLIER; + + rcu_read_lock(); + if(lid >= ids->size) { + rcu_read_unlock(); + return NULL; + } + + /* we need a barrier here to sync with grow_ary() */ + rmb(); + out = ids->entries[lid].p; + if(out == NULL) { + rcu_read_unlock(); + return NULL; + } + spin_lock(&out->lock); + + /* ipc_rmid() may have already freed the ID while ipc_lock + * was spinning: here verify that the structure is still valid + */ + if (out->deleted) { + spin_unlock(&out->lock); + rcu_read_unlock(); + return NULL; + } + return out; +} + +void ipc_unlock(struct kern_ipc_perm* perm) +{ + spin_unlock(&perm->lock); + rcu_read_unlock(); +} + +int ipc_buildid(struct ipc_ids* ids, int id, int seq) +{ + return SEQ_MULTIPLIER*seq + id; +} + +int ipc_checkid(struct ipc_ids* ids, struct kern_ipc_perm* ipcp, int uid) +{ + if(uid/SEQ_MULTIPLIER != ipcp->seq) + return 1; + return 0; } #ifndef __ia64__ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/ipc/util.h linux-2.5-bk/ipc/util.h --- linux-2.5.45/ipc/util.h Wed Oct 30 16:42:59 2002 +++ linux-2.5-bk/ipc/util.h Thu Oct 31 22:13:07 2002 @@ -4,7 +4,6 @@ * * ipc helper functions (c) 1999 Manfred Spraul */ - #define USHRT_MAX 0xffff #define SEQ_MULTIPLIER (IPCMNI) @@ -19,7 +18,6 @@ unsigned short seq; unsigned short seq_max; struct semaphore sem; - spinlock_t ary; struct ipc_id* entries; }; @@ -27,7 +25,6 @@ struct kern_ipc_perm* p; }; - void __init ipc_init_ids(struct ipc_ids* ids, int size); /* must be called with ids->sem acquired.*/ @@ -44,57 +41,17 @@ */ void* ipc_alloc(int size); void ipc_free(void* ptr, int size); +/* for allocation that need to be freed by RCU + * both function can sleep + */ +void* ipc_rcu_alloc(int size); +void ipc_rcu_free(void* arg, int size); -extern inline void ipc_lockall(struct ipc_ids* ids) -{ - spin_lock(&ids->ary); -} - -extern inline struct kern_ipc_perm* ipc_get(struct ipc_ids* ids, int id) -{ - struct kern_ipc_perm* out; - int lid = id % SEQ_MULTIPLIER; - if(lid >= ids->size) - return NULL; - - out = ids->entries[lid].p; - return out; -} - -extern inline void ipc_unlockall(struct ipc_ids* ids) -{ - spin_unlock(&ids->ary); -} -extern inline struct kern_ipc_perm* ipc_lock(struct ipc_ids* ids, int id) -{ - struct kern_ipc_perm* out; - int lid = id % SEQ_MULTIPLIER; - if(lid >= ids->size) - return NULL; - - spin_lock(&ids->ary); - out = ids->entries[lid].p; - if(out==NULL) - spin_unlock(&ids->ary); - return out; -} - -extern inline void ipc_unlock(struct ipc_ids* ids, int id) -{ - spin_unlock(&ids->ary); -} - -extern inline int ipc_buildid(struct ipc_ids* ids, int id, int seq) -{ - return SEQ_MULTIPLIER*seq + id; -} - -extern inline int ipc_checkid(struct ipc_ids* ids, struct kern_ipc_perm* ipcp, int uid) -{ - if(uid/SEQ_MULTIPLIER != ipcp->seq) - return 1; - return 0; -} +struct kern_ipc_perm* ipc_get(struct ipc_ids* ids, int id); +struct kern_ipc_perm* ipc_lock(struct ipc_ids* ids, int id); +void ipc_unlock(struct kern_ipc_perm* perm); +int ipc_buildid(struct ipc_ids* ids, int id, int seq); +int ipc_checkid(struct ipc_ids* ids, struct kern_ipc_perm* ipcp, int uid); void kernel_to_ipc64_perm(struct kern_ipc_perm *in, struct ipc64_perm *out); void ipc64_perm_to_ipc_perm(struct ipc64_perm *in, struct ipc_perm *out); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/kernel/fork.c linux-2.5-bk/kernel/fork.c --- linux-2.5.45/kernel/fork.c Wed Oct 30 16:41:55 2002 +++ linux-2.5-bk/kernel/fork.c Thu Oct 31 22:13:07 2002 @@ -215,6 +215,7 @@ mm->locked_vm = 0; mm->mmap = NULL; mm->mmap_cache = NULL; + mm->free_area_cache = TASK_UNMAPPED_BASE; mm->map_count = 0; mm->rss = 0; mm->cpu_vm_mask = 0; @@ -308,6 +309,8 @@ mm->page_table_lock = SPIN_LOCK_UNLOCKED; mm->ioctx_list_lock = RW_LOCK_UNLOCKED; mm->default_kioctx = (struct kioctx)INIT_KIOCTX(mm->default_kioctx, *mm); + mm->free_area_cache = TASK_UNMAPPED_BASE; + mm->pgd = pgd_alloc(mm); if (mm->pgd) return mm; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/kernel/ksyms.c linux-2.5-bk/kernel/ksyms.c --- linux-2.5.45/kernel/ksyms.c Wed Oct 30 16:41:34 2002 +++ linux-2.5-bk/kernel/ksyms.c Thu Oct 31 22:13:07 2002 @@ -493,7 +493,6 @@ EXPORT_SYMBOL(loops_per_jiffy); #endif -EXPORT_SYMBOL(kstat); /* misc */ EXPORT_SYMBOL(panic); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/kernel/sched.c linux-2.5-bk/kernel/sched.c --- linux-2.5.45/kernel/sched.c Wed Oct 30 16:43:37 2002 +++ linux-2.5-bk/kernel/sched.c Thu Oct 31 22:13:07 2002 @@ -839,6 +839,8 @@ #endif +DEFINE_PER_CPU(struct kernel_stat, kstat); + /* * We place interactive tasks back into the active array, if possible. * @@ -872,21 +874,21 @@ if (p == rq->idle) { /* note: this timer irq context must be accounted for as well */ if (irq_count() - HARDIRQ_OFFSET >= SOFTIRQ_OFFSET) - kstat.per_cpu_system[cpu] += sys_ticks; + kstat_cpu(cpu).cpustat.system += sys_ticks; else if (atomic_read(&nr_iowait_tasks) > 0) - kstat.per_cpu_iowait[cpu] += sys_ticks; + kstat_cpu(cpu).cpustat.iowait += sys_ticks; else - kstat.per_cpu_idle[cpu] += sys_ticks; + kstat_cpu(cpu).cpustat.idle += sys_ticks; #if CONFIG_SMP idle_tick(rq); #endif return; } if (TASK_NICE(p) > 0) - kstat.per_cpu_nice[cpu] += user_ticks; + kstat_cpu(cpu).cpustat.nice += user_ticks; else - kstat.per_cpu_user[cpu] += user_ticks; - kstat.per_cpu_system[cpu] += sys_ticks; + kstat_cpu(cpu).cpustat.user += user_ticks; + kstat_cpu(cpu).cpustat.system += sys_ticks; /* Task might have expired already, but not scheduled off yet */ if (p->array != rq->active) { @@ -2112,11 +2114,44 @@ spinlock_t kernel_flag __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED; #endif +static void kstat_init_cpu(int cpu) +{ + /* Add any initialisation to kstat here */ + /* Useful when cpu offlining logic is added.. */ +} + +static int __devinit kstat_cpu_notify(struct notifier_block *self, + unsigned long action, void *hcpu) +{ + int cpu = (unsigned long)hcpu; + switch(action) { + case CPU_UP_PREPARE: + kstat_init_cpu(cpu); + break; + default: + break; + } + return NOTIFY_OK; +} + +static struct notifier_block __devinitdata kstat_nb = { + .notifier_call = kstat_cpu_notify, + .next = NULL, +}; + +__init static void init_kstat(void) { + kstat_cpu_notify(&kstat_nb, (unsigned long)CPU_UP_PREPARE, + (void *)(long)smp_processor_id()); + register_cpu_notifier(&kstat_nb); +} + void __init sched_init(void) { runqueue_t *rq; int i, j, k; + /* Init the kstat counters */ + init_kstat(); for (i = 0; i < NR_CPUS; i++) { prio_array_t *array; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/kernel/timer.c linux-2.5-bk/kernel/timer.c --- linux-2.5.45/kernel/timer.c Wed Oct 30 16:42:21 2002 +++ linux-2.5-bk/kernel/timer.c Thu Oct 31 22:13:07 2002 @@ -409,7 +409,6 @@ /* Don't completely fail for HZ > 500. */ int tickadj = 500/HZ ? : 1; /* microsecs */ -struct kernel_stat kstat; /* * phase-lock loop variables diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/lib/kobject.c linux-2.5-bk/lib/kobject.c --- linux-2.5.45/lib/kobject.c Wed Oct 30 16:43:41 2002 +++ linux-2.5-bk/lib/kobject.c Thu Oct 31 22:13:07 2002 @@ -2,9 +2,10 @@ * kobject.c - library routines for handling generic kernel objects */ -#define DEBUG 1 +#define DEBUG 0 #include +#include #include #include @@ -76,11 +77,13 @@ } up_write(&s->rwsem); } - error = sysfs_create_dir(kobj); - if (!error) { - error = kobject_populate_dir(kobj); - if (error) - sysfs_remove_dir(kobj); + if (strlen(kobj->name)) { + error = sysfs_create_dir(kobj); + if (!error) { + error = kobject_populate_dir(kobj); + if (error) + sysfs_remove_dir(kobj); + } } return error; } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/mm/Makefile linux-2.5-bk/mm/Makefile --- linux-2.5.45/mm/Makefile Wed Oct 30 16:43:03 2002 +++ linux-2.5-bk/mm/Makefile Thu Oct 31 22:13:07 2002 @@ -4,7 +4,7 @@ export-objs := shmem.o filemap.o mempool.o page_alloc.o page-writeback.o -obj-y := memory.o mmap.o filemap.o mprotect.o mlock.o mremap.o \ +obj-y := memory.o mmap.o filemap.o fremap.o mprotect.o mlock.o mremap.o \ vmalloc.o slab.o bootmem.o swap.o vmscan.o page_io.o \ page_alloc.o swap_state.o swapfile.o oom_kill.o \ shmem.o highmem.o mempool.o msync.o mincore.o readahead.o \ diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/mm/filemap.c linux-2.5-bk/mm/filemap.c --- linux-2.5.45/mm/filemap.c Wed Oct 30 16:42:26 2002 +++ linux-2.5-bk/mm/filemap.c Thu Oct 31 22:13:07 2002 @@ -288,7 +288,7 @@ * at a cost of "thundering herd" phenomena during rare hash * collisions. */ -static inline wait_queue_head_t *page_waitqueue(struct page *page) +static wait_queue_head_t *page_waitqueue(struct page *page) { const struct zone *zone = page_zone(page); @@ -550,24 +550,6 @@ } /* - * Mark a page as having seen activity. - * - * inactive,unreferenced -> inactive,referenced - * inactive,referenced -> active,unreferenced - * active,unreferenced -> active,referenced - */ -void mark_page_accessed(struct page *page) -{ - if (!PageActive(page) && PageReferenced(page)) { - activate_page(page); - ClearPageReferenced(page); - return; - } else if (!PageReferenced(page)) { - SetPageReferenced(page); - } -} - -/* * This is a generic file read routine, and uses the * inode->i_op->readpage() function for the actual low-level * stuff. @@ -758,7 +740,7 @@ return ret; } -static inline void fault_in_pages_readable(const char *uaddr, int size) +static void fault_in_pages_readable(const char *uaddr, int size) { volatile char c; int ret; @@ -1166,8 +1148,159 @@ return NULL; } +static struct page * filemap_getpage(struct file *file, unsigned long pgoff, + int nonblock) +{ + struct address_space *mapping = file->f_dentry->d_inode->i_mapping; + struct page *page; + int error; + + /* + * Do we have something in the page cache already? + */ +retry_find: + page = find_get_page(mapping, pgoff); + if (!page) { + if (nonblock) + return NULL; + goto no_cached_page; + } + + /* + * Ok, found a page in the page cache, now we need to check + * that it's up-to-date. + */ + if (!PageUptodate(page)) + goto page_not_uptodate; + +success: + /* + * Found the page and have a reference on it, need to check sharing + * and possibly copy it over to another page.. + */ + mark_page_accessed(page); + flush_page_to_ram(page); + + return page; + +no_cached_page: + error = page_cache_read(file, pgoff); + + /* + * The page we want has now been added to the page cache. + * In the unlikely event that someone removed it in the + * meantime, we'll just come back here and read it again. + */ + if (error >= 0) + goto retry_find; + + /* + * An error return from page_cache_read can result if the + * system is low on memory, or a problem occurs while trying + * to schedule I/O. + */ + return NULL; + +page_not_uptodate: + lock_page(page); + + /* Did it get unhashed while we waited for it? */ + if (!page->mapping) { + unlock_page(page); + goto err; + } + + /* Did somebody else get it up-to-date? */ + if (PageUptodate(page)) { + unlock_page(page); + goto success; + } + + if (!mapping->a_ops->readpage(file, page)) { + wait_on_page_locked(page); + if (PageUptodate(page)) + goto success; + } + + /* + * Umm, take care of errors if the page isn't up-to-date. + * Try to re-read it _once_. We do this synchronously, + * because there really aren't any performance issues here + * and we need to check for errors. + */ + lock_page(page); + + /* Somebody truncated the page on us? */ + if (!page->mapping) { + unlock_page(page); + goto err; + } + /* Somebody else successfully read it in? */ + if (PageUptodate(page)) { + unlock_page(page); + goto success; + } + + ClearPageError(page); + if (!mapping->a_ops->readpage(file, page)) { + wait_on_page_locked(page); + if (PageUptodate(page)) + goto success; + } + + /* + * Things didn't work out. Return zero to tell the + * mm layer so, possibly freeing the page cache page first. + */ +err: + page_cache_release(page); + + return NULL; +} + +static int filemap_populate(struct vm_area_struct *vma, + unsigned long addr, + unsigned long len, + unsigned long prot, + unsigned long pgoff, + int nonblock) +{ + struct file *file = vma->vm_file; + struct address_space *mapping = file->f_dentry->d_inode->i_mapping; + struct inode *inode = mapping->host; + unsigned long size; + struct mm_struct *mm = vma->vm_mm; + struct page *page; + int err; + +repeat: + size = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + if (pgoff + (len >> PAGE_CACHE_SHIFT) > size) + return -EINVAL; + + page = filemap_getpage(file, pgoff, nonblock); + if (!page && !nonblock) + return -ENOMEM; + if (page) { + err = install_page(mm, vma, addr, page, prot); + if (err) { + page_cache_release(page); + return err; + } + } + + len -= PAGE_SIZE; + addr += PAGE_SIZE; + pgoff++; + if (len) + goto repeat; + + return 0; +} + static struct vm_operations_struct generic_file_vm_ops = { .nopage = filemap_nopage, + .populate = filemap_populate, }; /* This is used for a general mmap of a disk file */ @@ -1296,7 +1429,7 @@ return page; } -inline void remove_suid(struct dentry *dentry) +void remove_suid(struct dentry *dentry) { struct iattr newattrs; struct inode *inode = dentry->d_inode; @@ -1332,7 +1465,7 @@ return left; } -static inline int +static int __filemap_copy_from_user_iovec(char *vaddr, const struct iovec *iov, size_t base, size_t bytes) { diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/mm/fremap.c linux-2.5-bk/mm/fremap.c --- linux-2.5.45/mm/fremap.c Wed Dec 31 16:00:00 1969 +++ linux-2.5-bk/mm/fremap.c Thu Oct 31 22:13:07 2002 @@ -0,0 +1,146 @@ +/* + * linux/mm/mpopulate.c + * + * Explicit pagetable population and nonlinear (random) mappings support. + * + * started by Ingo Molnar, Copyright (C) 2002 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static inline void zap_pte(struct mm_struct *mm, pte_t *ptep) +{ + pte_t pte = *ptep; + + if (pte_none(pte)) + return; + if (pte_present(pte)) { + unsigned long pfn = pte_pfn(pte); + + pte = ptep_get_and_clear(ptep); + if (pfn_valid(pfn)) { + struct page *page = pfn_to_page(pfn); + if (!PageReserved(page)) { + if (pte_dirty(pte)) + set_page_dirty(page); + page_remove_rmap(page, ptep); + page_cache_release(page); + mm->rss--; + } + } + } else { + free_swap_and_cache(pte_to_swp_entry(pte)); + pte_clear(ptep); + } +} + +/* + * Install a page to a given virtual memory address, release any + * previously existing mapping. + */ +int install_page(struct mm_struct *mm, struct vm_area_struct *vma, + unsigned long addr, struct page *page, unsigned long prot) +{ + int err = -ENOMEM; + pte_t *pte, entry; + pgd_t *pgd; + pmd_t *pmd; + + pgd = pgd_offset(mm, addr); + spin_lock(&mm->page_table_lock); + + pmd = pmd_alloc(mm, pgd, addr); + if (!pmd) + goto err_unlock; + + pte = pte_alloc_map(mm, pmd, addr); + if (!pte) + goto err_unlock; + + zap_pte(mm, pte); + + mm->rss++; + flush_page_to_ram(page); + flush_icache_page(vma, page); + entry = mk_pte(page, protection_map[prot]); + if (prot & PROT_WRITE) + entry = pte_mkwrite(pte_mkdirty(entry)); + set_pte(pte, entry); + page_add_rmap(page, pte); + pte_unmap(pte); + flush_tlb_page(vma, addr); + + spin_unlock(&mm->page_table_lock); + + return 0; + +err_unlock: + spin_unlock(&mm->page_table_lock); + return err; +} + +/*** + * sys_remap_file_pages - remap arbitrary pages of a shared backing store + * file within an existing vma. + * @start: start of the remapped virtual memory range + * @size: size of the remapped virtual memory range + * @prot: new protection bits of the range + * @pgoff: to be mapped page of the backing store file + * @flags: 0 or MAP_NONBLOCKED - the later will cause no IO. + * + * this syscall works purely via pagetables, so it's the most efficient + * way to map the same (large) file into a given virtual window. Unlike + * mremap()/mmap() it does not create any new vmas. + * + * The new mappings do not live across swapout, so either use MAP_LOCKED + * or use PROT_NONE in the original linear mapping and add a special + * SIGBUS pagefault handler to reinstall zapped mappings. + */ +int sys_remap_file_pages(unsigned long start, unsigned long size, + unsigned long prot, unsigned long pgoff, unsigned long flags) +{ + struct mm_struct *mm = current->mm; + unsigned long end = start + size; + struct vm_area_struct *vma; + int err = -EINVAL; + + /* + * Sanitize the syscall parameters: + */ + start = PAGE_ALIGN(start); + size = PAGE_ALIGN(size); + prot &= 0xf; + + down_read(&mm->mmap_sem); + + vma = find_vma(mm, start); + /* + * Make sure the vma is shared, that it supports prefaulting, + * and that the remapped range is valid and fully within + * the single existing vma: + */ + if (vma && (vma->vm_flags & VM_SHARED) && + vma->vm_ops && vma->vm_ops->populate && + end > start && start >= vma->vm_start && + end <= vma->vm_end) { + /* + * Change the default protection to PROT_NONE: + */ + if (pgprot_val(vma->vm_page_prot) != pgprot_val(__S000)) + vma->vm_page_prot = __S000; + err = vma->vm_ops->populate(vma, start, size, prot, + pgoff, flags & MAP_NONBLOCK); + } + + up_read(&mm->mmap_sem); + + return err; +} + diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/mm/highmem.c linux-2.5-bk/mm/highmem.c --- linux-2.5.45/mm/highmem.c Wed Oct 30 16:43:03 2002 +++ linux-2.5-bk/mm/highmem.c Thu Oct 31 22:13:07 2002 @@ -229,7 +229,7 @@ /* * highmem version, map in to vec */ -static inline void bounce_copy_vec(struct bio_vec *to, unsigned char *vfrom) +static void bounce_copy_vec(struct bio_vec *to, unsigned char *vfrom) { unsigned long flags; unsigned char *vto; @@ -272,7 +272,7 @@ * queue gfp mask set, *to may or may not be a highmem page. kmap it * always, it will do the Right Thing */ -static inline void copy_to_high_bio_irq(struct bio *to, struct bio *from) +static void copy_to_high_bio_irq(struct bio *to, struct bio *from) { unsigned char *vfrom; struct bio_vec *tovec, *fromvec; @@ -338,7 +338,7 @@ return 0; } -static inline void __bounce_end_io_read(struct bio *bio, mempool_t *pool) +static void __bounce_end_io_read(struct bio *bio, mempool_t *pool) { struct bio *bio_orig = bio->bi_private; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/mm/memory.c linux-2.5-bk/mm/memory.c --- linux-2.5.45/mm/memory.c Wed Oct 30 16:42:54 2002 +++ linux-2.5-bk/mm/memory.c Thu Oct 31 22:13:07 2002 @@ -38,6 +38,7 @@ #include #include +#include #include #include #include @@ -397,6 +398,11 @@ { pgd_t * dir; + if (is_vm_hugetlb_page(vma)) { + unmap_hugepage_range(vma, address, end); + return; + } + BUG_ON(address >= end); dir = pgd_offset(vma->vm_mm, address); @@ -436,6 +442,11 @@ mmu_gather_t *tlb; unsigned long end, block; + if (is_vm_hugetlb_page(vma)) { + zap_hugepage_range(vma, address, size); + return; + } + spin_lock(&mm->page_table_lock); /* @@ -573,6 +584,7 @@ i = -EFAULT; goto out; } + flush_dcache_page(pages[i]); if (!PageReserved(pages[i])) page_cache_get(pages[i]); } @@ -588,7 +600,7 @@ return i; } -static inline void zeromap_pte_range(pte_t * pte, unsigned long address, +static void zeromap_pte_range(pte_t * pte, unsigned long address, unsigned long size, pgprot_t prot) { unsigned long end; @@ -830,7 +842,7 @@ page_remove_rmap(old_page, page_table); break_cow(vma, new_page, address, page_table); page_add_rmap(new_page, page_table); - lru_cache_add(new_page); + lru_cache_add_active(new_page); /* Free the old page.. */ new_page = old_page; @@ -955,12 +967,13 @@ num = valid_swaphandles(entry, &offset); for (i = 0; i < num; offset++, i++) { /* Ok, do the async read-ahead now */ - new_page = read_swap_cache_async(swp_entry(swp_type(entry), offset)); + new_page = read_swap_cache_async(swp_entry(swp_type(entry), + offset)); if (!new_page) break; page_cache_release(new_page); } - return; + lru_add_drain(); /* Push any new pages onto the LRU now */ } /* @@ -995,7 +1008,7 @@ ret = VM_FAULT_MINOR; pte_unmap(page_table); spin_unlock(&mm->page_table_lock); - return ret; + goto out; } /* Had to read the page from swap area: Major fault */ @@ -1017,7 +1030,8 @@ spin_unlock(&mm->page_table_lock); unlock_page(page); page_cache_release(page); - return VM_FAULT_MINOR; + ret = VM_FAULT_MINOR; + goto out; } /* The page isn't present yet, go ahead with the fault. */ @@ -1041,6 +1055,7 @@ update_mmu_cache(vma, address, pte); pte_unmap(page_table); spin_unlock(&mm->page_table_lock); +out: return ret; } @@ -1080,7 +1095,7 @@ mm->rss++; flush_page_to_ram(page); entry = pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot))); - lru_cache_add(page); + lru_cache_add_active(page); mark_page_accessed(page); } @@ -1139,7 +1154,7 @@ } copy_user_highpage(page, new_page, address); page_cache_release(new_page); - lru_cache_add(page); + lru_cache_add_active(page); new_page = page; } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/mm/mmap.c linux-2.5-bk/mm/mmap.c --- linux-2.5.45/mm/mmap.c Wed Oct 30 16:43:03 2002 +++ linux-2.5-bk/mm/mmap.c Thu Oct 31 22:13:07 2002 @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -132,7 +133,7 @@ } /* Remove one vm structure from the inode's i_mapping address space. */ -static inline void remove_shared_vm_struct(struct vm_area_struct *vma) +static void remove_shared_vm_struct(struct vm_area_struct *vma) { struct file *file = vma->vm_file; @@ -301,7 +302,7 @@ } } -static inline void __vma_link_rb(struct mm_struct * mm, struct vm_area_struct * vma, +static void __vma_link_rb(struct mm_struct * mm, struct vm_area_struct * vma, struct rb_node ** rb_link, struct rb_node * rb_parent) { rb_link_node(&vma->vm_rb, rb_parent, rb_link); @@ -335,8 +336,9 @@ __vma_link_file(vma); } -static inline void vma_link(struct mm_struct * mm, struct vm_area_struct * vma, struct vm_area_struct * prev, - struct rb_node ** rb_link, struct rb_node * rb_parent) +static void vma_link(struct mm_struct *mm, struct vm_area_struct *vma, + struct vm_area_struct *prev, struct rb_node **rb_link, + struct rb_node *rb_parent) { struct address_space *mapping = NULL; @@ -606,6 +608,12 @@ mm->locked_vm += len >> PAGE_SHIFT; make_pages_present(addr, addr + len); } + if (flags & MAP_POPULATE) { + up_write(&mm->mmap_sem); + sys_remap_file_pages(addr, len, prot, + pgoff, flags & MAP_NONBLOCK); + down_write(&mm->mmap_sem); + } return addr; unmap_and_free_vma: @@ -638,24 +646,33 @@ #ifndef HAVE_ARCH_UNMAPPED_AREA static inline unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags) { + struct mm_struct *mm = current->mm; struct vm_area_struct *vma; + int found_hole = 0; if (len > TASK_SIZE) return -ENOMEM; if (addr) { addr = PAGE_ALIGN(addr); - vma = find_vma(current->mm, addr); + vma = find_vma(mm, addr); if (TASK_SIZE - len >= addr && (!vma || addr + len <= vma->vm_start)) return addr; } - addr = PAGE_ALIGN(TASK_UNMAPPED_BASE); + addr = mm->free_area_cache; - for (vma = find_vma(current->mm, addr); ; vma = vma->vm_next) { + for (vma = find_vma(mm, addr); ; vma = vma->vm_next) { /* At this point: (!vma || addr < vma->vm_end). */ if (TASK_SIZE - len < addr) return -ENOMEM; + /* + * Record the first available hole. + */ + if (!found_hole && (!vma || addr < vma->vm_start)) { + mm->free_area_cache = addr; + found_hole = 1; + } if (!vma || addr + len <= vma->vm_start) return addr; addr = vma->vm_end; @@ -939,13 +956,19 @@ * By the time this function is called, the area struct has been * removed from the process mapping list. */ -static void unmap_vma(struct mm_struct *mm, struct vm_area_struct *area) +void unmap_vma(struct mm_struct *mm, struct vm_area_struct *area) { size_t len = area->vm_end - area->vm_start; area->vm_mm->total_vm -= len >> PAGE_SHIFT; if (area->vm_flags & VM_LOCKED) area->vm_mm->locked_vm -= len >> PAGE_SHIFT; + /* + * Is this a new hole at the lowest possible address? + */ + if (area->vm_start >= TASK_UNMAPPED_BASE && + area->vm_start < area->vm_mm->free_area_cache) + area->vm_mm->free_area_cache = area->vm_start; remove_shared_vm_struct(area); @@ -1024,14 +1047,10 @@ touched = NULL; do { struct vm_area_struct *next = mpnt->vm_next; - if (!(is_vm_hugetlb_page(mpnt))) { - mpnt->vm_next = touched; - touched = mpnt; - rb_erase(&mpnt->vm_rb, &mm->mm_rb); - mm->map_count--; - } - else - free_hugepages(mpnt); + mpnt->vm_next = touched; + touched = mpnt; + rb_erase(&mpnt->vm_rb, &mm->mm_rb); + mm->map_count--; mpnt = next; } while (mpnt && mpnt->vm_start < end); *npp = mpnt; @@ -1284,10 +1303,7 @@ vm_unacct_memory((end - start) >> PAGE_SHIFT); mm->map_count--; - if (!(is_vm_hugetlb_page(mpnt))) - unmap_page_range(tlb, mpnt, start, end); - else - mpnt->vm_ops->close(mpnt); + unmap_page_range(tlb, mpnt, start, end); mpnt = mpnt->vm_next; } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/mm/mprotect.c linux-2.5-bk/mm/mprotect.c --- linux-2.5.45/mm/mprotect.c Wed Oct 30 16:42:28 2002 +++ linux-2.5-bk/mm/mprotect.c Thu Oct 31 22:13:07 2002 @@ -9,6 +9,7 @@ */ #include +#include #include #include #include diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/mm/mremap.c linux-2.5-bk/mm/mremap.c --- linux-2.5.45/mm/mremap.c Wed Oct 30 16:42:58 2002 +++ linux-2.5-bk/mm/mremap.c Thu Oct 31 22:13:07 2002 @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -20,7 +21,7 @@ #include #include -static inline pte_t *get_one_pte_map_nested(struct mm_struct *mm, unsigned long addr) +static pte_t *get_one_pte_map_nested(struct mm_struct *mm, unsigned long addr) { pgd_t * pgd; pmd_t * pmd; @@ -80,7 +81,7 @@ return pte; } -static inline int copy_one_pte(struct mm_struct *mm, pte_t * src, pte_t * dst) +static int copy_one_pte(struct mm_struct *mm, pte_t * src, pte_t * dst) { int error = 0; pte_t pte; @@ -169,7 +170,7 @@ return -1; } -static inline unsigned long move_vma(struct vm_area_struct * vma, +static unsigned long move_vma(struct vm_area_struct * vma, unsigned long addr, unsigned long old_len, unsigned long new_len, unsigned long new_addr) { diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/mm/msync.c linux-2.5-bk/mm/msync.c --- linux-2.5.45/mm/msync.c Wed Oct 30 16:42:27 2002 +++ linux-2.5-bk/mm/msync.c Thu Oct 31 22:13:07 2002 @@ -39,7 +39,7 @@ return 0; } -static inline int filemap_sync_pte_range(pmd_t * pmd, +static int filemap_sync_pte_range(pmd_t * pmd, unsigned long address, unsigned long end, struct vm_area_struct *vma, unsigned int flags) { diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/mm/page_alloc.c linux-2.5-bk/mm/page_alloc.c --- linux-2.5.45/mm/page_alloc.c Wed Oct 30 16:41:51 2002 +++ linux-2.5-bk/mm/page_alloc.c Thu Oct 31 22:13:07 2002 @@ -51,7 +51,7 @@ /* * Temporary debugging check for pages not lying within a given zone. */ -static inline int bad_range(struct zone *zone, struct page *page) +static int bad_range(struct zone *zone, struct page *page) { if (page_to_pfn(page) >= zone->zone_start_pfn + zone->spanned_pages) return 1; diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/mm/shmem.c linux-2.5-bk/mm/shmem.c --- linux-2.5.45/mm/shmem.c Wed Oct 30 16:42:53 2002 +++ linux-2.5-bk/mm/shmem.c Thu Oct 31 22:13:07 2002 @@ -53,6 +53,7 @@ /* Flag allocation requirements to shmem_getpage and shmem_swp_alloc */ enum sgp_type { + SGP_QUICK, /* don't try more than file page cache lookup */ SGP_READ, /* don't exceed i_size, don't allocate page */ SGP_CACHE, /* don't exceed i_size, may allocate page */ SGP_WRITE, /* may exceed i_size, may allocate page */ @@ -757,6 +758,8 @@ if (filepage && PageUptodate(filepage)) goto done; error = 0; + if (sgp == SGP_QUICK) + goto failed; spin_lock(&info->lock); shmem_recalc_inode(inode); @@ -949,6 +952,42 @@ return page; } +static int shmem_populate(struct vm_area_struct *vma, + unsigned long addr, unsigned long len, + unsigned long prot, unsigned long pgoff, int nonblock) +{ + struct inode *inode = vma->vm_file->f_dentry->d_inode; + struct mm_struct *mm = vma->vm_mm; + enum sgp_type sgp = nonblock? SGP_QUICK: SGP_CACHE; + unsigned long size; + + size = (inode->i_size + PAGE_SIZE - 1) >> PAGE_SHIFT; + if (pgoff >= size || pgoff + (len >> PAGE_SHIFT) > size) + return -EINVAL; + + while ((long) len > 0) { + struct page *page = NULL; + int err; + /* + * Will need changing if PAGE_CACHE_SIZE != PAGE_SIZE + */ + err = shmem_getpage(inode, pgoff, &page, sgp); + if (err) + return err; + if (page) { + err = install_page(mm, vma, addr, page, prot); + if (err) { + page_cache_release(page); + return err; + } + } + len -= PAGE_SIZE; + addr += PAGE_SIZE; + pgoff++; + } + return 0; +} + void shmem_lock(struct file *file, int lock) { struct inode *inode = file->f_dentry->d_inode; @@ -1821,6 +1860,7 @@ static struct vm_operations_struct shmem_vm_ops = { .nopage = shmem_nopage, + .populate = shmem_populate, }; static struct super_block *shmem_get_sb(struct file_system_type *fs_type, diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/mm/swap.c linux-2.5-bk/mm/swap.c --- linux-2.5.45/mm/swap.c Wed Oct 30 16:42:28 2002 +++ linux-2.5-bk/mm/swap.c Thu Oct 31 22:13:07 2002 @@ -22,6 +22,7 @@ #include #include #include +#include /* How many pages do we try to swap or page in/out together? */ int page_cluster; @@ -43,15 +44,33 @@ spin_unlock_irq(&zone->lru_lock); } +/* + * Mark a page as having seen activity. + * + * inactive,unreferenced -> inactive,referenced + * inactive,referenced -> active,unreferenced + * active,unreferenced -> active,referenced + */ +void mark_page_accessed(struct page *page) +{ + if (!PageActive(page) && PageReferenced(page) && PageLRU(page)) { + activate_page(page); + ClearPageReferenced(page); + } else if (!PageReferenced(page)) { + SetPageReferenced(page); + } +} + /** * lru_cache_add: add a page to the page lists * @page: the page to add */ -static struct pagevec lru_add_pvecs[NR_CPUS]; +static DEFINE_PER_CPU(struct pagevec, lru_add_pvecs) = { 0, }; +static DEFINE_PER_CPU(struct pagevec, lru_add_active_pvecs) = { 0, }; void lru_cache_add(struct page *page) { - struct pagevec *pvec = &lru_add_pvecs[get_cpu()]; + struct pagevec *pvec = &per_cpu(lru_add_pvecs, get_cpu()); page_cache_get(page); if (!pagevec_add(pvec, page)) @@ -59,12 +78,26 @@ put_cpu(); } +void lru_cache_add_active(struct page *page) +{ + struct pagevec *pvec = &per_cpu(lru_add_active_pvecs, get_cpu()); + + page_cache_get(page); + if (!pagevec_add(pvec, page)) + __pagevec_lru_add_active(pvec); + put_cpu(); +} + void lru_add_drain(void) { - struct pagevec *pvec = &lru_add_pvecs[get_cpu()]; + int cpu = get_cpu(); + struct pagevec *pvec = &per_cpu(lru_add_pvecs, cpu); if (pagevec_count(pvec)) __pagevec_lru_add(pvec); + pvec = &per_cpu(lru_add_active_pvecs, cpu); + if (pagevec_count(pvec)) + __pagevec_lru_add_active(pvec); put_cpu(); } @@ -198,8 +231,6 @@ /* * Add the passed pages to the LRU, then drop the caller's refcount * on them. Reinitialises the caller's pagevec. - * - * Mapped pages go onto the active list. */ void __pagevec_lru_add(struct pagevec *pvec) { @@ -218,13 +249,33 @@ } if (TestSetPageLRU(page)) BUG(); - if (page_mapped(page)) { - if (TestSetPageActive(page)) - BUG(); - add_page_to_active_list(zone, page); - } else { - add_page_to_inactive_list(zone, page); + add_page_to_inactive_list(zone, page); + } + if (zone) + spin_unlock_irq(&zone->lru_lock); + pagevec_release(pvec); +} + +void __pagevec_lru_add_active(struct pagevec *pvec) +{ + int i; + struct zone *zone = NULL; + + for (i = 0; i < pagevec_count(pvec); i++) { + struct page *page = pvec->pages[i]; + struct zone *pagezone = page_zone(page); + + if (pagezone != zone) { + if (zone) + spin_unlock_irq(&zone->lru_lock); + zone = pagezone; + spin_lock_irq(&zone->lru_lock); } + if (TestSetPageLRU(page)) + BUG(); + if (TestSetPageActive(page)) + BUG(); + add_page_to_active_list(zone, page); } if (zone) spin_unlock_irq(&zone->lru_lock); diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/mm/swap_state.c linux-2.5-bk/mm/swap_state.c --- linux-2.5.45/mm/swap_state.c Wed Oct 30 16:42:54 2002 +++ linux-2.5-bk/mm/swap_state.c Thu Oct 31 22:13:07 2002 @@ -376,7 +376,7 @@ /* * Initiate read into locked page and return. */ - lru_cache_add(new_page); + lru_cache_add_active(new_page); swap_readpage(NULL, new_page); return new_page; } diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/mm/swapfile.c linux-2.5-bk/mm/swapfile.c --- linux-2.5.45/mm/swapfile.c Wed Oct 30 16:42:19 2002 +++ linux-2.5-bk/mm/swapfile.c Thu Oct 31 22:13:07 2002 @@ -389,7 +389,7 @@ } /* mmlist_lock and vma->vm_mm->page_table_lock are held */ -static inline void unuse_pmd(struct vm_area_struct * vma, pmd_t *dir, +static void unuse_pmd(struct vm_area_struct * vma, pmd_t *dir, unsigned long address, unsigned long size, unsigned long offset, swp_entry_t entry, struct page* page) { @@ -418,7 +418,7 @@ } /* mmlist_lock and vma->vm_mm->page_table_lock are held */ -static inline void unuse_pgd(struct vm_area_struct * vma, pgd_t *dir, +static void unuse_pgd(struct vm_area_struct * vma, pgd_t *dir, unsigned long address, unsigned long size, swp_entry_t entry, struct page* page) { diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/mm/vmalloc.c linux-2.5-bk/mm/vmalloc.c --- linux-2.5.45/mm/vmalloc.c Wed Oct 30 16:42:59 2002 +++ linux-2.5-bk/mm/vmalloc.c Thu Oct 31 22:13:07 2002 @@ -23,7 +23,7 @@ rwlock_t vmlist_lock = RW_LOCK_UNLOCKED; struct vm_struct *vmlist; -static inline void unmap_area_pte(pmd_t *pmd, unsigned long address, +static void unmap_area_pte(pmd_t *pmd, unsigned long address, unsigned long size) { unsigned long end; @@ -56,7 +56,7 @@ } while (address < end); } -static inline void unmap_area_pmd(pgd_t *dir, unsigned long address, +static void unmap_area_pmd(pgd_t *dir, unsigned long address, unsigned long size) { unsigned long end; @@ -83,7 +83,7 @@ } while (address < end); } -static inline int map_area_pte(pte_t *pte, unsigned long address, +static int map_area_pte(pte_t *pte, unsigned long address, unsigned long size, pgprot_t prot, struct page ***pages) { @@ -110,7 +110,7 @@ return 0; } -static inline int map_area_pmd(pmd_t *pmd, unsigned long address, +static int map_area_pmd(pmd_t *pmd, unsigned long address, unsigned long size, pgprot_t prot, struct page ***pages) { diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/mm/vmscan.c linux-2.5-bk/mm/vmscan.c --- linux-2.5.45/mm/vmscan.c Wed Oct 30 16:41:56 2002 +++ linux-2.5-bk/mm/vmscan.c Thu Oct 31 22:13:07 2002 @@ -166,7 +166,7 @@ } /* Must be called with page's pte_chain_lock held. */ -static inline int page_mapping_inuse(struct page * page) +static inline int page_mapping_inuse(struct page *page) { struct address_space *mapping = page->mapping; @@ -178,8 +178,14 @@ if (!mapping) return 0; + /* Be more reluctant to reclaim swapcache than pagecache */ + if (PageSwapCache(page)) + return 1; + /* File is mmap'd by somebody. */ - if (!list_empty(&mapping->i_mmap) || !list_empty(&mapping->i_mmap_shared)) + if (!list_empty(&mapping->i_mmap)) + return 1; + if (!list_empty(&mapping->i_mmap_shared)) return 1; return 0; @@ -193,7 +199,7 @@ /* * shrink_list returns the number of reclaimed pages */ -static /* inline */ int +static int shrink_list(struct list_head *page_list, unsigned int gfp_mask, int *max_scan, int *nr_mapped) { @@ -417,7 +423,7 @@ * For pagecache intensive workloads, the first loop here is the hottest spot * in the kernel (apart from the copy_*_user functions). */ -static /* inline */ int +static int shrink_cache(const int nr_pages, struct zone *zone, unsigned int gfp_mask, int max_scan, int *nr_mapped) { @@ -521,7 +527,7 @@ * The downside is that we have to touch page->count against each page. * But we had to alter page->flags anyway. */ -static /* inline */ void +static void refill_inactive_zone(struct zone *zone, const int nr_pages_in, struct page_state *ps, int priority) { @@ -667,7 +673,7 @@ * pages. This is a basic per-zone page freer. Used by both kswapd and * direct reclaim. */ -static /* inline */ int +static int shrink_zone(struct zone *zone, int max_scan, unsigned int gfp_mask, const int nr_pages, int *nr_mapped, struct page_state *ps, int priority) { diff -Nur --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5.45/scripts/kconfig/Makefile linux-2.5-bk/scripts/kconfig/Makefile --- linux-2.5.45/scripts/kconfig/Makefile Wed Oct 30 16:43:45 2002 +++ linux-2.5-bk/scripts/kconfig/Makefile Thu Oct 31 22:13:09 2002 @@ -34,6 +34,7 @@ $(obj)/qconf.o: $(obj)/.tmp_qtcheck +ifeq ($(MAKECMDGOALS),$(obj)/qconf) -include $(obj)/.tmp_qtcheck # QT needs some extra effort... @@ -52,6 +53,7 @@ LIB=qt; \ if [ -f $$DIR/lib/libqt-mt.so ]; then LIB=qt-mt; fi; \ echo "QTDIR=$$DIR" > $@; echo "QTLIB=$$LIB" >> $@ +endif $(obj)/zconf.tab.o: $(obj)/lex.zconf.c